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;
}

Back to SapheData implementation

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License