feat(ath9k): WPA2-PSK crypto core + connect path (auth/assoc/4-way)
wpa2_crypto.{c,h} (new): self-contained, verified crypto base for the
ath9k STA connect path. No heap, no SSE, DKM-freestanding.
- SHA1 (RFC 3174), HMAC-SHA1 (RFC 2202), PBKDF2-HMAC-SHA1 (RFC 2898)
- PRF-512 (802.11i H.4) for PMK -> PTK
- AES-128 encrypt/decrypt (FIPS-197)
- RFC 3394 AES key unwrap (KEK -> GTK)
- CCMP (RFC 3610 + 802.11i): MIC verify + CTR decrypt, and encrypt for TX
- wpa2_kat(): 7 in-driver known-answer/reference cases (SHA1/HMAC/PBKDF2/
AES x2/CCMP round-trip+tamper/unwrap). Panics ATH-E02 if any case fails.
ath9k.c: connect path built on the crypto base.
- TX descriptor ring + ath9k_tx_frame (AR9002 minimal field set; bit
layout to be reconciled with HAL initvals during hardware bring-up)
- management frames: auth (open) / assoc request with WPA2-PSK RSN IE
- EAPOL-Key builder with HMAC-SHA1-128 MIC; M2 and M4 senders
- 4-way state machine driven from RX: M1 (ANonce) -> derive PTK ->
M2 -> M3 (MIC verify, GTK unwrap) -> M4 -> CONNECTED
- ath9k_tx_impl for data frames (CCMP encrypt); data TX before key
install panics ATH-E04 (zero-degradation rule)
- netdev now registers tx/connect/disconnect callbacks
build.ps1: Build-DkmDriverMulti to compile+ld -r ath9k.c with wpa2_crypto.c.
Verified on host: all wpa2_kat cases pass (AES-128 = FIPS-197 C.1
69c4e0d8..., RFC 3394 C.1 unwrap = 0011..eeff, CCMP round-trip + tamper).
Hardware bring-up (AR9285 initvals, TX/RX descriptor bit layout, key
install into the MAC) still requires real-hardware iteration.
This commit is contained in:
+585
-10
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "../dkm_shared.h"
|
||||
#include "wpa2_crypto.h"
|
||||
|
||||
static const char *const g_depends[] = {"pci","irq"};
|
||||
static const char *const g_provides[] = {"netdev"};
|
||||
@@ -173,6 +174,12 @@ static struct ath9k_scan_result g_scan_results[ATH9K_MAX_SCAN_RESULTS];
|
||||
static u32 g_scan_count;
|
||||
static int g_scanning;
|
||||
|
||||
/* 前向声明:接收路径需要把管理/控制帧交给连接状态机 */
|
||||
static int ath9k_handle_mgmt(const u8 *frame, u32 len);
|
||||
static void ath9k_handle_eapol(const u8 *eap, u32 len);
|
||||
static int eapol_find(const u8 *frame, u32 len, u32 *out_off);
|
||||
static void copy_mac(u8 dst[6], const u8 src[6]);
|
||||
|
||||
/* ---- 串口日志(参考 e1000 raw_log) ---- */
|
||||
#define COM1 0x3F8
|
||||
|
||||
@@ -478,6 +485,20 @@ static int find_bssid(const u8 bssid[6]) {
|
||||
|
||||
/* 处理一个接收到的 802.11 帧(从 RX 缓冲区解析) */
|
||||
static void ath9k_handle_rx_frame(const u8 *buf, u32 len, i8 rssi) {
|
||||
if (len < 24) return;
|
||||
u16 fctl = (u16)buf[0] | ((u16)buf[1] << 8);
|
||||
u16 ftype = fctl & 0x000C;
|
||||
|
||||
/* 连接状态机:管理帧(认证/关联响应)与数据帧中的 EAPOL-Key */
|
||||
if (ftype == 0x0000) {
|
||||
if (ath9k_handle_mgmt(buf, len) == 0) return;
|
||||
} else if (ftype == 0x0008) { /* data */
|
||||
u32 eoff = 0;
|
||||
if (eapol_find(buf, len, &eoff) == 0)
|
||||
ath9k_handle_eapol(buf + eoff, len - eoff);
|
||||
/* 数据帧继续走 beacon 解析(多数会被拒绝) */
|
||||
}
|
||||
|
||||
struct ath9k_scan_result tmp;
|
||||
for (int i = 0; i < (int)sizeof(tmp); i++) ((u8 *)&tmp)[i] = 0;
|
||||
if (parse_beacon(buf, len, &tmp) != 0) return;
|
||||
@@ -604,14 +625,550 @@ static int ath9k_is_wireless_impl(void *ctx) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ath9k 当前不支持 TX(仅扫描)——严格错误策略插桩:上层若真把帧交给
|
||||
* 只会扫描的无线驱动,panic 而非静默丢包(ATH-E01)。 */
|
||||
static int ath9k_tx_stub(void *ctx, const void *packet, u32 length) {
|
||||
(void)ctx; (void)packet; (void)length;
|
||||
g_log->panic("ATH-E01 TX NOT IMPLEMENTED (scan-only driver got a tx)");
|
||||
/* ============================================================
|
||||
* TX 路径 + WPA2-PSK 连接状态机
|
||||
*
|
||||
* 说明:TX 描述符采用 AR9002(AR9280/9285)队列描述符布局的
|
||||
* 最小可用子集;位域与 HAL initvals 需在实机 bring-up 时用
|
||||
* 串口日志对拍(日志点见 ath9k_tx_frame)。协议层(认证/关联/
|
||||
* 4-way/密钥安装)由 wpa2_crypto 提供并通过 wpa2_kat 自检。
|
||||
* ============================================================ */
|
||||
|
||||
/* TX 队列(BE)寄存器 */
|
||||
#define AR_TXDP(_q) (0x0000u + 0x0800u * (_q)) /* TXQ 描述符基址 */
|
||||
#define AR_TXE 0x0800u
|
||||
#define AR_TXCFG 0x0008u
|
||||
#define AR_TXCFG_TXE 0x00000004u
|
||||
|
||||
#define ATH9K_TX_DESC_COUNT 8u
|
||||
#define ATH9K_TX_BUF_SIZE 2048u
|
||||
|
||||
/* AR9002 TX 队列描述符(32 字节,最小字段集) */
|
||||
struct ath9k_tx_desc {
|
||||
u32 ds_link; /* 下一描述符物理地址 */
|
||||
u32 ds_data; /* 数据缓冲物理地址 */
|
||||
u32 ds_ctl0; /* FRAME_LEN(0:12) | TXQ(12:16) */
|
||||
u32 ds_ctl1; /* BUF_LEN(0:12) | MORE(12) */
|
||||
u32 ds_ctl2; /* RTS/CTS + 速率索引(0 = 默认 1M CCK) */
|
||||
u32 ds_ctl3; /* 分片/重试(0) */
|
||||
u32 ds_ctl4;
|
||||
u32 ds_ctl5;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define ATH9K_TXCTL0_LEN_S 0
|
||||
#define ATH9K_TXCTL0_LEN_M 0x00000FFFu
|
||||
#define ATH9K_TXCTL1_LEN_S 0
|
||||
#define ATH9K_TXCTL1_LEN_M 0x00000FFFu
|
||||
#define ATH9K_TXCTL1_MORE 0x00001000u
|
||||
|
||||
/* ---- 连接状态 ---- */
|
||||
#define WPA_STATE_IDLE 0
|
||||
#define WPA_STATE_AUTH_SENT 1
|
||||
#define WPA_STATE_ASSOC_SENT 2
|
||||
#define WPA_STATE_M1_WAIT 3
|
||||
#define WPA_STATE_M3_WAIT 4
|
||||
#define WPA_STATE_CONNECTED 5
|
||||
|
||||
static struct dkm_dma_buffer g_tx_desc_dma;
|
||||
static struct ath9k_tx_desc *g_tx_desc;
|
||||
static struct dkm_dma_buffer g_tx_buf_dma;
|
||||
static u8 *g_tx_buf;
|
||||
static u32 g_tx_head;
|
||||
static int g_tx_ready;
|
||||
static const struct dkm_dma_api *g_dma;
|
||||
|
||||
/* WPA2 会话 */
|
||||
static u8 g_sta_mac[6]; /* SPA */
|
||||
static u8 g_ap_mac[6]; /* AA / BSSID */
|
||||
static u8 g_channel;
|
||||
static char g_target_ssid[ATH9K_SSID_MAX + 1];
|
||||
static int g_wpa_state;
|
||||
static u8 g_anonce[32];
|
||||
static u8 g_snonce[32];
|
||||
static u8 g_pmk[WPA2_PMK_LEN];
|
||||
static u8 g_ptk[64]; /* KCK(16) KEK(16) TK(16) + 余量 */
|
||||
static u64 g_ptk_replay;
|
||||
static u64 g_tx_pn; /* CCMP 发包序号 */
|
||||
static u16 g_seq;
|
||||
static int g_key_installed;
|
||||
|
||||
#define ATH9K_STA_MAC_ID0 0x0800u
|
||||
|
||||
/* ---- 802.11 管理帧构造 ---- */
|
||||
#define IEEE80211_STYPE_AUTH 0x00B0u
|
||||
#define IEEE80211_STYPE_ASSOC_REQ 0x0000u
|
||||
#define IEEE80211_STYPE_ASSOC_RESP 0x0010u
|
||||
#define IEEE80211_STYPE_REASSOC_REQ 0x0020u
|
||||
#define IEEE80211_STYPE_DEAUTH 0x00C0u
|
||||
|
||||
#define WLAN_EID_SUPP_RATES 1
|
||||
#define WLAN_EID_ERP 42
|
||||
#define WLAN_EID_RSN 48
|
||||
|
||||
static u32 put_le16(u8 *p, u16 v) { p[0]=(u8)v; p[1]=(u8)(v>>8); return 2; }
|
||||
|
||||
/* 帧控制字(管理帧,非保护) */
|
||||
static void mgmt_fc(u8 *f, u16 subtype) {
|
||||
u16 fc = 0x0000u | subtype; /* proto 0, type=mgmt(0) */
|
||||
f[0] = (u8)(fc & 0xFF);
|
||||
f[1] = (u8)(fc >> 8);
|
||||
f[2] = 0; f[3] = 0; /* duration */
|
||||
}
|
||||
|
||||
/* 填 24 字节管理帧头:DA / SA / BSSID / seq */
|
||||
static void mgmt_hdr(u8 *f, const u8 *da, const u8 *sa, const u8 *bssid) {
|
||||
for (int i = 0; i < 6; i++) f[4+i] = da[i];
|
||||
for (int i = 0; i < 6; i++) f[10+i] = sa[i];
|
||||
for (int i = 0; i < 6; i++) f[16+i] = bssid[i];
|
||||
u16 sc = (u16)((++g_seq & 0xFFF) << 4);
|
||||
f[22] = (u8)(sc & 0xFF); f[23] = (u8)(sc >> 8);
|
||||
}
|
||||
|
||||
/* WPA2-PSK RSN IE(AKM 00-0F-AC:2,CCMP) */
|
||||
static u32 rsn_ie_psk(u8 *p) {
|
||||
u8 *q = p;
|
||||
q[0] = WLAN_EID_RSN;
|
||||
q[1] = 20; /* 长度 */
|
||||
q += 2;
|
||||
q += put_le16(q, 1); /* version */
|
||||
/* group cipher: OUI 00-0F-AC + type 4 (CCMP) */
|
||||
q[0]=0x00; q[1]=0x0F; q[2]=0xAC; q[3]=0x04; q += 4;
|
||||
q += put_le16(q, 1); /* pairwise count */
|
||||
q[0]=0x00; q[1]=0x0F; q[2]=0xAC; q[3]=0x04; q += 4;
|
||||
q += put_le16(q, 1); /* AKM count */
|
||||
q[0]=0x00; q[1]=0x0F; q[2]=0xAC; q[3]=0x02; q += 4; /* PSK */
|
||||
q += put_le16(q, 0); /* RSN caps */
|
||||
return (u32)(q - p);
|
||||
}
|
||||
|
||||
/* 发送一帧(构造 802.11 头 → TX 环 → 触发) */
|
||||
static int ath9k_tx_frame(const u8 *frame, u32 len);
|
||||
|
||||
/* ---- 认证请求(open system) ---- */
|
||||
static int ath9k_send_auth(void) {
|
||||
u8 f[30 + 4];
|
||||
for (u32 i = 0; i < sizeof(f); i++) f[i] = 0;
|
||||
mgmt_fc(f, IEEE80211_STYPE_AUTH);
|
||||
mgmt_hdr(f, g_ap_mac, g_sta_mac, g_ap_mac);
|
||||
/* auth body: alg(2)=0, seq(2)=1, status(2)=0 */
|
||||
put_le16(f + 24, 0);
|
||||
put_le16(f + 26, 1);
|
||||
put_le16(f + 28, 0);
|
||||
raw_log("[ath9k.raw] tx auth request\n");
|
||||
return ath9k_tx_frame(f, 30);
|
||||
}
|
||||
|
||||
/* ---- 关联请求(含 RSN IE) ---- */
|
||||
static int ath9k_send_assoc(void) {
|
||||
u8 f[24 + 4 + 2 + 34 + 4 + 24 + 2];
|
||||
u32 n = 0;
|
||||
for (u32 i = 0; i < sizeof(f); i++) f[i] = 0;
|
||||
mgmt_fc(f, IEEE80211_STYPE_ASSOC_REQ);
|
||||
mgmt_hdr(f, g_ap_mac, g_sta_mac, g_ap_mac);
|
||||
n = 24;
|
||||
n += put_le16(f + n, 0x0431); /* capability: ESS + privacy + short preamble */
|
||||
n += put_le16(f + n, 10); /* listen interval */
|
||||
/* SSID IE */
|
||||
{
|
||||
u32 sl = 0;
|
||||
while (g_target_ssid[sl]) sl++;
|
||||
f[n++] = WLAN_EID_SSID;
|
||||
f[n++] = (u8)sl;
|
||||
for (u32 i = 0; i < sl; i++) f[n++] = (u8)g_target_ssid[i];
|
||||
}
|
||||
/* supported rates: 1,2,5.5,11,6,9,12,18 Mbps */
|
||||
f[n++] = WLAN_EID_SUPP_RATES;
|
||||
f[n++] = 8;
|
||||
f[n++] = 0x82; f[n++] = 0x84; f[n++] = 0x8B; f[n++] = 0x96;
|
||||
f[n++] = 0x0C; f[n++] = 0x12; f[n++] = 0x18; f[n++] = 0x24;
|
||||
/* RSN IE */
|
||||
n += rsn_ie_psk(f + n);
|
||||
raw_log("[ath9k.raw] tx assoc request\n");
|
||||
return ath9k_tx_frame(f, n);
|
||||
}
|
||||
|
||||
/* ---- EAPOL-Key 构造(802.1X 4-way) ---- */
|
||||
#define EAPOL_VERSION 2
|
||||
#define EAPOL_TYPE_KEY 3
|
||||
#define WPA_KEY_DESC_TYPE 2
|
||||
#define WPA_KEY_INFO_KEY_TYPE 0x0008u /* 1 = pairwise */
|
||||
#define WPA_KEY_INFO_INSTALL 0x0040u
|
||||
#define WPA_KEY_INFO_ACK 0x0080u
|
||||
#define WPA_KEY_INFO_MIC 0x0100u
|
||||
#define WPA_KEY_INFO_SECURE 0x0200u
|
||||
#define WPA_KEY_INFO_ENC_KEY 0x1000u
|
||||
#define WPA_KEY_INFO_VER2 0x0002u /* WPA2 = 3 (HMAC-SHA1 + AES) */
|
||||
|
||||
/* 构造 802.11 数据帧(toDS)+ LLC/SNAP + EAPOL-Key,做 MIC 后发送。
|
||||
* key_info 由调用方给,nonce 指针可为 0(M4 用)。 */
|
||||
static int ath9k_send_eapol(u16 key_info, const u8 *nonce,
|
||||
const u8 *key_data, u32 kd_len) {
|
||||
static u8 f[256];
|
||||
for (u32 i = 0; i < sizeof(f); i++) f[i] = 0;
|
||||
/* 802.11 data 头:toDS(AP 为 RA=A1,SA=A2,DA=A3=BSSID) */
|
||||
u16 fc = 0x0100u | 0x0008u; /* data + ToDS */
|
||||
f[0] = (u8)(fc & 0xFF); f[1] = (u8)(fc >> 8);
|
||||
mgmt_hdr(f, g_ap_mac, g_sta_mac, g_ap_mac);
|
||||
u32 n = 24;
|
||||
/* LLC/SNAP:AA AA 03 00 00 00 88 8E (EAPOL) */
|
||||
static const u8 snap[8] = {0xAA,0xAA,0x03,0x00,0x00,0x00,0x88,0x8E};
|
||||
for (int i = 0; i < 8; i++) f[n++] = snap[i];
|
||||
/* EAPOL 头 */
|
||||
u32 eapol_off = n;
|
||||
f[n++] = EAPOL_VERSION;
|
||||
f[n++] = EAPOL_TYPE_KEY;
|
||||
u32 len_off = n; n += 2; /* 长度稍后回填 */
|
||||
/* EAPOL-Key 体 */
|
||||
u32 mic_off;
|
||||
f[n++] = WPA_KEY_DESC_TYPE;
|
||||
n += put_le16(f + n, key_info);
|
||||
n += put_le16(f + n, 16); /* key length (CCMP) */
|
||||
for (int i = 0; i < 8; i++) f[n++] = (u8)(g_ptk_replay >> (i * 8));
|
||||
for (int i = 0; i < 32; i++) f[n++] = nonce ? nonce[i] : 0; /* nonce */
|
||||
n += 16; /* key IV(全 0) */
|
||||
n += 8; /* key RSC */
|
||||
n += 8; /* key ID(全 0) */
|
||||
mic_off = n; n += 16; /* key MIC(待填) */
|
||||
n += put_le16(f + n, (u16)kd_len);
|
||||
for (u32 i = 0; i < kd_len; i++) f[n++] = key_data[i];
|
||||
/* 回填 EAPOL 长度(含 4 字节 EAPOL 头) */
|
||||
u16 elen = (u16)(n - eapol_off - 4);
|
||||
f[len_off] = (u8)(elen & 0xFF); f[len_off + 1] = (u8)(elen >> 8);
|
||||
/* MIC:若置位,对 EAPOL 帧(MIC 字段已清零)用 KCK 计算 HMAC-SHA1-128 */
|
||||
if (key_info & WPA_KEY_INFO_MIC) {
|
||||
u8 mic[20];
|
||||
wpa2_hmac_sha1(g_ptk, WPA2_KCK_LEN, f + eapol_off, n - eapol_off, mic);
|
||||
for (int i = 0; i < 16; i++) f[mic_off + i] = mic[i];
|
||||
}
|
||||
return ath9k_tx_frame(f, n);
|
||||
}
|
||||
|
||||
/* ---- EAPOL-Key M2(SNonce + MIC,4-way 第 2 条) ---- */
|
||||
static int ath9k_send_m2(void) {
|
||||
u16 ki = WPA_KEY_INFO_VER2 | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC;
|
||||
u8 kd[2 + 20]; /* RSN IE(WPA2 常见,可省) */
|
||||
u32 kdn = rsn_ie_psk(kd);
|
||||
raw_log("[ath9k.raw] tx EAPOL M2\n");
|
||||
return ath9k_send_eapol(ki, g_snonce, kd, kdn);
|
||||
}
|
||||
|
||||
/* ---- EAPOL-Key M4(MIC + secure,4-way 第 4 条) ---- */
|
||||
static int ath9k_send_m4(void) {
|
||||
u16 ki = WPA_KEY_INFO_VER2 | WPA_KEY_INFO_KEY_TYPE |
|
||||
WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
|
||||
raw_log("[ath9k.raw] tx EAPOL M4\n");
|
||||
return ath9k_send_eapol(ki, 0, 0, 0);
|
||||
}
|
||||
|
||||
/* ---- PTK 派生(802.11i H.4) ---- */
|
||||
static void ath9k_derive_ptk(void) {
|
||||
u8 seed[64];
|
||||
const u8 *aa = g_ap_mac, *spa = g_sta_mac;
|
||||
int swap = 0;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (aa[i] != spa[i]) { swap = aa[i] > spa[i]; break; }
|
||||
}
|
||||
const u8 *m1 = swap ? spa : aa; /* min(AA,SPA) */
|
||||
const u8 *m2 = swap ? aa : spa; /* max(AA,SPA) */
|
||||
for (int i = 0; i < 6; i++) { seed[i] = m1[i]; seed[6+i] = m2[i]; }
|
||||
int nswap = 0;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (g_anonce[i] != g_snonce[i]) { nswap = g_anonce[i] > g_snonce[i]; break; }
|
||||
}
|
||||
const u8 *n1 = nswap ? g_snonce : g_anonce;
|
||||
const u8 *n2 = nswap ? g_anonce : g_snonce;
|
||||
for (int i = 0; i < 32; i++) { seed[12+i] = n1[i]; seed[44+i] = n2[i]; }
|
||||
wpa2_prf512(g_pmk, seed, 64, g_ptk);
|
||||
}
|
||||
|
||||
/* ---- 扫描结果查找(按 SSID 找 BSSID/信道) ---- */
|
||||
static int ath9k_find_ssid(const char *ssid, u8 *bssid, u8 *channel, u8 *sec) {
|
||||
for (u32 i = 0; i < g_scan_count; i++) {
|
||||
const char *a = g_scan_results[i].ssid;
|
||||
const char *b = ssid;
|
||||
while (*a && *b && *a == *b) { a++; b++; }
|
||||
if (*a == 0 && *b == 0) {
|
||||
for (int j = 0; j < 6; j++) bssid[j] = g_scan_results[i].bssid[j];
|
||||
*channel = g_scan_results[i].channel;
|
||||
*sec = g_scan_results[i].security;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ---- 随机 SNonce(TSC 混合,实机 TLS 级随机由上层补充) ---- */
|
||||
static void ath9k_make_snonce(void) {
|
||||
u64 t = dkm_rdtsc();
|
||||
for (int i = 0; i < 32; i++) {
|
||||
t = t * 6364136223846793005ULL + 1442695040888963407ULL;
|
||||
g_snonce[i] = (u8)(t >> ((i % 8) * 8));
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 连接入口 ---- */
|
||||
static int ath9k_connect_impl(void *ctx, const char *ssid, const char *password) {
|
||||
(void)ctx;
|
||||
if (!ssid) return -1;
|
||||
raw_log("[ath9k.raw] connect request\n");
|
||||
if (!g_tx_ready) { raw_log("[ath9k.raw] TX not ready\n"); return -2; }
|
||||
|
||||
u8 sec = 0;
|
||||
if (ath9k_find_ssid(ssid, g_ap_mac, &g_channel, &sec) != 0) {
|
||||
raw_log("[ath9k.raw] connect: SSID not in scan results\n");
|
||||
return -3; /* 调用方先扫描 */
|
||||
}
|
||||
u32 sl = 0;
|
||||
while (ssid[sl] && sl < ATH9K_SSID_MAX) { g_target_ssid[sl] = ssid[sl]; sl++; }
|
||||
g_target_ssid[sl] = 0;
|
||||
copy_mac(g_sta_mac, g_mac);
|
||||
|
||||
/* 需要 WPA2-PSK 时先算 PMK */
|
||||
if (sec != DKM_NET_SEC_OPEN) {
|
||||
if (!password || !password[0]) { raw_log("[ath9k.raw] connect: empty PSK\n"); return -4; }
|
||||
wpa2_pbkdf2_sha1(password, (const u8 *)g_target_ssid, sl, g_pmk);
|
||||
raw_log("[ath9k.raw] PMK derived\n");
|
||||
}
|
||||
|
||||
ath9k_set_channel(g_channel);
|
||||
g_key_installed = 0;
|
||||
g_ptk_replay = 0;
|
||||
g_tx_pn = 0;
|
||||
g_wpa_state = WPA_STATE_AUTH_SENT;
|
||||
if (ath9k_send_auth() != 0) { g_wpa_state = WPA_STATE_IDLE; return -5; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath9k_disconnect_impl(void *ctx) {
|
||||
(void)ctx;
|
||||
raw_log("[ath9k.raw] disconnect\n");
|
||||
g_wpa_state = WPA_STATE_IDLE;
|
||||
g_key_installed = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- 管理/控制帧处理(连接状态机驱动) ---- */
|
||||
static int ath9k_handle_mgmt(const u8 *frame, u32 len);
|
||||
|
||||
/* ath9k TX:把帧写进 TX 环并触发 DMA。
|
||||
* 描述符位域按 AR9002 队列描述符最小集构造;实机对拍日志见下方 raw_hex。
|
||||
* 上层把数据帧交给本驱动时,若尚未安装密钥(未连接)则 panic(ATH-E04)。 */
|
||||
static int ath9k_tx_frame(const u8 *frame, u32 len) {
|
||||
if (!g_tx_ready || !g_tx_desc) return -1;
|
||||
if (len > ATH9K_TX_BUF_SIZE) return -1;
|
||||
u32 idx = g_tx_head;
|
||||
u8 *buf = g_tx_buf + (u64)idx * ATH9K_TX_BUF_SIZE;
|
||||
for (u32 i = 0; i < len; i++) buf[i] = frame[i];
|
||||
struct ath9k_tx_desc *d = &g_tx_desc[idx];
|
||||
d->ds_data = (u32)(g_tx_buf_dma.phys + (u64)idx * ATH9K_TX_BUF_SIZE);
|
||||
d->ds_ctl0 = (len & ATH9K_TXCTL0_LEN_M); /* FRAME_LEN,TXQ=0(BE) */
|
||||
d->ds_ctl1 = (len & ATH9K_TXCTL1_LEN_M); /* BUF_LEN,无 MORE */
|
||||
d->ds_ctl2 = 0;
|
||||
d->ds_ctl3 = 0;
|
||||
__asm__ volatile("" ::: "memory");
|
||||
/* 触发 TX:写 TXDP 指向队首描述符并置 TXE */
|
||||
ath9k_reg_write(AR_TXDP(0), (u32)g_tx_desc_dma.phys);
|
||||
ath9k_reg_write(AR_TXCFG, AR_TXCFG_TXE);
|
||||
g_tx_head = (g_tx_head + 1) % ATH9K_TX_DESC_COUNT;
|
||||
raw_hex("[ath9k] tx kick len=", len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- 数据帧 TX(已连接后由 net 栈调用;CCMP 加密) ---- */
|
||||
static int ath9k_tx_impl(void *ctx, const void *packet, u32 length) {
|
||||
(void)ctx;
|
||||
if (!g_key_installed) {
|
||||
/* 未安装密钥时不允许数据帧上路:零降级 panic(ATH-E04) */
|
||||
g_log->panic("ATH-E04 DATA TX BEFORE KEY INSTALL");
|
||||
return -1;
|
||||
}
|
||||
static u8 f[ATH9K_TX_BUF_SIZE];
|
||||
for (u32 i = 0; i < sizeof(f); i++) f[i] = 0;
|
||||
/* toDS 数据帧头:A1=BSSID, A2=STA, A3=DA(未解析,用 BSSID 占位) */
|
||||
u16 fc = 0x0100u | 0x0008u;
|
||||
f[0] = (u8)(fc & 0xFF); f[1] = (u8)(fc >> 8);
|
||||
mgmt_hdr(f, g_ap_mac, g_sta_mac, g_ap_mac);
|
||||
int rc = wpa2_ccmp_encrypt(f, sizeof(f), (const u8 *)packet, length,
|
||||
g_ptk + WPA2_KCK_LEN + WPA2_KEK_LEN, ++g_tx_pn);
|
||||
if (rc <= 0) return -1;
|
||||
return ath9k_tx_frame(f, (u32)rc);
|
||||
}
|
||||
|
||||
/* ---- 4-way 消息处理 ---- */
|
||||
/* 定位 802.11 数据帧中的 EAPOL-Key(返回 EAPOL 起始偏移,-1 未找到) */
|
||||
static int eapol_find(const u8 *frame, u32 len, u32 *out_off) {
|
||||
/* 24B MAC 头 + 8B LLC/SNAP,可能带 8B CCMP 头(加密 EAPOL 场景) */
|
||||
u32 off = 24;
|
||||
if (len < off + 8) return -1;
|
||||
if (frame[off] == 0xAA && frame[off+1] == 0xAA) {
|
||||
/* 明文 EAPOL */
|
||||
} else if (len >= off + 16) {
|
||||
off += 8; /* 跳过 CCMP 头 */
|
||||
if (frame[off] != 0xAA) return -1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
if (off + 8 + 4 > len) return -1;
|
||||
if (frame[off+6] != 0x88 || frame[off+7] != 0x8E) return -1; /* ethertype EAPOL */
|
||||
if (frame[off+8] != EAPOL_VERSION || frame[off+9] != EAPOL_TYPE_KEY) return -1;
|
||||
*out_off = off + 8;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ath9k_handle_eapol(const u8 *eap, u32 len) {
|
||||
/* eap 指向 EAPOL 头(version,type,length,然后 Key 体) */
|
||||
if (len < 4 + 1 + 2 + 2 + 8 + 32 + 16 + 8 + 8 + 16 + 2) return;
|
||||
const u8 *k = eap + 4; /* Key descriptor 起始 */
|
||||
u16 key_info = (u16)k[1] | ((u16)k[2] << 8);
|
||||
const u8 *nonce = k + 13; /* key nonce 偏移:1(desc)+2(info)+2(len)+8(replay) */
|
||||
const u8 *mic = k + 13 + 32 + 16 + 8 + 8;
|
||||
u16 kd_len = (u16)k[13 + 32 + 16 + 8 + 8 + 16] | ((u16)k[13+32+16+8+8+16+1] << 8);
|
||||
const u8 *kd = mic + 16 + 2;
|
||||
|
||||
int pairwise = (key_info & WPA_KEY_INFO_KEY_TYPE) != 0;
|
||||
int ack = (key_info & WPA_KEY_INFO_ACK) != 0;
|
||||
int has_mic = (key_info & WPA_KEY_INFO_MIC) != 0;
|
||||
|
||||
if (!pairwise) {
|
||||
/* GTK 消息(M3 附带),此处只做 MIC 校验后记录,实际 GTK 由 M3 的 key data 携带 */
|
||||
raw_log("[ath9k.raw] EAPOL GTK msg\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ack && !has_mic) {
|
||||
/* ---- M1:带 ANonce ---- */
|
||||
raw_log("[ath9k.raw] EAPOL M1 recv\n");
|
||||
for (int i = 0; i < 32; i++) g_anonce[i] = nonce[i];
|
||||
/* replay counter 8 字节(小端) */
|
||||
g_ptk_replay = 0;
|
||||
for (int i = 0; i < 8; i++) g_ptk_replay |= (u64)k[5 + i] << (i * 8);
|
||||
ath9k_make_snonce();
|
||||
ath9k_derive_ptk();
|
||||
raw_log("[ath9k.raw] PTK derived\n");
|
||||
g_wpa_state = WPA_STATE_M3_WAIT;
|
||||
ath9k_send_m2();
|
||||
return;
|
||||
}
|
||||
|
||||
if (has_mic && !ack) {
|
||||
/* ---- M3:校验 MIC → 安装 PTK/GTK → 回 M4 ---- */
|
||||
raw_log("[ath9k.raw] EAPOL M3 recv\n");
|
||||
u64 rc = 0;
|
||||
for (int i = 0; i < 8; i++) rc |= (u64)k[5 + i] << (i * 8);
|
||||
g_ptk_replay = rc;
|
||||
/* MIC 校验:MIC 字段清零后对整段 EAPOL 计算 HMAC-SHA1-128 再比对。
|
||||
* 用本地副本避免改写 RX 缓冲(const 语义)。 */
|
||||
u32 mic_rel = (u32)(mic - eap);
|
||||
u8 tmp[256];
|
||||
if (len > sizeof(tmp)) { raw_log("[ath9k.raw] EAPOL too long\n"); return; }
|
||||
for (u32 i = 0; i < len; i++) tmp[i] = eap[i];
|
||||
for (int i = 0; i < 16; i++) tmp[mic_rel + i] = 0;
|
||||
u8 calc[20];
|
||||
wpa2_hmac_sha1(g_ptk, WPA2_KCK_LEN, tmp, len, calc);
|
||||
u8 diff = 0;
|
||||
for (int i = 0; i < 16; i++) diff |= calc[i] ^ mic[i];
|
||||
if (diff) {
|
||||
raw_log("[ath9k.raw] M3 MIC MISMATCH (bad PSK?)\n");
|
||||
g_wpa_state = WPA_STATE_IDLE;
|
||||
return;
|
||||
}
|
||||
raw_log("[ath9k.raw] M3 MIC ok\n");
|
||||
|
||||
/* key data 中可能含 GTK(KDE 0xDD + OUI 00-0F-AC + type 1,AES key wrap)。
|
||||
* 若含 ENC_KEY,用 KEK unwrap 取 GTK。 */
|
||||
if ((key_info & WPA_KEY_INFO_ENC_KEY) && kd_len > 0) {
|
||||
/* 遍历 KDE */
|
||||
u32 p = 0;
|
||||
while (p + 2 <= kd_len) {
|
||||
u8 ktype = kd[p];
|
||||
u8 klen = kd[p+1];
|
||||
if (p + 2 + klen > kd_len) break;
|
||||
if (ktype == 0xDD && klen >= 6 &&
|
||||
kd[p+2] == 0x00 && kd[p+3] == 0x0F && kd[p+4] == 0xAC && kd[p+5] == 0x01) {
|
||||
const u8 *wrapped = kd + p + 6;
|
||||
u32 wlen = (u32)klen - 6;
|
||||
u8 gtk[32];
|
||||
if (wpa2_key_unwrap(g_ptk + WPA2_KCK_LEN, wrapped, wlen, gtk) == 0) {
|
||||
raw_log("[ath9k.raw] GTK unwrapped\n");
|
||||
} else {
|
||||
raw_log("[ath9k.raw] GTK unwrap FAILED\n");
|
||||
}
|
||||
}
|
||||
p += 2 + klen;
|
||||
}
|
||||
}
|
||||
g_key_installed = 1;
|
||||
g_wpa_state = WPA_STATE_CONNECTED;
|
||||
ath9k_send_m4();
|
||||
raw_log("[ath9k.raw] === CONNECTED (WPA2-PSK) ===\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 管理帧处理(认证/关联响应) ---- */
|
||||
static int ath9k_handle_mgmt(const u8 *frame, u32 len) {
|
||||
if (len < 24) return -1;
|
||||
u16 fctl = (u16)frame[0] | ((u16)frame[1] << 8);
|
||||
if ((fctl & 0x000C) != 0x0000) return -1; /* 非管理帧 */
|
||||
u16 subtype = fctl & 0x00F0;
|
||||
/* 只处理来自目标 AP 的帧 */
|
||||
for (int i = 0; i < 6; i++) if (frame[10+i] != g_ap_mac[i]) return -1;
|
||||
|
||||
if (subtype == 0x00B0) { /* Authentication */
|
||||
if (len < 24 + 6) return -1;
|
||||
u16 status = (u16)frame[24+4] | ((u16)frame[24+5] << 8);
|
||||
if (status != 0) { raw_log("[ath9k.raw] auth FAILED\n"); g_wpa_state = WPA_STATE_IDLE; return -1; }
|
||||
raw_log("[ath9k.raw] auth ok\n");
|
||||
if (g_wpa_state == WPA_STATE_AUTH_SENT) {
|
||||
g_wpa_state = WPA_STATE_ASSOC_SENT;
|
||||
ath9k_send_assoc();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (subtype == 0x0010) { /* Association Response */
|
||||
if (len < 24 + 6) return -1;
|
||||
u16 status = (u16)frame[24+2] | ((u16)frame[24+3] << 8);
|
||||
if (status != 0) { raw_log("[ath9k.raw] assoc FAILED\n"); g_wpa_state = WPA_STATE_IDLE; return -1; }
|
||||
raw_log("[ath9k.raw] assoc ok, waiting 4-way\n");
|
||||
g_wpa_state = WPA_STATE_M1_WAIT;
|
||||
return 0;
|
||||
}
|
||||
if (subtype == 0x00C0) { /* Deauth */
|
||||
raw_log("[ath9k.raw] deauth from AP\n");
|
||||
g_wpa_state = WPA_STATE_IDLE;
|
||||
g_key_installed = 0;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ---- TX 环初始化 ---- */
|
||||
static int ath9k_tx_init(const struct dkm_dma_api *dma) {
|
||||
if (!dma || !dma->alloc_pages || !g_mmio) return -1;
|
||||
g_dma = dma;
|
||||
/* 描述符环 1 页 + 缓冲 1 页 */
|
||||
if (dma->alloc_pages(1, 16, 0x100000000ULL, &g_tx_desc_dma) != 0) {
|
||||
raw_log("[ath9k.raw] tx_init: desc alloc failed\n");
|
||||
return -2;
|
||||
}
|
||||
if (dma->alloc_pages(1, 16, 0x100000000ULL, &g_tx_buf_dma) != 0) {
|
||||
raw_log("[ath9k.raw] tx_init: buf alloc failed\n");
|
||||
return -3;
|
||||
}
|
||||
g_tx_desc = (struct ath9k_tx_desc *)g_tx_desc_dma.virt;
|
||||
g_tx_buf = (u8 *)g_tx_buf_dma.virt;
|
||||
for (u32 i = 0; i < sizeof(struct ath9k_tx_desc) * ATH9K_TX_DESC_COUNT; i++)
|
||||
((u8 *)g_tx_desc)[i] = 0;
|
||||
for (u32 i = 0; i < ATH9K_TX_DESC_COUNT; i++) {
|
||||
g_tx_desc[i].ds_link = (u32)(g_tx_desc_dma.phys +
|
||||
(((i + 1) % ATH9K_TX_DESC_COUNT) * sizeof(struct ath9k_tx_desc)));
|
||||
}
|
||||
__asm__ volatile("" ::: "memory");
|
||||
g_tx_head = 0;
|
||||
g_tx_ready = 1;
|
||||
raw_log("[ath9k.raw] tx_init: rings allocated\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath9k_rx_poll_impl(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
|
||||
(void)ctx; (void)buffer; (void)capacity;
|
||||
if (out_length) *out_length = 0;
|
||||
@@ -737,15 +1294,29 @@ int driver_init(const struct dkm_kernel_api *api,
|
||||
return -5;
|
||||
}
|
||||
|
||||
/* 9. RX 环初始化 */
|
||||
/* 9. RX/TX 环初始化 */
|
||||
if (api->dma && api->dma->alloc_pages) {
|
||||
int rc = ath9k_rx_init(api->dma);
|
||||
raw_hex("[ath9k.raw] rx_init rc=", (u64)(i8)rc);
|
||||
if (rc != 0) {
|
||||
raw_log("[ath9k.raw] RX init failed, scan unavailable\n");
|
||||
}
|
||||
int trc = ath9k_tx_init(api->dma);
|
||||
raw_hex("[ath9k.raw] tx_init rc=", (u64)(i8)trc);
|
||||
if (trc != 0) raw_log("[ath9k.raw] TX init failed, connect unavailable\n");
|
||||
} else {
|
||||
raw_log("[ath9k.raw] no DMA api, scan unavailable\n");
|
||||
raw_log("[ath9k.raw] no DMA api, scan/TX unavailable\n");
|
||||
}
|
||||
|
||||
/* 9b. WPA2 加密底座自检(KAT):失败即 panic,绝不带病上路 */
|
||||
{
|
||||
int krc = wpa2_kat();
|
||||
if (krc != 0) {
|
||||
raw_hex("[ath9k.raw] WPA2 KAT FAILED case=", (u64)krc);
|
||||
g_log->panic("ATH-E02 WPA2 CRYPTO KAT FAILED");
|
||||
return -7;
|
||||
}
|
||||
raw_log("[ath9k.raw] WPA2 crypto KAT ok\n");
|
||||
}
|
||||
|
||||
/* 10. 注册中断 */
|
||||
@@ -754,20 +1325,24 @@ int driver_init(const struct dkm_kernel_api *api,
|
||||
raw_hex("[ath9k.raw] irq_register rc=", (u64)(i8)rc);
|
||||
}
|
||||
|
||||
/* 11. 注册 netdev(标记 WIRELESS + RX_READY) */
|
||||
/* 11. 注册 netdev(无线 + RX/TX) */
|
||||
struct dkm_net_device_desc ndesc;
|
||||
for (u32 i = 0; i < sizeof(ndesc); i++) ((u8 *)&ndesc)[i] = 0;
|
||||
static const char devname[] = "ath9k0";
|
||||
ndesc.name = devname;
|
||||
copy_mac(ndesc.mac, g_mac);
|
||||
ndesc.flags = DKM_NET_F_WIRELESS | (g_rx_ready ? DKM_NET_F_RX_READY : 0);
|
||||
ndesc.flags = DKM_NET_F_WIRELESS |
|
||||
(g_rx_ready ? DKM_NET_F_RX_READY : 0) |
|
||||
(g_tx_ready ? DKM_NET_F_TX_READY : 0);
|
||||
ndesc.ctx = (void *)0; /* ath9k 用全局状态,ctx 不需要 */
|
||||
ndesc.tx = ath9k_tx_stub;
|
||||
ndesc.tx = ath9k_tx_impl;
|
||||
ndesc.rx_poll = ath9k_rx_poll_impl;
|
||||
ndesc.scan_start = ath9k_scan_start_impl;
|
||||
ndesc.scan_count = ath9k_scan_count_impl;
|
||||
ndesc.scan_result = ath9k_scan_result_impl;
|
||||
ndesc.is_wireless = ath9k_is_wireless_impl;
|
||||
ndesc.connect = ath9k_connect_impl;
|
||||
ndesc.disconnect = ath9k_disconnect_impl;
|
||||
|
||||
if (api->net && api->net->register_device) {
|
||||
int rc = api->net->register_device(&ndesc);
|
||||
|
||||
@@ -0,0 +1,639 @@
|
||||
/* wpa2_crypto.c — WPA2-PSK 加密底座实现(见 wpa2_crypto.h)
|
||||
* 全部为恒定内存访问模式的最小实现,无堆、无 SSE。
|
||||
*/
|
||||
|
||||
#include "wpa2_crypto.h"
|
||||
|
||||
/* ================= SHA1 (RFC 3174) ================= */
|
||||
|
||||
typedef struct {
|
||||
u32 h[5];
|
||||
u64 total_len;
|
||||
u8 buf[64];
|
||||
u32 buf_len;
|
||||
} sha1_ctx;
|
||||
|
||||
static void sha1_block(u32 h[5], const u8 *p) {
|
||||
u32 w[80];
|
||||
for (int i = 0; i < 16; i++)
|
||||
w[i] = ((u32)p[i*4] << 24) | ((u32)p[i*4+1] << 16) |
|
||||
((u32)p[i*4+2] << 8) | (u32)p[i*4+3];
|
||||
for (int i = 16; i < 80; i++) {
|
||||
u32 x = w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16];
|
||||
w[i] = (x << 1) | (x >> 31); /* ROTL 1 */
|
||||
}
|
||||
u32 a=h[0], b=h[1], c=h[2], d=h[3], e=h[4];
|
||||
for (int i = 0; i < 80; i++) {
|
||||
u32 f, k;
|
||||
if (i < 20) { f = (b & c) | ((~b) & d); k = 0x5A827999; }
|
||||
else if (i < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1; }
|
||||
else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; }
|
||||
else { f = b ^ c ^ d; k = 0xCA62C1D6; }
|
||||
u32 t = ((a << 5) | (a >> 27)) + f + e + k + w[i]; /* ROTL 5(a) */
|
||||
e = d; d = c; c = (b << 30) | (b >> 2); b = a; a = t; /* ROTL 30(b) */
|
||||
}
|
||||
h[0]+=a; h[1]+=b; h[2]+=c; h[3]+=d; h[4]+=e;
|
||||
}
|
||||
|
||||
static void sha1_init(sha1_ctx *c) {
|
||||
c->h[0]=0x67452301; c->h[1]=0xEFCDAB89; c->h[2]=0x98BADCFE;
|
||||
c->h[3]=0x10325476; c->h[4]=0xC3D2E1F0;
|
||||
c->total_len = 0; c->buf_len = 0;
|
||||
}
|
||||
|
||||
static void sha1_update(sha1_ctx *c, const u8 *data, u32 len) {
|
||||
c->total_len += len;
|
||||
while (len > 0) {
|
||||
u32 take = 64 - c->buf_len;
|
||||
if (take > len) take = len;
|
||||
for (u32 i = 0; i < take; i++) c->buf[c->buf_len + i] = data[i];
|
||||
c->buf_len += take; data += take; len -= take;
|
||||
if (c->buf_len == 64) { sha1_block(c->h, c->buf); c->buf_len = 0; }
|
||||
}
|
||||
}
|
||||
|
||||
static void sha1_final(sha1_ctx *c, u8 out[20]) {
|
||||
u64 bits = c->total_len * 8;
|
||||
u8 pad = 0x80;
|
||||
sha1_update(c, &pad, 1);
|
||||
u8 z = 0;
|
||||
while (c->buf_len != 56) sha1_update(c, &z, 1);
|
||||
u8 lenb[8];
|
||||
for (int i = 0; i < 8; i++) lenb[i] = (u8)(bits >> (56 - i * 8));
|
||||
sha1_update(c, lenb, 8);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
out[i*4] = (u8)(c->h[i] >> 24);
|
||||
out[i*4+1] = (u8)(c->h[i] >> 16);
|
||||
out[i*4+2] = (u8)(c->h[i] >> 8);
|
||||
out[i*4+3] = (u8)(c->h[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void wpa2_sha1(const u8 *data, u32 len, u8 out[20]) {
|
||||
sha1_ctx c;
|
||||
sha1_init(&c);
|
||||
sha1_update(&c, data, len);
|
||||
sha1_final(&c, out);
|
||||
}
|
||||
|
||||
/* ================= HMAC-SHA1 (RFC 2202) ================= */
|
||||
|
||||
void wpa2_hmac_sha1(const u8 *key, u32 key_len,
|
||||
const u8 *data, u32 data_len, u8 out[20]) {
|
||||
u8 k[64], ipad[64], opad[64], kb[20];
|
||||
for (int i = 0; i < 64; i++) k[i] = 0;
|
||||
if (key_len > 64) {
|
||||
wpa2_sha1(key, key_len, kb);
|
||||
for (int i = 0; i < 20; i++) k[i] = kb[i];
|
||||
} else {
|
||||
for (u32 i = 0; i < key_len; i++) k[i] = key[i];
|
||||
}
|
||||
for (int i = 0; i < 64; i++) { ipad[i] = k[i] ^ 0x36; opad[i] = k[i] ^ 0x5C; }
|
||||
u8 inner[20];
|
||||
sha1_ctx c;
|
||||
sha1_init(&c);
|
||||
sha1_update(&c, ipad, 64);
|
||||
sha1_update(&c, data, data_len);
|
||||
sha1_final(&c, inner);
|
||||
sha1_init(&c);
|
||||
sha1_update(&c, opad, 64);
|
||||
sha1_update(&c, inner, 20);
|
||||
sha1_final(&c, out);
|
||||
}
|
||||
|
||||
/* ================= PBKDF2-HMAC-SHA1 (RFC 2898) ================= */
|
||||
|
||||
static void pbkdf2_f(const u8 *pass, u32 pass_len,
|
||||
const u8 *salt, u32 salt_len,
|
||||
u32 iterations, u32 block_index, u8 out[20]) {
|
||||
/* U1 = HMAC(pass, salt || INT_32_BE(block_index));U_i = HMAC(pass, U_{i-1}) */
|
||||
u8 sbuf[36];
|
||||
u32 n = salt_len;
|
||||
if (n > 32) n = 32; /* SSID ≤ 32 字节,防溢出 */
|
||||
for (u32 i = 0; i < n; i++) sbuf[i] = salt[i];
|
||||
sbuf[n+0] = (u8)(block_index >> 24);
|
||||
sbuf[n+1] = (u8)(block_index >> 16);
|
||||
sbuf[n+2] = (u8)(block_index >> 8);
|
||||
sbuf[n+3] = (u8)(block_index);
|
||||
u8 u[20], t[20];
|
||||
wpa2_hmac_sha1(pass, pass_len, sbuf, n + 4, u);
|
||||
for (int i = 0; i < 20; i++) t[i] = u[i];
|
||||
for (u32 it = 1; it < iterations; it++) {
|
||||
wpa2_hmac_sha1(pass, pass_len, u, 20, u);
|
||||
for (int i = 0; i < 20; i++) t[i] ^= u[i];
|
||||
}
|
||||
for (int i = 0; i < 20; i++) out[i] = t[i];
|
||||
}
|
||||
|
||||
void wpa2_pbkdf2_sha1(const char *passphrase, const u8 *ssid, u32 ssid_len,
|
||||
u8 psk[WPA2_PMK_LEN]) {
|
||||
u32 plen = 0;
|
||||
while (passphrase[plen]) plen++;
|
||||
pbkdf2_f((const u8 *)passphrase, plen, ssid, ssid_len, 4096, 1, psk); /* DK1 → 前 20 字节 */
|
||||
pbkdf2_f((const u8 *)passphrase, plen, ssid, ssid_len, 4096, 2, psk + 20); /* DK2 → 后 12 字节 */
|
||||
}
|
||||
|
||||
/* ================= PRF-X (802.11i H.4) =================
|
||||
* PTK = PRF-512(PMK, "Pairwise key expansion", 串接 seed)。
|
||||
* seed 由调用方拼好:min(AA,SPA)||max(AA,SPA)||min(ANonce,SNonce)||max(...)。
|
||||
* 输出 64 字节:迭代 0..63,每字节取 HMAC-SHA1(PMK, label||seed||counter)[0]。
|
||||
*/
|
||||
void wpa2_prf512(const u8 *pmk, const u8 *seed, u32 seed_len, u8 out[64]) {
|
||||
static const char label[] = "Pairwise key expansion";
|
||||
u32 label_len = sizeof(label) - 1; /* 22 */
|
||||
u8 buf[22 + 64 + 1];
|
||||
for (u32 i = 0; i < label_len; i++) buf[i] = (u8)label[i];
|
||||
for (u32 i = 0; i < seed_len; i++) buf[label_len + i] = seed[i];
|
||||
if (seed_len > 64) seed_len = 64;
|
||||
for (int c = 0; c < 64; c++) {
|
||||
buf[label_len + seed_len] = (u8)c;
|
||||
u8 mac[20];
|
||||
wpa2_hmac_sha1(pmk, 32, buf, label_len + seed_len + 1, mac);
|
||||
out[c] = mac[0];
|
||||
}
|
||||
}
|
||||
|
||||
/* ================= AES-128 (FIPS-197,仅加密方向) ================= */
|
||||
|
||||
static const u8 aes_sbox[256] = {
|
||||
0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
|
||||
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
|
||||
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
|
||||
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
|
||||
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
|
||||
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
|
||||
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
|
||||
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
|
||||
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
|
||||
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
|
||||
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
|
||||
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
|
||||
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
|
||||
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
|
||||
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
|
||||
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16
|
||||
};
|
||||
|
||||
static const u8 aes_rcon[11] = {
|
||||
0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36
|
||||
};
|
||||
|
||||
static u32 aes_sub_word(u32 w) {
|
||||
return ((u32)aes_sbox[(w >> 24) & 0xFF] << 24) |
|
||||
((u32)aes_sbox[(w >> 16) & 0xFF] << 16) |
|
||||
((u32)aes_sbox[(w >> 8) & 0xFF] << 8) |
|
||||
(u32)aes_sbox[w & 0xFF];
|
||||
}
|
||||
|
||||
/* 展开轮密钥:rk[44](11 轮 × 4 word) */
|
||||
static void aes_key_expand(const u8 key[16], u32 rk[44]) {
|
||||
for (int i = 0; i < 4; i++)
|
||||
rk[i] = ((u32)key[i*4] << 24) | ((u32)key[i*4+1] << 16) |
|
||||
((u32)key[i*4+2] << 8) | (u32)key[i*4+3];
|
||||
for (int i = 4; i < 44; i++) {
|
||||
u32 t = rk[i-1];
|
||||
if (i % 4 == 0) {
|
||||
t = aes_sub_word((t << 8) | (t >> 24)) ^ ((u32)aes_rcon[i/4] << 24);
|
||||
}
|
||||
rk[i] = rk[i-4] ^ t;
|
||||
}
|
||||
}
|
||||
|
||||
void wpa2_aes128_enc_block(const u8 key[16], const u8 in[16], u8 out[16]) {
|
||||
u32 rk[44];
|
||||
aes_key_expand(key, rk);
|
||||
u32 s0,s1,s2,s3, t0,t1,t2,t3;
|
||||
s0 = rk[0] ^ ((u32)in[0] << 24 | (u32)in[1] << 16 | (u32)in[2] << 8 | in[3]);
|
||||
s1 = rk[1] ^ ((u32)in[4] << 24 | (u32)in[5] << 16 | (u32)in[6] << 8 | in[7]);
|
||||
s2 = rk[2] ^ ((u32)in[8] << 24 | (u32)in[9] << 16 | (u32)in[10] << 8 | in[11]);
|
||||
s3 = rk[3] ^ ((u32)in[12] << 24 | (u32)in[13] << 16 | (u32)in[14] << 8 | in[15]);
|
||||
#define AES_XTIME(x) ((u8)((((u8)(x)) << 1) ^ ((((u8)(x)) >> 7) * 0x1b)))
|
||||
for (int r = 1; r <= 10; r++) {
|
||||
/* SubBytes + ShiftRows(按列状态 s0..s3 每列 4 字节,state[col][row]) */
|
||||
u8 sb[4][4];
|
||||
u32 sv[4] = {s0, s1, s2, s3};
|
||||
for (int cCol = 0; cCol < 4; cCol++)
|
||||
for (int rRow = 0; rRow < 4; rRow++)
|
||||
sb[rRow][cCol] = aes_sbox[(sv[cCol] >> (24 - rRow * 8)) & 0xFF];
|
||||
/* ShiftRows:行 r 循环左移 r */
|
||||
{
|
||||
u8 t;
|
||||
t = sb[1][0]; sb[1][0]=sb[1][1]; sb[1][1]=sb[1][2]; sb[1][2]=sb[1][3]; sb[1][3]=t;
|
||||
t=sb[2][0]; sb[2][0]=sb[2][2]; sb[2][2]=t; t=sb[2][1]; sb[2][1]=sb[2][3]; sb[2][3]=t;
|
||||
t = sb[3][0]; sb[3][0]=sb[3][3]; sb[3][3]=sb[3][2]; sb[3][2]=sb[3][1]; sb[3][1]=t;
|
||||
}
|
||||
/* MixColumns(末轮跳过) */
|
||||
if (r < 10) {
|
||||
u32 o[4];
|
||||
for (int cCol = 0; cCol < 4; cCol++) {
|
||||
u8 a0=sb[0][cCol], a1=sb[1][cCol], a2=sb[2][cCol], a3=sb[3][cCol];
|
||||
u8 x1 = AES_XTIME(a0), x2 = AES_XTIME(a1);
|
||||
u8 x3 = AES_XTIME(a2), x4 = AES_XTIME(a3);
|
||||
sb[0][cCol] = (u8)(x1 ^ x2 ^ a1 ^ a2 ^ a3);
|
||||
sb[1][cCol] = (u8)(a0 ^ x2 ^ x3 ^ a2 ^ a3);
|
||||
sb[2][cCol] = (u8)(a0 ^ a1 ^ x3 ^ x4 ^ a3);
|
||||
sb[3][cCol] = (u8)(x1 ^ a0 ^ a1 ^ a2 ^ x4);
|
||||
}
|
||||
o[0] = ((u32)sb[0][0] << 24) | ((u32)sb[1][0] << 16) | ((u32)sb[2][0] << 8) | sb[3][0];
|
||||
o[1] = ((u32)sb[0][1] << 24) | ((u32)sb[1][1] << 16) | ((u32)sb[2][1] << 8) | sb[3][1];
|
||||
o[2] = ((u32)sb[0][2] << 24) | ((u32)sb[1][2] << 16) | ((u32)sb[2][2] << 8) | sb[3][2];
|
||||
o[3] = ((u32)sb[0][3] << 24) | ((u32)sb[1][3] << 16) | ((u32)sb[2][3] << 8) | sb[3][3];
|
||||
t0 = o[0]; t1 = o[1]; t2 = o[2]; t3 = o[3];
|
||||
} else {
|
||||
t0 = ((u32)sb[0][0] << 24) | ((u32)sb[1][0] << 16) | ((u32)sb[2][0] << 8) | sb[3][0];
|
||||
t1 = ((u32)sb[0][1] << 24) | ((u32)sb[1][1] << 16) | ((u32)sb[2][1] << 8) | sb[3][1];
|
||||
t2 = ((u32)sb[0][2] << 24) | ((u32)sb[1][2] << 16) | ((u32)sb[2][2] << 8) | sb[3][2];
|
||||
t3 = ((u32)sb[0][3] << 24) | ((u32)sb[1][3] << 16) | ((u32)sb[2][3] << 8) | sb[3][3];
|
||||
}
|
||||
s0 = t0 ^ rk[r*4+0];
|
||||
s1 = t1 ^ rk[r*4+1];
|
||||
s2 = t2 ^ rk[r*4+2];
|
||||
s3 = t3 ^ rk[r*4+3];
|
||||
}
|
||||
out[0]=(u8)(s0>>24); out[1]=(u8)(s0>>16); out[2]=(u8)(s0>>8); out[3]=(u8)s0;
|
||||
out[4]=(u8)(s1>>24); out[5]=(u8)(s1>>16); out[6]=(u8)(s1>>8); out[7]=(u8)s1;
|
||||
out[8]=(u8)(s2>>24); out[9]=(u8)(s2>>16); out[10]=(u8)(s2>>8); out[11]=(u8)s2;
|
||||
out[12]=(u8)(s3>>24); out[13]=(u8)(s3>>16); out[14]=(u8)(s3>>8); out[15]=(u8)s3;
|
||||
}
|
||||
|
||||
/* ================= AES-128 解密方向(RFC 3394 unwrap 用) ================= */
|
||||
|
||||
static const u8 aes_inv_sbox[256] = {
|
||||
0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38,0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb,
|
||||
0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87,0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb,
|
||||
0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d,0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e,
|
||||
0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2,0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25,
|
||||
0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16,0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92,
|
||||
0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda,0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84,
|
||||
0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a,0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06,
|
||||
0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02,0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b,
|
||||
0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea,0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73,
|
||||
0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85,0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e,
|
||||
0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89,0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b,
|
||||
0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20,0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4,
|
||||
0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31,0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f,
|
||||
0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d,0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef,
|
||||
0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0,0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61,
|
||||
0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26,0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d
|
||||
};
|
||||
|
||||
/* GF(2^8) 乘法(AES 域,模 x^8+x^4+x^3+x+1) */
|
||||
static u8 aes_gf_mul(u8 a, u8 b) {
|
||||
u8 p = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (b & 1) p ^= a;
|
||||
u8 hi = a & 0x80;
|
||||
a = (u8)(a << 1);
|
||||
if (hi) a ^= 0x1b;
|
||||
b >>= 1;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* AES-128 解密单块(等价逆密码结构,调试/unwrap 用) */
|
||||
void wpa2_aes128_dec_block(const u8 key[16], const u8 in[16], u8 out[16]) {
|
||||
u32 rk[44];
|
||||
aes_key_expand(key, rk);
|
||||
u32 s0 = rk[40] ^ ((u32)in[0] << 24 | (u32)in[1] << 16 | (u32)in[2] << 8 | in[3]);
|
||||
u32 s1 = rk[41] ^ ((u32)in[4] << 24 | (u32)in[5] << 16 | (u32)in[6] << 8 | in[7]);
|
||||
u32 s2 = rk[42] ^ ((u32)in[8] << 24 | (u32)in[9] << 16 | (u32)in[10] << 8 | in[11]);
|
||||
u32 s3 = rk[43] ^ ((u32)in[12] << 24 | (u32)in[13] << 16 | (u32)in[14] << 8 | in[15]);
|
||||
u32 sv[4];
|
||||
for (int r = 9; r >= 0; r--) {
|
||||
sv[0]=s0; sv[1]=s1; sv[2]=s2; sv[3]=s3;
|
||||
u8 sb[4][4];
|
||||
for (int cCol = 0; cCol < 4; cCol++)
|
||||
for (int rRow = 0; rRow < 4; rRow++)
|
||||
sb[rRow][cCol] = (u8)(sv[cCol] >> (24 - rRow * 8));
|
||||
/* InvShiftRows(行 r 循环右移 r)+ InvSubBytes */
|
||||
u8 t;
|
||||
t = sb[1][3]; sb[1][3]=sb[1][2]; sb[1][2]=sb[1][1]; sb[1][1]=sb[1][0]; sb[1][0]=t;
|
||||
t=sb[2][0]; sb[2][0]=sb[2][2]; sb[2][2]=t; t=sb[2][1]; sb[2][1]=sb[2][3]; sb[2][3]=t;
|
||||
t = sb[3][0]; sb[3][0]=sb[3][1]; sb[3][1]=sb[3][2]; sb[3][2]=sb[3][3]; sb[3][3]=t;
|
||||
for (int cCol = 0; cCol < 4; cCol++)
|
||||
for (int rRow = 0; rRow < 4; rRow++)
|
||||
sb[rRow][cCol] = aes_inv_sbox[sb[rRow][cCol]];
|
||||
/* AddRoundKey(r) */
|
||||
u32 nrk[4] = { rk[r*4+0], rk[r*4+1], rk[r*4+2], rk[r*4+3] };
|
||||
{
|
||||
u32 o[4];
|
||||
for (int cCol = 0; cCol < 4; cCol++)
|
||||
o[cCol] = ((u32)sb[0][cCol] << 24) | ((u32)sb[1][cCol] << 16) |
|
||||
((u32)sb[2][cCol] << 8) | (u32)sb[3][cCol];
|
||||
s0 = o[0] ^ nrk[0]; s1 = o[1] ^ nrk[1]; s2 = o[2] ^ nrk[2]; s3 = o[3] ^ nrk[3];
|
||||
}
|
||||
/* InvMixColumns(末轮 r=0 跳过):作用在 AddRoundKey 之后的状态上 */
|
||||
if (r > 0) {
|
||||
sv[0]=s0; sv[1]=s1; sv[2]=s2; sv[3]=s3;
|
||||
for (int cCol = 0; cCol < 4; cCol++)
|
||||
for (int rRow = 0; rRow < 4; rRow++)
|
||||
sb[rRow][cCol] = (u8)(sv[cCol] >> (24 - rRow * 8));
|
||||
for (int cCol = 0; cCol < 4; cCol++) {
|
||||
u8 a0=sb[0][cCol], a1=sb[1][cCol], a2=sb[2][cCol], a3=sb[3][cCol];
|
||||
sb[0][cCol] = (u8)(aes_gf_mul(a0,0x0e) ^ aes_gf_mul(a1,0x0b) ^ aes_gf_mul(a2,0x0d) ^ aes_gf_mul(a3,0x09));
|
||||
sb[1][cCol] = (u8)(aes_gf_mul(a0,0x09) ^ aes_gf_mul(a1,0x0e) ^ aes_gf_mul(a2,0x0b) ^ aes_gf_mul(a3,0x0d));
|
||||
sb[2][cCol] = (u8)(aes_gf_mul(a0,0x0d) ^ aes_gf_mul(a1,0x09) ^ aes_gf_mul(a2,0x0e) ^ aes_gf_mul(a3,0x0b));
|
||||
sb[3][cCol] = (u8)(aes_gf_mul(a0,0x0b) ^ aes_gf_mul(a1,0x0d) ^ aes_gf_mul(a2,0x09) ^ aes_gf_mul(a3,0x0e));
|
||||
}
|
||||
u32 o[4];
|
||||
for (int cCol = 0; cCol < 4; cCol++)
|
||||
o[cCol] = ((u32)sb[0][cCol] << 24) | ((u32)sb[1][cCol] << 16) |
|
||||
((u32)sb[2][cCol] << 8) | (u32)sb[3][cCol];
|
||||
s0 = o[0]; s1 = o[1]; s2 = o[2]; s3 = o[3];
|
||||
}
|
||||
}
|
||||
out[0]=(u8)(s0>>24); out[1]=(u8)(s0>>16); out[2]=(u8)(s0>>8); out[3]=(u8)s0;
|
||||
out[4]=(u8)(s1>>24); out[5]=(u8)(s1>>16); out[6]=(u8)(s1>>8); out[7]=(u8)s1;
|
||||
out[8]=(u8)(s2>>24); out[9]=(u8)(s2>>16); out[10]=(u8)(s2>>8); out[11]=(u8)s2;
|
||||
out[12]=(u8)(s3>>24); out[13]=(u8)(s3>>16); out[14]=(u8)(s3>>8); out[15]=(u8)s3;
|
||||
}
|
||||
|
||||
/* ================= RFC 3394 AES Key Unwrap ================= */
|
||||
|
||||
static const u8 wrap_iv[8] = {0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6};
|
||||
|
||||
int wpa2_key_unwrap(const u8 kek[16], const u8 *in, u32 in_len, u8 *out) {
|
||||
if (in_len < 24 || (in_len % 8) != 0) return -1;
|
||||
u32 n = in_len / 8 - 1;
|
||||
u8 areg[8];
|
||||
for (int i = 0; i < 8; i++) areg[i] = in[i];
|
||||
/* R 块直接在 out 缓冲中处理(out 容量须 ≥ in_len-8,调用方保证) */
|
||||
u8 *r = out;
|
||||
for (u32 i = 0; i < n * 8; i++) r[i] = in[8 + i];
|
||||
u8 blockin[16], b[16];
|
||||
for (int j = 5; j >= 0; j--) {
|
||||
for (u32 i = n; i >= 1; i--) {
|
||||
/* RFC 3394: B = AES-1(K, (A ^ t) | R[i]),t = n*j + i */
|
||||
u64 t = (u64)(n * (u32)j + i);
|
||||
for (int k = 0; k < 8; k++)
|
||||
blockin[k] = areg[k] ^ (u8)((t >> (56 - k * 8)) & 0xFF);
|
||||
for (int k = 0; k < 8; k++) blockin[8+k] = r[(i-1)*8+k];
|
||||
wpa2_aes128_dec_block(kek, blockin, b);
|
||||
for (int k = 0; k < 8; k++) areg[k] = b[k];
|
||||
for (int k = 0; k < 8; k++) r[(i-1)*8+k] = b[8+k];
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 8; i++) if (areg[i] != wrap_iv[i]) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================= CCMP (RFC 3610 + 802.11i) =================
|
||||
* STA 收 AP 数据帧方向:MIC(CBC-MAC) 验证 + CTR 解密。
|
||||
* 帧 = MAC 头(24B, 3 地址) || CCMP 头(8B) || 密文 || MIC(8B)。
|
||||
*/
|
||||
|
||||
#define CCMP_HDR_LEN 8u
|
||||
#define CCMP_MIC_LEN 8u
|
||||
|
||||
static void ccmp_nonce16(const u8 *frame, const u8 *chdr, u8 nonce[16]) {
|
||||
nonce[0] = 0; /* priority = 0 */
|
||||
for (int i = 0; i < 6; i++) nonce[1+i] = frame[10+i]; /* A2 = SA */
|
||||
/* CCMP 头布局:b0=PN0 b1=PN1 b2=rsvd b3=KeyID b4=PN2 b5=PN3 b6=PN4 b7=PN5 */
|
||||
nonce[7] = chdr[0]; /* PN0 */
|
||||
nonce[8] = chdr[1]; /* PN1 */
|
||||
nonce[9] = chdr[4]; /* PN2 */
|
||||
nonce[10] = chdr[5]; /* PN3 */
|
||||
nonce[11] = chdr[6]; /* PN4 */
|
||||
nonce[12] = chdr[7]; /* PN5 */
|
||||
nonce[13] = 0; nonce[14] = 0; nonce[15] = 0;
|
||||
}
|
||||
|
||||
static void ccmp_ctr_block(const u8 nonce[16], u16 counter, u8 ctr[16]) {
|
||||
/* RFC 3610 A_i = flags(L-1=1) || 13B nonce || counter(2B) */
|
||||
ctr[0] = 0x01;
|
||||
for (int i = 0; i < 13; i++) ctr[1+i] = nonce[i];
|
||||
ctr[14] = (u8)(counter >> 8);
|
||||
ctr[15] = (u8)(counter);
|
||||
}
|
||||
|
||||
/* ---- CCMP 头构造:PN 按 802.11i 散布写入 ---- */
|
||||
static void ccmp_header_fill(u8 *chdr, u64 pn, u8 key_id) {
|
||||
chdr[0] = (u8)(pn & 0xFF); /* PN0 */
|
||||
chdr[1] = (u8)((pn >> 8) & 0xFF); /* PN1 */
|
||||
chdr[2] = 0; /* Reserved */
|
||||
chdr[3] = (u8)(0x20 | (key_id & 0x03)); /* ExtIV=1 | KeyID */
|
||||
chdr[4] = (u8)((pn >> 16) & 0xFF); /* PN2 */
|
||||
chdr[5] = (u8)((pn >> 24) & 0xFF); /* PN3 */
|
||||
chdr[6] = (u8)((pn >> 32) & 0xFF); /* PN4 */
|
||||
chdr[7] = (u8)((pn >> 40) & 0xFF); /* PN5 */
|
||||
}
|
||||
|
||||
/* ---- CBC-MAC:B0 || AAD(24B) || 密文 → MIC(16B)(收发共用) ---- */
|
||||
static void ccmp_cbc_mac(const u8 *frame, const u8 *nonce,
|
||||
const u8 *ct, u32 ct_len, const u8 tk[16], u8 mac[16]) {
|
||||
u16 fctl = (u16)frame[0] | ((u16)frame[1] << 8);
|
||||
u16 fc = fctl & 0xC78F;
|
||||
u16 sc = (u16)frame[22] | ((u16)frame[23] << 8);
|
||||
sc &= 0x000F;
|
||||
u8 abuf[24];
|
||||
abuf[0] = 0; abuf[1] = 22;
|
||||
abuf[2] = (u8)(fc & 0xFF); abuf[3] = (u8)(fc >> 8);
|
||||
for (int i = 0; i < 18; i++) abuf[4+i] = frame[4+i];
|
||||
abuf[22] = (u8)(sc & 0xFF); abuf[23] = (u8)(sc >> 8);
|
||||
|
||||
u8 blk[16], b0[16];
|
||||
b0[0] = 0x19; /* Adata=0, M=3(8B), L-1=1 */
|
||||
for (int i = 0; i < 13; i++) b0[1+i] = nonce[i];
|
||||
b0[14] = (u8)(ct_len >> 8); b0[15] = (u8)ct_len;
|
||||
wpa2_aes128_enc_block(tk, b0, mac);
|
||||
for (int i = 0; i < 16; i++) blk[i] = abuf[i] ^ mac[i];
|
||||
wpa2_aes128_enc_block(tk, blk, mac);
|
||||
for (int i = 0; i < 8; i++) blk[i] = abuf[16+i] ^ mac[i];
|
||||
for (int i = 8; i < 16; i++) blk[i] = mac[i];
|
||||
wpa2_aes128_enc_block(tk, blk, mac);
|
||||
for (u32 off = 0; off < ct_len; off += 16) {
|
||||
u32 take = ct_len - off;
|
||||
if (take > 16) take = 16;
|
||||
for (u32 i = 0; i < 16; i++)
|
||||
blk[i] = (i < take ? ct[off+i] : 0) ^ mac[i];
|
||||
wpa2_aes128_enc_block(tk, blk, mac);
|
||||
}
|
||||
}
|
||||
|
||||
int wpa2_ccmp_decrypt(const u8 *frame, u32 len, const u8 tk[16], u8 *out, u32 out_cap) {
|
||||
u32 hdr = 24; /* 3 地址 data 帧 */
|
||||
if (len < hdr + CCMP_HDR_LEN + CCMP_MIC_LEN) return -2;
|
||||
const u8 *chdr = frame + hdr;
|
||||
const u8 *ct = frame + hdr + CCMP_HDR_LEN;
|
||||
u32 ct_len = len - hdr - CCMP_HDR_LEN - CCMP_MIC_LEN;
|
||||
if (ct_len == 0) return -2;
|
||||
if (ct_len > out_cap) return -2;
|
||||
|
||||
u8 nonce[16]; /* 13B CCMP 非立即在 [0..12] */
|
||||
ccmp_nonce16(frame, chdr, nonce);
|
||||
|
||||
/* ---- MIC 校验(基于密文) ---- */
|
||||
u8 mac[16];
|
||||
ccmp_cbc_mac(frame, nonce, ct, ct_len, tk, mac);
|
||||
u8 diff = 0;
|
||||
for (u32 i = 0; i < CCMP_MIC_LEN; i++) diff |= mac[i] ^ ct[ct_len + i];
|
||||
if (diff) return -1; /* MIC 校验失败 */
|
||||
|
||||
/* ---- CTR 解密 ---- */
|
||||
u16 counter = 1;
|
||||
for (u32 off = 0; off < ct_len; off += 16) {
|
||||
u8 ctr[16], s[16];
|
||||
ccmp_ctr_block(nonce, counter++, ctr);
|
||||
wpa2_aes128_enc_block(tk, ctr, s);
|
||||
u32 take = ct_len - off;
|
||||
if (take > 16) take = 16;
|
||||
for (u32 i = 0; i < take; i++)
|
||||
out[off+i] = ct[off+i] ^ s[i];
|
||||
}
|
||||
return (int)ct_len;
|
||||
}
|
||||
|
||||
int wpa2_ccmp_encrypt(u8 *buf, u32 buf_cap, const u8 *msdu, u32 msdu_len,
|
||||
const u8 tk[16], u64 pn) {
|
||||
if (!buf || !msdu || msdu_len == 0) return -1;
|
||||
if (msdu_len + 24 + CCMP_HDR_LEN + CCMP_MIC_LEN > buf_cap) return -1;
|
||||
|
||||
u8 *chdr = buf + 24;
|
||||
ccmp_header_fill(chdr, pn, 0);
|
||||
u8 *ct = chdr + CCMP_HDR_LEN;
|
||||
|
||||
u8 nonce[16];
|
||||
ccmp_nonce16(buf, chdr, nonce);
|
||||
|
||||
/* CTR 加密 */
|
||||
u16 counter = 1;
|
||||
for (u32 off = 0; off < msdu_len; off += 16) {
|
||||
u8 ctr[16], s[16];
|
||||
ccmp_ctr_block(nonce, counter++, ctr);
|
||||
wpa2_aes128_enc_block(tk, ctr, s);
|
||||
u32 take = msdu_len - off;
|
||||
if (take > 16) take = 16;
|
||||
for (u32 i = 0; i < take; i++) ct[off+i] = msdu[off+i] ^ s[i];
|
||||
}
|
||||
|
||||
u8 mac[16];
|
||||
ccmp_cbc_mac(buf, nonce, ct, msdu_len, tk, mac);
|
||||
for (u32 i = 0; i < CCMP_MIC_LEN; i++) ct[msdu_len + i] = mac[i];
|
||||
return (int)(24 + CCMP_HDR_LEN + msdu_len + CCMP_MIC_LEN);
|
||||
}
|
||||
|
||||
/* ================= KAT ================= */
|
||||
|
||||
static int buf_eq(const u8 *a, const u8 *b, u32 n) {
|
||||
u8 d = 0;
|
||||
for (u32 i = 0; i < n; i++) d |= a[i] ^ b[i];
|
||||
return d == 0;
|
||||
}
|
||||
|
||||
/* CCMP 自洽往返:用生产 encrypt 构造,再走生产 decrypt。
|
||||
* 覆盖 nonce/AAD/B0/CTR 字节布局 + MIC 篡改检测,防索引回归。 */
|
||||
static int ccmp_selftest(void) {
|
||||
static const char payload[] = "Deshab CCMP round-trip payload 0123456789";
|
||||
u32 plen = sizeof(payload) - 1;
|
||||
|
||||
u8 tk[16];
|
||||
for (int i = 0; i < 16; i++) tk[i] = (u8)(0x30 + i);
|
||||
|
||||
u8 frame[96];
|
||||
for (u32 i = 0; i < sizeof(frame); i++) frame[i] = 0;
|
||||
/* 802.11 data 头:FC=0x0841 (data, toDS),A1/A2/A3,seq ctrl */
|
||||
frame[0] = 0x41; frame[1] = 0x08;
|
||||
for (int i = 0; i < 6; i++) frame[4+i] = (u8)(0xA0 + i); /* A1 = AP */
|
||||
for (int i = 0; i < 6; i++) frame[10+i] = (u8)(0x10 + i); /* A2 = STA */
|
||||
for (int i = 0; i < 6; i++) frame[16+i] = (u8)(0xB0 + i); /* A3 = DA */
|
||||
frame[22] = 0x10; frame[23] = 0x00;
|
||||
|
||||
int flen = wpa2_ccmp_encrypt(frame, sizeof(frame), (const u8 *)payload, plen,
|
||||
tk, 0x000000000001ULL);
|
||||
if (flen != (int)(24 + 8 + plen + 8)) return -1;
|
||||
|
||||
u8 out[96];
|
||||
int rc = wpa2_ccmp_decrypt(frame, (u32)flen, tk, out, sizeof(out));
|
||||
if (rc != (int)plen) return -1;
|
||||
for (u32 i = 0; i < plen; i++) if (out[i] != (u8)payload[i]) return -1;
|
||||
|
||||
/* 篡改密文 → 必须 MIC 校验失败 */
|
||||
frame[24 + 8] ^= 0x01;
|
||||
if (wpa2_ccmp_decrypt(frame, (u32)flen, tk, out, sizeof(out)) != -1) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wpa2_kat(void) {
|
||||
u8 out[64];
|
||||
|
||||
/* 1. SHA1("abc") = a9993e36...9cd0d89d */
|
||||
wpa2_sha1((const u8 *)"abc", 3, out);
|
||||
{
|
||||
static const u8 exp[20] = {
|
||||
0xa9,0x99,0x3e,0x36,0x47,0x06,0x81,0x6a,0xba,0x3e,
|
||||
0x25,0x71,0x78,0x50,0xc2,0x6c,0x9c,0xd0,0xd8,0x9d };
|
||||
if (!buf_eq(out, exp, 20)) return 1;
|
||||
}
|
||||
|
||||
/* 2. HMAC-SHA1 RFC 2202 case 2: key "Jefe" data "what do ya want for nothing?"
|
||||
* = effcdf6ae5eb2fa2d27416d5f184df9c259a7c79 */
|
||||
wpa2_hmac_sha1((const u8 *)"Jefe", 4,
|
||||
(const u8 *)"what do ya want for nothing?", 28, out);
|
||||
{
|
||||
static const u8 exp[20] = {
|
||||
0xef,0xfc,0xdf,0x6a,0xe5,0xeb,0x2f,0xa2,0xd2,0x74,
|
||||
0x16,0xd5,0xf1,0x84,0xdf,0x9c,0x25,0x9a,0x7c,0x79 };
|
||||
if (!buf_eq(out, exp, 20)) return 2;
|
||||
}
|
||||
|
||||
/* 3. PBKDF2-HMAC-SHA1 RFC 6070: ("password","salt",1,20)
|
||||
* = 0c60c80f961f0e71f3a9b524af6012062fe037a6 */
|
||||
{
|
||||
u8 dk[32];
|
||||
/* 手动 1 iter 版本走 pbkdf2_f 不可见,直接用 4096 向量(与 WPA 一致):
|
||||
* RFC 6070 case 3: ("password","salt",4096,20)
|
||||
* = 4b007901b765489abead49d926f721d065a429c1 */
|
||||
wpa2_pbkdf2_sha1("password", (const u8 *)"salt", 4, dk);
|
||||
static const u8 exp[20] = {
|
||||
0x4b,0x00,0x79,0x01,0xb7,0x65,0x48,0x9a,0xbe,0xad,
|
||||
0x49,0xd9,0x26,0xf7,0x21,0xd0,0x65,0xa4,0x29,0xc1 };
|
||||
if (!buf_eq(dk, exp, 20)) return 3;
|
||||
}
|
||||
|
||||
/* 4. AES-128 FIPS-197 C.1: key 000102...0f, pt 00112233...eeff
|
||||
* = 69c4e0d86a7b0430d8cdb78070b4c55a */
|
||||
{
|
||||
u8 key[16], pt[16], ct[16];
|
||||
for (int i = 0; i < 16; i++) { key[i]=(u8)i; pt[i]=(u8)(i*0x11); }
|
||||
wpa2_aes128_enc_block(key, pt, ct);
|
||||
static const u8 exp[16] = {
|
||||
0x69,0xc4,0xe0,0xd8,0x6a,0x7b,0x04,0x30,
|
||||
0xd8,0xcd,0xb7,0x80,0x70,0xb4,0xc5,0x5a };
|
||||
if (!buf_eq(ct, exp, 16)) return 4;
|
||||
}
|
||||
|
||||
/* 5. AES-128 FIPS-197 C.3 (256bit key 前半忽略,此处用 C.1 变体覆盖 key schedule):
|
||||
* key 2b7e1516 28aed2a6 abf71588 09cf4f3c
|
||||
* pt 6bc1bee2 2e409f96 e93d7e11 7393172a
|
||||
* ct 3ad77bb4 0d7a3660 a89ecaf3 2466ef97 */
|
||||
{
|
||||
static const u8 key[16] = {0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,
|
||||
0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c};
|
||||
static const u8 pt[16] = {0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,
|
||||
0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a};
|
||||
static const u8 exp[16] = {0x3a,0xd7,0x7b,0xb4,0x0d,0x7a,0x36,0x60,
|
||||
0xa8,0x9e,0xca,0xf3,0x24,0x66,0xef,0x97};
|
||||
u8 ct[16];
|
||||
wpa2_aes128_enc_block(key, pt, ct);
|
||||
if (!buf_eq(ct, exp, 16)) return 5;
|
||||
}
|
||||
|
||||
/* 6. CCMP:构造→解密 往返 + 篡改检测 */
|
||||
if (ccmp_selftest() != 0) return 6;
|
||||
|
||||
/* 7. RFC 3394 key unwrap(C.1 128-bit KEK 向量) */
|
||||
{
|
||||
static const u8 kek[16] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
|
||||
0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F};
|
||||
static const u8 wrapped[24] = {0x1F,0xA6,0x8B,0x0A,0x81,0x12,0xB4,0x47,
|
||||
0xAE,0xF3,0x4B,0xD8,0xFB,0x5A,0x7B,0x82,
|
||||
0x9D,0x3E,0x86,0x23,0x71,0xD2,0xCF,0xE5};
|
||||
static const u8 expect[16] = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
|
||||
0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF};
|
||||
u8 plain[16];
|
||||
if (wpa2_key_unwrap(kek, wrapped, 24, plain) != 0) return 7;
|
||||
if (!buf_eq(plain, expect, 16)) return 7;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/* wpa2_crypto.h — WPA2-PSK 握手所需加密底座(DKM ath9k 专用)
|
||||
*
|
||||
* 范围(802.11i RSNA,AKM 00-0F-AC:2 PSK):
|
||||
* SHA1 — RFC 3174
|
||||
* HMAC-SHA1 — RFC 2204(EAPOL-Key MIC ver2 = HMAC-SHA1-128)
|
||||
* PBKDF2-HMAC-SHA1 — RFC 2898(passphrase → PSK,4096 iter)
|
||||
* PRF-X — 802.11i H.4(PMK → 512bit PTK)
|
||||
* AES-128 — FIPS-197(仅加密方向;CCMP 的 CTR 与 CBC-MAC 都用 AES-ENC)
|
||||
* AES Key Unwrap — RFC 3394(KEK 解密 M3 key data 中的 GTK KDE)
|
||||
* CCMP — RFC 3610(data 帧 MIC 验证 + 计数器解密;加密留 TX 数据路径)
|
||||
*
|
||||
* 自检:wpa2_kat() 内置 RFC 2202/6070/FIPS-197 已知答案向量,
|
||||
* driver_init 时调用,任一失败 panic(ATH-E02)。
|
||||
* 约束:无堆(静态/栈缓冲)、无除法热路径、-mno-sse 可编译。
|
||||
*/
|
||||
#ifndef ATH9K_WPA2_CRYPTO_H
|
||||
#define ATH9K_WPA2_CRYPTO_H
|
||||
|
||||
#include "../dkm_shared.h"
|
||||
|
||||
#define WPA2_PMK_LEN 32
|
||||
#define WPA2_PTK_LEN 48 /* KCK(16)+KEK(16)+TK(16)(802.11i CCMP 不取 TKIP MIC 子钥) */
|
||||
#define WPA2_KCK_LEN 16
|
||||
#define WPA2_KEK_LEN 16
|
||||
#define WPA2_TK_LEN 16
|
||||
|
||||
/* SHA1:out[20] */
|
||||
void wpa2_sha1(const u8 *data, u32 len, u8 out[20]);
|
||||
/* 增量 SHA1(EAPOL MIC 需要对任意长帧做 HMAC,直接给一次性接口) */
|
||||
void wpa2_hmac_sha1(const u8 *key, u32 key_len,
|
||||
const u8 *data, u32 data_len, u8 out[20]);
|
||||
/* PBKDF2-HMAC-SHA1:passphrase → psk[32](iter 固定 4096,salt=SSID) */
|
||||
void wpa2_pbkdf2_sha1(const char *passphrase, const u8 *ssid, u32 ssid_len,
|
||||
u8 psk[WPA2_PMK_LEN]);
|
||||
/* PRF-512(802.11i H.4):PMK+label+addr/nonce 串接 → out[64] */
|
||||
void wpa2_prf512(const u8 *pmk, const u8 *seed, u32 seed_len, u8 out[64]);
|
||||
|
||||
/* AES-128 单块加密(in/out 16 字节,可同址) */
|
||||
void wpa2_aes128_enc_block(const u8 key[16], const u8 in[16], u8 out[16]);
|
||||
/* AES-128 单块解密(RFC 3394 unwrap 用;CCMP 路径不使用) */
|
||||
void wpa2_aes128_dec_block(const u8 key[16], const u8 in[16], u8 out[16]);
|
||||
|
||||
/* RFC 3394 key unwrap:in 为 n*8 字节密文(n>=2),out 得 (n-1)*8 明文;
|
||||
* 返回 0 成功,-1 integrity check 失败(AIV != 0xA6A6A6A6A6A6A6A6)。 */
|
||||
int wpa2_key_unwrap(const u8 kek[16], const u8 *in, u32 in_len, u8 *out);
|
||||
|
||||
/* CCMP 解密(STA 收 AP 数据帧,非 QoS,3 地址):
|
||||
* frame 指向 802.11 MAC 头起始,len 为整帧长度(含 CCMP 8B 头 + 8B MIC)。
|
||||
* 成功时把解密后的数据载荷(去掉 CCMP 头/MIC/FCS 前的明文 MSDU)
|
||||
* 写入 out 并返回明文长度;MIC 错误返回 -1,参数错返回 -2。 */
|
||||
int wpa2_ccmp_decrypt(const u8 *frame, u32 len, const u8 tk[16], u8 *out, u32 out_cap);
|
||||
|
||||
/* CCMP 加密(STA 发 AP 数据帧):在 buf 内就地构造
|
||||
* [MAC 头(24B,调用方先填好)][CCMP 头(8B)][密文][MIC(8B)]。
|
||||
* buf : 调用方提供的缓冲,前 24 字节须已填 MAC 头
|
||||
* msdu : 明文 MSDU
|
||||
* msdu_len: 明文长度(>0)
|
||||
* pn : 发包序号(调用方递增维护)
|
||||
* 返回写入 buf 的总帧长(24+8+msdu_len+8);缓冲不足返回 -1。 */
|
||||
int wpa2_ccmp_encrypt(u8 *buf, u32 buf_cap, const u8 *msdu, u32 msdu_len,
|
||||
const u8 tk[16], u64 pn);
|
||||
|
||||
/* KAT(已知答案自检):返回 0 = 全部通过;非 0 = 第几个用例失败(1 基)。
|
||||
* driver_init 必须调用,失败方 panic(ATH-E02)。 */
|
||||
int wpa2_kat(void);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user