Saphe Implementation - SapheData.cpp
/** * * Copyright (C) 2007-2008 * Uri Sternfeld (saphesolution@yahoo.com) * All Rights Reserved * * Written by Uri Sternfeld * Last modified: 11/1/2008 * **/ #include "SapheData.h" // Return the constant size plus the lengthes of the given URLs unsigned int CalculateEncryptedPartSize(const char* url_requested_by_client, const char* phishing_reports_url, const char* login_url) { return 4 + // enc buffer size CHALLENGE_SIZE + // server challenge AES_BLOCK_SIZE + // IV SHA1_DIGEST_SIZE + // hmac 4 + // client ip 2 + strlen(url_requested_by_client) + 2 + strlen(phishing_reports_url) + 2 + strlen(login_url); } // Some utility functions int WriteWordToStream(unsigned short w, byte* ptr) { ptr[0] = w&0xFF; ptr[1] = (w>>8)&0xFF; return 2; } int WriteDwordToStream(unsigned long dw, byte* ptr) { ptr[0] = dw&0xFF; ptr[1] = (dw>>8)&0xFF; ptr[2] = (dw>>16)&0xFF; ptr[3] = (dw>>24)&0xFF; return 4; } int ReadWordFromStream(const byte* ptr, unsigned short* w) { for (int i = 1 ; i >= 0 ; --i) { *w <<= 8; *w |= ptr[i]; } return 2; } int ReadDwordFromStream(const byte* ptr, unsigned long* dw) { for (int i = 3 ; i >= 0 ; --i) { *dw <<= 8; *dw |= ptr[i]; } return 4; } // Offsets constants to help access data on the SapheData buffer #define OFFSET_ENC_SIZE 0 #define OFFSET_SERVER_CHALLENGE 4 #define OFFSET_IV (4 + CHALLENGE_SIZE) #define OFFSET_HMAC (4 + CHALLENGE_SIZE + AES_BLOCK_SIZE) #define OFFSET_ENC_BUF (4 + CHALLENGE_SIZE + AES_BLOCK_SIZE + SHA1_DIGEST_SIZE) void CreateEncryptedPart(const char* password, const byte client_challenge[CHALLENGE_SIZE], const byte server_challenge[CHALLENGE_SIZE], const byte IV[AES_BLOCK_SIZE], unsigned long client_ip_address_little_endian, const char* url_requested_by_client, const char* login_url, const char* phishing_reports_url, byte* encrypted_part) { // Derive the key... byte key[AES_BLOCK_SIZE]; DeriveKey(password, client_challenge, server_challenge, key); unsigned short url_requested_by_client_size = strlen(url_requested_by_client); unsigned short login_url_size = strlen(login_url); unsigned short phishing_reports_url_size = strlen(phishing_reports_url); unsigned long enc_size = 4 + 2 + url_requested_by_client_size + 2 + phishing_reports_url_size + 2 + login_url_size; // Write encrypted buffer size WriteDwordToStream(enc_size, encrypted_part+OFFSET_ENC_SIZE); // Write server challenge and IV memcpy(encrypted_part+OFFSET_SERVER_CHALLENGE, server_challenge, CHALLENGE_SIZE); memcpy(encrypted_part+OFFSET_IV, IV, AES_BLOCK_SIZE); // Build the buffer to be encrypted byte* enc_buf = new byte[enc_size]; int offset = 0; offset += WriteDwordToStream(client_ip_address_little_endian, enc_buf+offset); offset += WriteWordToStream(url_requested_by_client_size, enc_buf+offset); memcpy(enc_buf+offset, (void*)url_requested_by_client, url_requested_by_client_size); offset += url_requested_by_client_size; offset += WriteWordToStream(login_url_size, enc_buf+offset); memcpy(enc_buf+offset, (void*)login_url, login_url_size); offset += login_url_size; offset += WriteWordToStream(phishing_reports_url_size, enc_buf+offset); memcpy(enc_buf+offset, (void*)phishing_reports_url, phishing_reports_url_size); offset += phishing_reports_url_size; // Calculate HMAC byte hmac[SHA1_DIGEST_SIZE]; SHA1_HMAC(key, enc_buf, enc_size, hmac); // Write the HMAC memcpy(encrypted_part+OFFSET_HMAC, hmac, SHA1_DIGEST_SIZE); // Encrypt the buffer AESExpandedKey exp_key; AESCryptExpandKey(key, exp_key); AESCFBEncrypt(exp_key, enc_buf, enc_size, IV); // Write the encrypted buffer memcpy(encrypted_part+OFFSET_ENC_BUF, enc_buf, enc_size); delete [] enc_buf; } SapheDataParseResult DecryptEncryptedPart(const byte* encrypted_part, unsigned int encrypted_part_size, const char* password, const byte client_challenge[CHALLENGE_SIZE], unsigned long* client_ip_address_little_endian, char url_requested_by_client[MAX_URL_SIZE+1], char login_url[MAX_URL_SIZE+1], char phishing_reports_url[MAX_URL_SIZE+1]) { if (encrypted_part_size < 4) { return SAPHE_PART_PARSE_ERROR; } unsigned long enc_size; ReadDwordFromStream(encrypted_part+OFFSET_ENC_SIZE, &enc_size); // Check the size of the part if (encrypted_part_size < OFFSET_ENC_BUF+enc_size) { return SAPHE_PART_PARSE_ERROR; } byte* enc_part = new byte[enc_size]; memcpy(enc_part, encrypted_part+OFFSET_ENC_BUF, enc_size); // Derive the key byte key[AES_BLOCK_SIZE]; DeriveKey(password, client_challenge, encrypted_part+OFFSET_SERVER_CHALLENGE, key); // Decrypt the encrypted part AESExpandedKey exp_key; AESCryptExpandKey(key, exp_key); AESCFBDecrypt(exp_key, enc_part, enc_size, encrypted_part+OFFSET_IV); // Calculate the HMAC byte hmac[SHA1_DIGEST_SIZE]; SHA1_HMAC(key, enc_part, enc_size, hmac); // Compare the hmacs if (memcmp(hmac, encrypted_part+OFFSET_HMAC, SHA1_DIGEST_SIZE) != 0) { delete [] enc_part; return SAPHE_SIGNATURE_ERROR; } // Parse the decrypted contents int offset = 0; offset += ReadDwordFromStream(enc_part+offset, client_ip_address_little_endian); unsigned short url_requested_by_client_size; unsigned short login_url_size; unsigned short phishing_reports_url_size; offset += ReadWordFromStream(enc_part+offset, &url_requested_by_client_size); if (url_requested_by_client_size > MAX_URL_SIZE) { delete [] enc_part; return SAPHE_INVALID_URL_SIZE; } if (url_requested_by_client_size == 0) { delete [] enc_part; return SAPHE_REQUESTED_URL_MISSING; } memcpy(url_requested_by_client, enc_part+offset, url_requested_by_client_size); url_requested_by_client[url_requested_by_client_size] = 0; offset += url_requested_by_client_size; offset += ReadWordFromStream(enc_part+offset, &login_url_size); if (login_url_size > MAX_URL_SIZE) { delete [] enc_part; return SAPHE_INVALID_URL_SIZE; } if (login_url_size == 0) { delete [] enc_part; return SAPHE_LOGIN_URL_MISSING; } memcpy(login_url, enc_part+offset, login_url_size); login_url[login_url_size] = 0; offset += login_url_size; offset += ReadWordFromStream(enc_part+offset, &phishing_reports_url_size); if (phishing_reports_url_size > MAX_URL_SIZE) { delete [] enc_part; return SAPHE_INVALID_URL_SIZE; } memcpy(phishing_reports_url, enc_part+offset, phishing_reports_url_size); phishing_reports_url[phishing_reports_url_size] = 0; offset += phishing_reports_url_size; delete [] enc_part; return SAPHE_SUCCESS; } bool IsSecureURL(const char* url) { return (strnicmp(url, "https://", 8) == 0); return true; } #define IP_STRING_SIZE 16 bool GetTextBetween(const char* raw_data, const char* start, const char* end, char ipstring[IP_STRING_SIZE]) { const char* tmp = strstr(raw_data, start); if (tmp == NULL) { return false; } tmp += strlen(start); const char* tmp2 = strstr(tmp, end); if (tmp2 == NULL) { return false; } size_t output_size = tmp2 - tmp; if (output_size >= IP_STRING_SIZE) { return false; } memset(ipstring, 0, IP_STRING_SIZE); strncpy(ipstring, tmp, output_size); return true; } bool ExtractIPAddress(const char* url, const byte* raw_data, unsigned long* extracted_ip) { char ipstring[16]; memset(ipstring, 0, sizeof(ipstring)); // Note: this URL is not secure! if (stricmp(url, CLIENT_IP_URL1) == 0) { // Simply get the IP string strncpy(ipstring, (const char*)raw_data, sizeof(ipstring)); } else if (stricmp(url, CLIENT_IP_URL2) == 0) { if (GetTextBetween((const char*)raw_data, "<a href=\"http://whois.domaintools.com/", "\">", ipstring) == false) { return false; } } else if (stricmp(url, CLIENT_IP_URL3) == 0) { if (GetTextBetween((const char*)raw_data, "window.clipboardData.setData('Text','", "')", ipstring) == false) { return false; } } else if (stricmp(url, CLIENT_IP_URL4) == 0) { if (GetTextBetween((const char*)raw_data, "IP Address properties of your Internet Connection ", " -->", ipstring) == false) { return false; } } else { // Unknown url... return false; } // Attempt to convert the extracted string to an IP address... *extracted_ip = ntohl(inet_addr(ipstring)); // Is this an illegal IP string? if (*extracted_ip == 0xFFFFFFFF) { return false; } return true; }