hackedteam/vector-ipa

View on GitHub
src/encryption.c

Summary

Maintainability
Test Coverage
/*
    MODULE -- encryption function

    Copyright (C) Alberto Ornaghi

    $Id: encryption.c 2854 2010-09-10 15:04:07Z alor $
*/

#include <main.h>
#include <encryption.h>
#include <checksum.h>
#include <threads.h>

#include <openssl/rc4.h>
#include <openssl/md5.h>
#include <openssl/x509.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/aes.h>

/* globals */

static LIST_HEAD(, wpa_session) wpa_sess_root;
static pthread_mutex_t root_mutex = PTHREAD_MUTEX_INITIALIZER;

/* protos */

int wifi_key_prepare(char *key_string);

int wep_decrypt(u_char *buf, size_t len, u_char *wkey, size_t wlen);
static int set_wep_key(char *string);
static void make_key_64(u_char *string, u_char *key);
static void make_key_128(u_char *string, u_char *key);

static int set_wpa_key(char *string);
void wpa_sess_add(u_char *sta, struct wpa_sa *sa);
void wpa_sess_del(u_char *sta);
int wpa_sess_get(u_char *sta, struct wpa_sa *sa);
int wpa_generate_PTK(u_char *bssid, u_char *sta, u_char *pmk, u_char *snonce, u_char *anonce,u_int16 bits, u_char *kck);
int wpa_check_MIC(struct eapol_header *eapol, struct eapol_key_header* eapol_key, size_t eapol_len, u_char *kck, int algo);
int wpa_decrypt_broadcast_key(struct eapol_key_header *eapol_key, struct rsn_ie_header *rsn_ie, struct wpa_sa *sa);
int wpa_decrypt(u_char *mac, u_char *data, size_t len, struct wpa_sa sa);
extern int wpa_ccmp_decrypt(u_char *mac, u_char *data, size_t len, struct wpa_sa sa);
extern int wpa_tkip_decrypt(u_char *mac, u_char *data, size_t len, struct wpa_sa sa);

/*******************************************/

/*
 * WEP decrypt function
 */
int wep_decrypt(u_char *buf, size_t len, u_char *wkey, size_t wlen)
{
   RC4_KEY key;
   u_char seed[32]; /* 256 bit for the wep key */
   struct wep_header *wep;
   u_char *encbuf;
   u_char decbuf[len];

   /* the key was not set, don't try to decript it */
   if (wlen == 0)
      return -ENOTHANDLED;

   /* get the wep header */
   wep = (struct wep_header *)buf;
   len -= sizeof(struct wep_header);

   /* get the key index */
   wep->key >>= 6;

   /* sanity check on the key index */
   if (wep->key * 5 > (int)(MAX_WKEY_LEN - wlen)) {
      DEBUG_MSG(D_VERBOSE, "WEP: invalid key index, the packet was skipped");
      return -ENOTHANDLED;
   }

   encbuf = (u_char *)(wep + 1);

   /* copy the IV in the first 24 bit of the RC4 seed */
   memcpy(seed, wep->init_vector, WEP_IV_LEN);

   /*
    * complete the seed with x bit from the secret key
    *
    * when using 64 bit WEP, the four keys are stored
    * in the wkey array, every 5 bytes there is a new
    * key, so we can indicize them with wep->key * 5
    */
   memcpy(seed + WEP_IV_LEN, &wkey[wep->key * 5], wlen);

   /* initialize the RC4 key */
   RC4_set_key(&key, WEP_IV_LEN + wlen, seed);

   /* decrypt the frame (len + 4 byte of crc) */
   RC4(&key, len + WEP_CRC_LEN, encbuf, decbuf);

   /*
    * check if the decryption was successful:
    * at the end of the packet there is a CRC check
    */
   if (CRC_checksum(decbuf, len + WEP_CRC_LEN, CRC_INIT) != CRC_RESULT) {
      DEBUG_MSG(D_VERBOSE, "WEP decryption failed, the packet was skipped");
      return -ENOTHANDLED;
   }

   /*
    * copy the decrypted packet over the original one
    * overwriting the wep header. this way the packet is
    * identical to a non-WEP one.
    */
   memcpy(buf, decbuf, len);

   /*
    * wipe out the remaining bytes at the end of the packets
    * we have moved the data over the wep header and the crc was left
    * at the end of the packet.
    */
   memset(buf + len, 0, WEP_CRC_LEN);

   return ESUCCESS;
}

/*
 * parse the string provided by the user and set the internally used buffer
 * the format is:
 *    type:bits:t:string
 * where:
 *    type: can be wep, wpa-psw, wpa-psk
 *    bits: is the number of bits used for the key
 *    t: can be:
 *       s: for strings (or hexadecimal escaped values)
 *       p: for passwords that will be used to generate the key
 * for example:
 *    wep:64:p:ciao
 *    wep:64:s:alor1
 *    wep:128:s:rcsredirect12
 *    wep:64:s:\x01\x02\x03\x04\x05
 *    wpa:pwd:password:ssid
 *    wpa:psk:663eb260e87cf389c6bd7331b28d82f5203b0cae4e315f9cbb7602f3236708a6
 */

int wifi_key_prepare(char *key_string)
{
   int status = -EINVALID;
   char *ks;
   char *p;

   if (key_string == NULL)
      return -EINVALID;

   ks = strdup(key_string);

   if ((p = strchr(ks, ':')) != NULL)
      *p = 0;

   /* the following string is a definition for WEP */
   if (!strcasecmp(ks, "wep")) {
      GBL_NET->wifi_schema = WIFI_WEP;
      status = set_wep_key(p + 1);
   }

   /* the following string is a definition for WPA */
   if (!strcasecmp(ks, "wpa")) {
      GBL_NET->wifi_schema = WIFI_WPA;
      status = set_wpa_key(p + 1);
   }

   SAFE_FREE(ks);
   return status;
}


int set_wep_key(char *string)
{
   int bit = 0;
   char *p, type;
   char *tok;
   char s[strlen(string) + 1];
   u_char tmp_wkey[512];
   size_t tmp_wkey_len;
   char tmp[128];

   memset(GBL_NET->wkey, 0, sizeof(GBL_NET->wkey));
   GBL_NET->wkey_len = 0;

   strcpy(s, string);

   p = my_strtok(s, ":", &tok);
   if (p == NULL) {
      DEBUG_MSG(D_ERROR, "Invalid parsing of the WEP key");
      return -EINVALID;
   }

   bit = atoi(p);

   /* sanity check */
   if (bit <= 0) {
      DEBUG_MSG(D_ERROR, "Unsupported WEP key lenght");
      return -EINVALID;
   }

   /* the len of the secret part of the RC4 seed */
   tmp_wkey_len = bit / 8 - WEP_IV_LEN;

   /* sanity check */
   if (bit != 64 && bit != 128) {
      DEBUG_MSG(D_ERROR, "Unsupported WEP key lenght");
      return -EINVALID;
   }

   /* get the type of the key */
   p = my_strtok(NULL, ":", &tok);
   if (p == NULL) {
      DEBUG_MSG(D_ERROR, "Invalid parsing of the WEP key");
      return -EINVALID;
   }

   type = *p;

   /* get the third part of the string */
   p = my_strtok(NULL, ":", &tok);
   if (p == NULL) {
      DEBUG_MSG(D_ERROR, "Invalid parsing of the WEP key");
      return -EINVALID;
   }

   if (type == 's') {
      /* escape the string and check its lenght */
      if (strescape((char *)tmp_wkey, p) != (int)tmp_wkey_len) {
         DEBUG_MSG(D_ERROR, "Specified WEP key lenght does not match the given string");
         return -EINVALID;
      }
   } else if (type == 'p') {
      /* create the key from the passphrase */
      if (bit == 64)
         make_key_64((u_char *)p, tmp_wkey);
      else if (bit == 128)
         make_key_128((u_char *)p, tmp_wkey);

   } else {
      DEBUG_MSG(D_ERROR, "Invalid parsing of the WEP key");
      return -EINVALID;
   }

   /* print the final string */
   DEBUG_MSG(D_INFO, "Using WEP key: %s", str_tohex(tmp_wkey, tmp_wkey_len, tmp, sizeof(tmp)));

   memcpy(GBL_NET->wkey, tmp_wkey, sizeof(GBL_NET->wkey));
   GBL_NET->wkey_len = tmp_wkey_len;

   return ESUCCESS;
}

/*
 * generate a key set (4 keys) from a passfrase
 */
static void make_key_64(u_char *string, u_char *key)
{
   int i, seed = 0;

   /*
    * seed is generated by xor'ing the keystring bytes
    * into the four bytes of the seed, starting at the little end
    */
   for(i = 0; string[i]; i++) {
      seed ^= (string[i] << ((i & 0x03) * 8));
   }

   /* generate the 4 keys from the seed */
   for(i = 0; i < 5*4; i++) {
      seed *= 0x000343fd;
      seed += 0x00269ec3;
      key[i] = seed >> 16;
   }

}

static void make_key_128(u_char *string, u_char *key)
{
   MD5_CTX ctx;
   u_char buf[64];
   u_char digest[MD5_DIGEST_LENGTH];
   int i, j = 0;

   /* repeat the string until buf is full */
   for (i = 0; i < 64; i++) {
      if(string[j] == 0)
         j = 0;
      buf[i] = string[j++];
   }

   /* compute the md5 digest of the buffer */
   MD5_Init(&ctx);
   MD5_Update(&ctx, buf, sizeof buf);
   MD5_Final(digest, &ctx);

   /*
    * copy the digest into the key
    * 13 byte == 104 bit
    */
   memset(key, 0, MAX_WKEY_LEN);
   memcpy(key, digest, 13);
}


static int set_wpa_key(char *string)
{
   char *p;
   char *pass;
   char *ssid;
   char tmp[128];
   int i;

   /* we need to generate the key */
   if (!strncasecmp(string, "pwd", 3)) {
      if ((p = strchr(string + + strlen("pwd") + 1, ':')) != NULL) {
         *p = 0;
      } else {
         DEBUG_MSG(D_ERROR, "Invalid parsing of the WPA password (missing SSID)");
         return -EINVALID;
      }

      /* the len of the password */
      i = strlen(string + strlen("pwd") + 1);

      /* sanity check */
      if (i < 8 || i > 63) {
         DEBUG_MSG(D_ERROR, "Invalid parsing of the WPA-PWD password (must be 8..63 chars)");
         return -EINVALID;
      }

      SAFE_STRDUP(pass, string + strlen("pwd") + 1);
      SAFE_STRDUP(ssid, p + 1);

      /*
       * undocumented function from OPENSSL which implement the PBKDF2 function used to generate the passphrase
       * the 4096 number of iterations was taken from wpa_passphrase.c of wpa_supplicant package
       */
      PKCS5_PBKDF2_HMAC_SHA1(pass, strlen(pass), (u_char *)ssid, strlen(ssid), 4096, 32, GBL_NET->wkey);

      SAFE_FREE(pass);
      SAFE_FREE(ssid);
   }

   /* just take the key and store it */
   if (!strncasecmp(string, "psk", 3)) {
      /* the hex string should be 32 bytes in hex format */
      if (strlen(string + strlen("psk") + 1) != 64) {
         DEBUG_MSG(D_ERROR, "Invalid parsing of the WPA-PSK password (must be 64 chars)");
         return -EINVALID;
      }
      /* parse the hex string into bytes */
      str_hex_to_bytes(string + strlen("psk") + 1, GBL_NET->wkey);
   }

   /* print the final string */
   DEBUG_MSG(D_INFO, "Using WPA key: %s", str_tohex(GBL_NET->wkey, WPA_KEY_LEN, tmp, sizeof(tmp)));

   return ESUCCESS;
}



void wpa_sess_add(u_char *sta, struct wpa_sa *sa)
{
   struct wpa_session *e, *s;
   char tmp[MAX_ASCII_ADDR_LEN];

   /* alloc the new element */
   SAFE_CALLOC(e, 1, sizeof(struct wpa_session));

   if (sta)
      memcpy(&e->sta, sta, ETH_ADDR_LEN);

   if (sa) {
      /* get the time of the creation.
       * this will be used to timeout the entry if we miss some packets
       * to prevent inconsistent state forever
       */
      gettimeofday(&sa->tv, NULL);

      memcpy(&e->sa, sa, sizeof(struct wpa_sa));
   }
   /* insert it in the list */
   pthread_mutex_lock(&root_mutex);

   /* check if the session already exists */
   LIST_FOREACH(s, &wpa_sess_root, next) {
      if (!memcmp(&e->sta, &s->sta, ETH_ADDR_LEN)) {
         /* already present in the list, replace the SA */
         if (sa) {
            memcpy(&s->sa, sa, sizeof(struct wpa_sa));
            /* update the time value */
            gettimeofday(&s->sa.tv, NULL);
         }

         DEBUG_MSG(D_INFO, "WPA session updated for [%s]", mac_addr_ntoa(e->sta, tmp));

         pthread_mutex_unlock(&root_mutex);
         return;
      }
   }

   LIST_INSERT_HEAD(&wpa_sess_root, e, next);
   pthread_mutex_unlock(&root_mutex);

   DEBUG_MSG(D_INFO, "New WPA session for [%s]", mac_addr_ntoa(e->sta, tmp));
}


void wpa_sess_del(u_char *sta)
{
   struct wpa_session *e, *tmp;
   char tmac[MAX_ASCII_ADDR_LEN];

   pthread_mutex_lock(&root_mutex);
   LIST_FOREACH_SAFE(e, &wpa_sess_root, next, tmp) {
      if (!memcmp(&e->sta, sta, ETH_ADDR_LEN)) {
         LIST_REMOVE(e, next);
         DEBUG_MSG(D_INFO, "WPA session deleted for [%s]", mac_addr_ntoa(e->sta, tmac));
         SAFE_FREE(e);
         break;
      }
   }
   pthread_mutex_unlock(&root_mutex);
}


int wpa_sess_get(u_char *sta, struct wpa_sa *sa)
{
   struct wpa_session *e;

   pthread_mutex_lock(&root_mutex);
   LIST_FOREACH(e, &wpa_sess_root, next) {
      if (!memcmp(&e->sta, sta, ETH_ADDR_LEN)) {
         memcpy(sa, &e->sa, sizeof(struct wpa_sa));
         pthread_mutex_unlock(&root_mutex);
         return ESUCCESS;
      }
   }
   pthread_mutex_unlock(&root_mutex);

   return -ENOTFOUND;
}

/* Function used to derive the PTK. Refer to IEEE 802.11I-2004, 8.5.1 */

/* derive the PTK from the BSSID, STA MAC, PMK (WPA-PSK), SNonce, ANonce */
int wpa_generate_PTK(u_char *bssid, u_char *sta, u_char *pmk, u_char *snonce, u_char *anonce, u_int16 bits, u_char *kck)
{
   u_int8 i;
   u_int len;
   u_char buff[100];
   size_t offset = sizeof("Pairwise key expansion");

   memset(buff, 0, 100);

   /* initialize the buffer */
   memcpy(buff, "Pairwise key expansion", offset);

   /*   Min(AA, SPA) || Max(AA, SPA)  */
   if (memcmp(sta, bssid, ETH_ADDR_LEN) < 0) {
      memcpy(buff + offset, sta, ETH_ADDR_LEN);
      memcpy(buff + offset + ETH_ADDR_LEN, bssid, ETH_ADDR_LEN);
   } else {
      memcpy(buff + offset, bssid, ETH_ADDR_LEN);
      memcpy(buff + offset + ETH_ADDR_LEN, sta, ETH_ADDR_LEN);
   }

   /* move after AA SPA */
   offset += ETH_ADDR_LEN * 2;

   /*   Min(ANonce,SNonce) || Max(ANonce,SNonce)  */
   if (memcmp(snonce, anonce, WPA_NONCE_LEN) < 0 ) {
      memcpy(buff + offset, snonce, WPA_NONCE_LEN);
      memcpy(buff + offset + WPA_NONCE_LEN, anonce, WPA_NONCE_LEN);
   } else {
      memcpy(buff + offset, anonce, WPA_NONCE_LEN);
      memcpy(buff + offset + WPA_NONCE_LEN, snonce, WPA_NONCE_LEN);
   }

   /* move after ANonce SNonce */
   offset += WPA_NONCE_LEN * 2;

   memset(kck, 0, WPA_PTK_LEN);

   /* generate the PTK */
   for (i = 0; i < (bits + 159)/160; i++) {
       buff[offset] = i;

       /* the buffer (ptk) is large enough (see declaration) */
       HMAC(EVP_sha1(), pmk, WPA_KEY_LEN, buff, 100, kck + i * 20, &len);
   }

   return ESUCCESS;
}


int wpa_check_MIC(struct eapol_header *eapol, struct eapol_key_header* eapol_key, size_t eapol_len, u_char *kck, int algo)
{
   u_char mic[WPA_MICKEY_LEN];
   u_int len;
   u_char hmac_mic[20]; /* MIC 16 byte, the HMAC-SHA1 use a buffer of 20 bytes */

   /* copy the MIC from the EAPOL packet */
   memcpy(mic, eapol_key->key_MIC, WPA_MICKEY_LEN);

   /* set to 0 the MIC in the EAPOL packet (to calculate the MIC) */
   memset(eapol_key->key_MIC, 0, WPA_MICKEY_LEN);

   if (algo == WPA_KEY_TKIP) {
       /* use HMAC-MD5 for the EAPOL-Key MIC   */
       HMAC(EVP_md5(), kck, WPA_KCK_LEN, (u_char *)eapol, eapol_len, hmac_mic, &len);
   } else if (algo == WPA_KEY_CCMP) {
       /* use HMAC-SHA1-128 for the EAPOL-Key MIC */
       HMAC(EVP_sha1(), kck, WPA_KCK_LEN, (u_char *)eapol, eapol_len, hmac_mic, &len);
   } else
       /* key descriptor version not recognized */
       return -EINVALID;

   /* restore the MIC in the EAPOL packet */
   memcpy(eapol_key->key_MIC, mic, WPA_MICKEY_LEN);

   /* compare calculated MIC with the Key MIC and return result (0 means success) */
   return memcmp(mic, hmac_mic, WPA_MICKEY_LEN);
}


int wpa_decrypt_broadcast_key(struct eapol_key_header *eapol_key, struct rsn_ie_header *rsn_ie, struct wpa_sa *sa)
{
   //guint8  new_key[32];
   u_int8  *encrypted_key;
   u_int16 key_len = 0;
   //static AIRPDCAP_KEY_ITEM dummy_key; /* needed in case AirPDcapRsnaMng() wants the key structure */

char tmp[512];

   /* Preparation for decrypting the group key - determine group key data length */
   /* depending on whether it's a TKIP or AES encryption key */
   if (sa->algo == WPA_KEY_TKIP) {
      key_len = ntohs(eapol_key->key_len);
   } else if (sa->algo == WPA_KEY_CCMP){
      key_len = ntohs(eapol_key->key_data_len);
   }

   /* sanity check */
   if (key_len > sizeof(struct rsn_ie_header) || key_len == 0)
      return -ENOTHANDLED;

   /* Encrypted key is in the information element field of the EAPOL key packet */
   SAFE_CALLOC(encrypted_key, key_len, sizeof(u_int8));

   DEBUG_MSG(D_DEBUG, "Encrypted Broadcast key:", str_tohex(encrypted_key, key_len, tmp, sizeof(tmp)));
   DEBUG_MSG(D_DEBUG, "KeyIV:", str_tohex(eapol_key->key_IV, 16, tmp, sizeof(tmp)));
   DEBUG_MSG(D_DEBUG, "decryption_key:", str_tohex(sa->ptk + 16, 16, tmp, sizeof(tmp)));

   /*
    * XXX - implement broadcast key
    * we don't really need it, it is used only for multicast and broadcast packets
    */

#if 0
    /* Build the full decryption key based on the IV and part of the pairwise key */
    memcpy(new_key, pEAPKey->key_iv, 16);
     memcpy(new_key+16, decryption_key, 16);
     DEBUG_DUMP("FullDecrKey:", new_key, 32);

     if (key_version == AIRPDCAP_WPA_KEY_VER_NOT_CCMP){
        guint8 dummy[256];
        /* TKIP key */
        /* Per 802.11i, Draft 3.0 spec, section 8.5.2, p. 97, line 4-8, */
        /* group key is decrypted using RC4.  Concatenate the IV with the 16 byte EK (PTK+16) to get the decryption key */

        rc4_state_struct rc4_state;
        crypt_rc4_init(&rc4_state, new_key, sizeof(new_key));

        /* Do dummy 256 iterations of the RC4 algorithm (per 802.11i, Draft 3.0, p. 97 line 6) */
        crypt_rc4(&rc4_state, dummy, 256);
        crypt_rc4(&rc4_state, encrypted_key, key_len);

     } else if (key_version == AIRPDCAP_WPA_KEY_VER_AES_CCMP){
        /* AES CCMP key */

        guint8 key_found;
        guint16 key_index;
        guint8 *decrypted_data;

        /* This storage is needed for the AES_unwrap function */
        decrypted_data = (guint8 *) g_malloc(key_len);

        AES_unwrap(decryption_key, 16, encrypted_key,  key_len, decrypted_data);

        /* With WPA2 what we get after Broadcast Key decryption is an actual RSN structure.
           The key itself is stored as a GTK KDE
           WPA2 IE (1 byte) id = 0xdd, length (1 byte), GTK OUI (4 bytes), key index (1 byte) and 1 reserved byte. Thus we have to
           pass pointer to the actual key with 8 bytes offset */

        key_found = FALSE;
        key_index = 0;
        while(key_index < key_len && !key_found){
           guint8 rsn_id;

           /* Get RSN ID */
           rsn_id = decrypted_data[key_index];

           if (rsn_id != 0xdd){
              key_index += decrypted_data[key_index+1]+2;
           }else{
              key_found = TRUE;
           }
        }

        if (key_found){
           /* Skip over the GTK header info, and don't copy past the end of the encrypted data */
           memcpy(encrypted_key, decrypted_data+key_index+8, key_len-key_index-8);
        }

        g_free(decrypted_data);
     }

     /* Decrypted key is now in szEncryptedKey with len of key_len */
     DEBUG_DUMP("Broadcast key:", encrypted_key, key_len);

     /* Load the proper key material info into the SA */
     sa->key = &dummy_key;
     sa->validKey = TRUE;
     sa->wpa.key_ver = key_version;
     memset(sa->wpa.ptk, 0, sizeof(sa->wpa.ptk));
     memcpy(sa->wpa.ptk+32, szEncryptedKey, key_len);
     g_free(szEncryptedKey);

#endif

   SAFE_FREE(encrypted_key);

   return ESUCCESS;
}


int wpa_decrypt(u_char *mac, u_char *data, size_t len, struct wpa_sa sa)
{
   /*
    * TKIP - for wpa
    * CCMP - for wpa2
    */

   if (sa.algo == WPA_KEY_CCMP) {
      return wpa_ccmp_decrypt(mac, data, len, sa);
   } else if (sa.algo == WPA_KEY_TKIP) {
      return wpa_tkip_decrypt(mac, data, len, sa);
   }

   /* not reached */
   return -ENOTHANDLED;
}

/* EOF */

// vim:ts=3:expandtab