從 Information Elements 解析無線網路的加密細節

無線網路的訊號是公開發送的,雖然無法攔截,但只要你有無線裝置,基本上都能『監聽』空氣中的無線網路封包。也因為無線通訊的本身是不安全的,就如同傳統網路可能被 ISP 或連線路徑中的 Route Node 所監聽一樣,所以無論是哪一種通訊,目前的解決方式都是採用封包或資料加密,以達成安全連線的目的。在一般網路通訊中,常聽到的方法有 Secure Sockets Layer(SSL) 這一類,而對於無線網路通訊來說,就有所謂的 WEP、WPA、WPA2 等等眾多規格。嚴格來說,SSL 和無線網路的加密並不算同一層面的實作,但是他們有相同的目的和類似的原理。

最早的無線加密非常單純,可以用 WEP 一種加密方式走遍天下,只要判斷出遠端無線基地台或存取點需要加密金鑰,我們就可以假設該加密是 WEP 。不過由於 WEP 並不安全,有心人經過封包收集後,還是能很快破出金鑰,所以,日後就出現了 WPA、WPA2 種種加密方法的進階補強版。

想要知道該無線訊號是哪一種加密方法,可以從無線裝置所提供的『Information Elements』去得知,從事件 IWEVGENIE 中,就可取得加密的細節 ,這部份可參考 iwlist 或 LXPanel netstat plugin 內部的實作。

Informations Elements(IEs) 的結構定義,分為兩種:

  1. 當加密方式是 WPA or Others,這也是最 IEs 原始的定義,結構如下:



  2. 當加密方式是 WPA2 時,則沒有 OUI 欄位,定義如下:


  • Type 欄位:
    • 0xdd: WPA or Others
    • 0x30: WPA2
  • OUI 定義(3 Bytes)
    • WPA: 0x00, 0x50, 0xf2
    • WPA2: 0x00, 0x0f, 0xac
* 若有 OUI 欄位(4 Bytes),最後 1 Byte 為 0x01
  • Data 欄位

* 所有數值皆採用 Little Endian

因為 WPA 和 WPA2 是後來擴充上去的定義,並不在原始的 IEs 標準裡,所以許多欄位可能依遵循的標準而有所增減,不過,除非是發射端過於老舊,理論上都應該會遵循後來 WPA/WPA2 擴充的標準。在程式實作解析WPA/WPA2 時,原則上只要是無法辨別的 Type,一律視為 TKIP 就可以向下相容了,可以從 『Wireless Tools』的 Source Code 中找到這樣的處理原則。

關於 Cipher Type 和 Authentication Suite(key_mgmt) 的定義可參考:
static const char * iw_ie_cypher_name[] = {
"none",
"WEP-40",
"TKIP",
"WRAP",
"CCMP",
"WEP-104",
};

static const char * iw_ie_key_mgmt_name[] = {
"none",
"802.1x",
"PSK",
};


LXPanel netstat plugin 在這部份的實作摘錄:
void
wireless_gen_ie(ap_info *info, unsigned char *buffer, int ielen)
{
int offset = 2;
int count;
int i;
unsigned char wpa1_oui[3] = {0x00, 0x50, 0xf2};
unsigned char wpa2_oui[3] = {0x00, 0x0f, 0xac};
unsigned char *wpa_oui;

/* check IE type */
switch(buffer[0]) {
case 0xdd: /* WPA or else */
wpa_oui = wpa1_oui;

if((ielen < 8)
|| (memcmp(&buffer[offset], wpa_oui, 3) != 0)
|| (buffer[offset + 3] != 0x01)) {
if (info->haskey)
info->en_method = NS_WIRELESS_AUTH_WEP;
else
info->en_method = NS_WIRELESS_AUTH_OFF;

info->key_mgmt = NS_IW_IE_KEY_MGMT_NONE;
info->group = NS_IW_IE_CIPHER_NONE;
info->pairwise = NS_IW_IE_CIPHER_NONE;

return;
}

/* OUI and 0x01 */
offset += 4;
break;

case 0x30: /* IEEE 802.11i/WPA2 */
wpa_oui = wpa2_oui;
break;

default: /* Unknown */
if (info->haskey)
info->en_method = NS_WIRELESS_AUTH_WEP;
else
info->en_method = NS_WIRELESS_AUTH_OFF;

info->key_mgmt = NS_IW_IE_KEY_MGMT_NONE;
info->group = NS_IW_IE_CIPHER_NONE;
info->pairwise = NS_IW_IE_CIPHER_NONE;
return;
}

/* assume TKIP */
info->en_method = NS_WIRELESS_AUTH_WPA;
info->key_mgmt = NS_IW_IE_KEY_MGMT_NONE;
info->group = NS_IW_IE_CIPHER_TKIP;
info->pairwise = NS_IW_IE_CIPHER_TKIP;

/* 2 bytes for version number (little endian) */
offset += 2;

/* check group cipher for short IE */
if ((offset+4) > ielen) {
/* this is a short IE, we can assume TKIP/TKIP. */
info->group = NS_IW_IE_CIPHER_TKIP;
info->pairwise = NS_IW_IE_CIPHER_TKIP;
return;
}

/* 4 Bytes for group cipher information [3 bytes][1 Byte] */
if(memcmp(&buffer[offset], wpa_oui, 3)!=0) {
/* the group cipher is proprietary */
info->group = NS_IW_IE_CIPHER_NONE;
} else {
/* pick a byte for type of group cipher */
info->group = buffer[offset+3];
}
offset += 4;

/* check pairwise cipher for short IE */
if ((offset+2) > ielen) {
/* this is a short IE, we can assume TKIP. */
info->pairwise = NS_IW_IE_CIPHER_TKIP;
return;
}

/* 2 bytes for number of pairwise ciphers (little endian) */
count = buffer[offset] | (buffer[offset + 1] << 8);
offset += 2;

/* if we are done */
if ((offset+4*count) > ielen) {
return;
}

/* choose first cipher of pairwise ciphers to use,
* FIXME: Let user decide the cipher is the best way. */
for(i=0;i<count;i++) {
if(memcmp(&buffer[offset], wpa_oui, 3)==0) {
/* pick a byte for type of group cipher */
info->pairwise = buffer[offset+3];
}
offset += 4;
}

/* check authentication suites */
if ((offset+2) > ielen) {
/* this is a short IE, we can assume TKIP. */
info->key_mgmt = NS_IW_IE_KEY_MGMT_NONE;
return;
}

/* 2 bytes for number of authentication suites (little endian) */
count = buffer[offset] | (buffer[offset + 1] << 8);
offset += 2;

/* if we are done */
if ((offset+4*count) > ielen) {
return;

/* choose first key_mgmt of authentication suites to use,
* FIXME: Let user decide the key_mgmt is the best way. */
for(i=0;i<count;i++) {
if(memcmp(&buffer[offset], wpa_oui, 3)==0) {
/* pick a byte for type of key_mgmt */
info-&gtkey_mgmt = buffer[offset+3];
}
offset += 4;
}
}


在之前,LXPanel netstat plugin 對 WPA/WPA2 IEs 支援一直有問題,在最近改寫後的 SVN 最新版已經可以很好的處理 IEs,當然這部份是參考 iwlib 的 iwlist.c 寫出來的,其目的是判斷加密的方式,以通知 LXNM 做相對應的處理。如果想要使用這功能,請使用最新 SVN 版本的 LXPanel 以及LXNM,因為改寫過後的 netstat plugin 和舊版 LXNM 完全不相容。

後記

本文的示意圖表是大略畫出來的,其中可能會用到些縮寫或簡化的名詞表示。

這個網誌中的熱門文章

Web 技術中的 Session 是什麼?

淺談 USB 通訊架構之定義(一)

淺談 USB 通訊架構之定義(二)

JavaScript 好用的 async 異步函數!

使用 NodeJS + Express 從 GET/POST Request 取值