xref: /OK3568_Linux_fs/external/rkwifibt/drivers/rtl8821cs/core/crypto/gcmp.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * GCM with GMAC Protocol (GCMP)
3  * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8 
9 #include "rtw_crypto_wrap.h"
10 
11 #include "aes.h"
12 #include "aes_wrap.h"
13 #include "wlancrypto_wrap.h"
14 
15 
gcmp_aad_nonce(_adapter * padapter,const struct ieee80211_hdr * hdr,const u8 * data,u8 * aad,size_t * aad_len,u8 * nonce)16 static void gcmp_aad_nonce(_adapter * padapter, const struct ieee80211_hdr *hdr, const u8 *data,
17 			   u8 *aad, size_t *aad_len, u8 *nonce)
18 {
19 	u16 fc, stype, seq;
20 	int qos = 0, addr4 = 0;
21 	u8 *pos;
22 
23 	fc = le_to_host16(hdr->frame_control);
24 	stype = WLAN_FC_GET_STYPE(fc);
25 	if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
26 	    (WLAN_FC_TODS | WLAN_FC_FROMDS))
27 		addr4 = 1;
28 
29 	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) {
30 		fc &= ~0x0070; /* Mask subtype bits */
31 		if (stype & WLAN_FC_STYPE_QOS_DATA) {
32 			const u8 *qc;
33 			qos = 1;
34 			fc &= ~WLAN_FC_ORDER;
35 			qc = (const u8 *)hdr + 24;
36 			if (addr4)
37 				qc += ETH_ALEN;
38 		}
39 	}
40 
41 	fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
42 	WPA_PUT_LE16(aad, fc);
43 	pos = aad + 2;
44 	os_memcpy(pos, GetAddr1Ptr((u8 *)hdr), 3 * ETH_ALEN);
45 	pos += 3 * ETH_ALEN;
46 	seq = le_to_host16(hdr->seq_ctrl);
47 	seq &= ~0xfff0; /* Mask Seq#; do not modify Frag# */
48 	WPA_PUT_LE16(pos, seq);
49 	pos += 2;
50 
51 	wpa_printf(_MSG_INFO_, "pos - aad = %u, qos(%d)\n", (pos - aad), qos);
52 
53 	os_memcpy(pos, (u8 *)hdr + 24, addr4 * ETH_ALEN + qos * 2);
54 	pos += addr4 * ETH_ALEN;
55 	if (qos) {
56 		pos[0] &= ~0x70;
57 		/* only spp mode need to refer QoS bit7 */
58 		if (padapter->registrypriv.amsdu_mode != RTW_AMSDU_MODE_SPP)
59 			pos[0] &= ~0x80;
60 		pos++;
61 		*pos++ = 0x00;
62 	}
63 
64 	wpa_printf(_MSG_INFO_, "pos - aad = %u\n", (pos - aad));
65 	*aad_len = pos - aad;
66 
67 	os_memcpy(nonce, hdr->addr2, ETH_ALEN);
68 	nonce[6] = data[7]; /* PN5 */
69 	nonce[7] = data[6]; /* PN4 */
70 	nonce[8] = data[5]; /* PN3 */
71 	nonce[9] = data[4]; /* PN2 */
72 	nonce[10] = data[1]; /* PN1 */
73 	nonce[11] = data[0]; /* PN0 */
74 }
75 
76 /**
77  * gcmp_decrypt -
78  * @tk: the temporal key
79  * @tk_len: length of @tk
80  * @hdr: the mac header
81  * @data: payload after mac header (PN + enc_data + MIC)
82  * @data_len: length of @data (PN + enc_data + MIC)
83  * @decrypted_len: length of the data decrypted
84  */
gcmp_decrypt(_adapter * padapter,const u8 * tk,size_t tk_len,const struct ieee80211_hdr * hdr,const u8 * data,size_t data_len,size_t * decrypted_len)85 u8 * gcmp_decrypt(_adapter *padapter, const u8 *tk, size_t tk_len, const struct ieee80211_hdr *hdr,
86 		  const u8 *data, size_t data_len, size_t *decrypted_len)
87 {
88 	u8 aad[30], nonce[12], *plain;
89 	size_t aad_len, mlen;
90 	const u8 *m;
91 
92 	if (data_len < 8 + 16)
93 		return NULL;
94 
95 	plain = os_malloc(data_len + AES_BLOCK_SIZE);
96 	if (plain == NULL)
97 		return NULL;
98 
99 	m = data + 8;
100 	mlen = data_len - 8 - 16;
101 
102 	os_memset(aad, 0, sizeof(aad));
103 	gcmp_aad_nonce(padapter, hdr, data, aad, &aad_len, nonce);
104 	wpa_hexdump(_MSG_EXCESSIVE_, "GCMP AAD", aad, aad_len);
105 	wpa_hexdump(_MSG_EXCESSIVE_, "GCMP nonce", nonce, sizeof(nonce));
106 
107 	if (aes_gcm_ad(tk, tk_len, nonce, sizeof(nonce), m, mlen, aad, aad_len,
108 		       m + mlen, plain) < 0) {
109 		u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
110 		wpa_printf(_MSG_INFO_, "Invalid GCMP frame: A1=" MACSTR
111 			   " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u",
112 			   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
113 			   MAC2STR(hdr->addr3),
114 			   WLAN_GET_SEQ_SEQ(seq_ctrl),
115 			   WLAN_GET_SEQ_FRAG(seq_ctrl));
116 		rtw_mfree(plain, data_len + AES_BLOCK_SIZE);
117 		return NULL;
118 	}
119 
120 	*decrypted_len = mlen;
121 	return plain;
122 }
123 
124 /**
125  * gcmp_encrypt -
126  * @tk: the temporal key
127  * @tk_len: length of @tk
128  * @frame: the point to mac header, the frame including mac header and payload,
129  *         if @pn is NULL, then the frame including pn
130  * @len: length of @frame
131  *         length = mac header + payload
132  * @hdrlen: length of the mac header
133  * @qos: pointer to the QOS field of the frame
134  * @pn: packet number
135  * @keyid: key id
136  * @encrypted_len: length of the encrypted frame
137  *                 including mac header, pn, payload and MIC
138  */
gcmp_encrypt(_adapter * padapter,const u8 * tk,size_t tk_len,const u8 * frame,size_t len,size_t hdrlen,const u8 * qos,const u8 * pn,int keyid,size_t * encrypted_len)139 u8 * gcmp_encrypt(_adapter *padapter, const u8 *tk, size_t tk_len, const u8 *frame, size_t len,
140 		  size_t hdrlen, const u8 *qos,
141 		  const u8 *pn, int keyid, size_t *encrypted_len)
142 {
143 	u8 aad[30], nonce[12], *crypt, *pos;
144 	const u8 *pdata;
145 	size_t aad_len, plen;
146 	struct ieee80211_hdr *hdr;
147 
148 	if (len < hdrlen || hdrlen < 24)
149 		return NULL;
150 	plen = len - hdrlen;
151 
152 	crypt = os_malloc(hdrlen + 8 + plen + 16 + AES_BLOCK_SIZE);
153 	if (crypt == NULL)
154 		return NULL;
155 
156 	if (pn == NULL) {
157 		os_memcpy(crypt, frame, hdrlen + 8);
158 		hdr = (struct ieee80211_hdr *)crypt;
159 		pos = crypt + hdrlen + 8;
160 		pdata = frame + hdrlen + 8;
161 	} else {
162 		os_memcpy(crypt, frame, hdrlen);
163 		hdr = (struct ieee80211_hdr *)crypt;
164 		pos = crypt + hdrlen;
165 
166 		*pos++ = pn[5]; /* PN0 */
167 		*pos++ = pn[4]; /* PN1 */
168 		*pos++ = 0x00; /* Rsvd */
169 		*pos++ = 0x20 | (keyid << 6);
170 		*pos++ = pn[3]; /* PN2 */
171 		*pos++ = pn[2]; /* PN3 */
172 		*pos++ = pn[1]; /* PN4 */
173 		*pos++ = pn[0]; /* PN5 */
174 		pdata = frame + hdrlen;
175 	}
176 
177 	os_memset(aad, 0, sizeof(aad));
178 	gcmp_aad_nonce(padapter, hdr, crypt + hdrlen, aad, &aad_len, nonce);
179 	wpa_hexdump(_MSG_EXCESSIVE_, "GCMP AAD", aad, aad_len);
180 	wpa_hexdump(_MSG_EXCESSIVE_, "GCMP nonce", nonce, sizeof(nonce));
181 
182 	if (aes_gcm_ae(tk, tk_len, nonce, sizeof(nonce), pdata, plen,
183 			aad, aad_len, pos, pos + plen) < 0) {
184 		rtw_mfree(crypt, hdrlen + 8 + plen + 16 + AES_BLOCK_SIZE);
185 		return NULL;
186 	}
187 
188 	wpa_hexdump(_MSG_EXCESSIVE_, "GCMP MIC", pos + plen, 16);
189 	wpa_hexdump(_MSG_EXCESSIVE_, "GCMP encrypted", pos, plen);
190 
191 	*encrypted_len = hdrlen + 8 + plen + 16;
192 
193 	return crypt;
194 }
195