blob: 74a524d21d626544fa55860fa156307046e87634 [file] [log] [blame]
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001/*
2 * DPP functionality shared between hostapd and wpa_supplicant
3 * Copyright (c) 2017, Qualcomm Atheros, Inc.
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "utils/includes.h"
10#include <openssl/opensslv.h>
11#include <openssl/err.h>
Roshan Pius3a1667e2018-07-03 15:17:14 -070012#include <openssl/asn1.h>
13#include <openssl/asn1t.h>
Dmitry Shmidtd2986c22017-10-23 14:22:09 -070014
15#include "utils/common.h"
16#include "utils/base64.h"
17#include "utils/json.h"
18#include "common/ieee802_11_common.h"
19#include "common/ieee802_11_defs.h"
20#include "common/wpa_ctrl.h"
21#include "crypto/crypto.h"
22#include "crypto/random.h"
23#include "crypto/aes.h"
24#include "crypto/aes_siv.h"
25#include "crypto/sha384.h"
26#include "crypto/sha512.h"
Roshan Pius3a1667e2018-07-03 15:17:14 -070027#include "drivers/driver.h"
Dmitry Shmidtd2986c22017-10-23 14:22:09 -070028#include "dpp.h"
29
30
Roshan Pius3a1667e2018-07-03 15:17:14 -070031#ifdef CONFIG_TESTING_OPTIONS
32enum dpp_test_behavior dpp_test = DPP_TEST_DISABLED;
33u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
34u8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
35u8 dpp_pkex_ephemeral_key_override[600];
36size_t dpp_pkex_ephemeral_key_override_len = 0;
37u8 dpp_protocol_key_override[600];
38size_t dpp_protocol_key_override_len = 0;
39u8 dpp_nonce_override[DPP_MAX_NONCE_LEN];
40size_t dpp_nonce_override_len = 0;
41
42static int dpp_test_gen_invalid_key(struct wpabuf *msg,
43 const struct dpp_curve_params *curve);
44#endif /* CONFIG_TESTING_OPTIONS */
45
46#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
47 (defined(LIBRESSL_VERSION_NUMBER) && \
48 LIBRESSL_VERSION_NUMBER < 0x20700000L)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -070049/* Compatibility wrappers for older versions. */
50
51static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
52{
53 sig->r = r;
54 sig->s = s;
55 return 1;
56}
57
58
59static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr,
60 const BIGNUM **ps)
61{
62 if (pr)
63 *pr = sig->r;
64 if (ps)
65 *ps = sig->s;
66}
67
68#endif
69
70
71static const struct dpp_curve_params dpp_curves[] = {
72 /* The mandatory to support and the default NIST P-256 curve needs to
73 * be the first entry on this list. */
74 { "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" },
75 { "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" },
76 { "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" },
77 { "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" },
78 { "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" },
79 { "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" },
80 { NULL, 0, 0, 0, 0, NULL, 0, NULL }
81};
82
83
84/* Role-specific elements for PKEX */
85
86/* NIST P-256 */
87static const u8 pkex_init_x_p256[32] = {
88 0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b,
89 0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54,
90 0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07,
91 0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25
92 };
93static const u8 pkex_init_y_p256[32] = {
94 0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b,
95 0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc,
96 0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45,
97 0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4
98 };
99static const u8 pkex_resp_x_p256[32] = {
100 0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39,
101 0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f,
102 0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f,
103 0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76
104};
105static const u8 pkex_resp_y_p256[32] = {
Roshan Pius3a1667e2018-07-03 15:17:14 -0700106 0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19,
107 0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1,
108 0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a,
109 0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700110};
111
112/* NIST P-384 */
113static const u8 pkex_init_x_p384[48] = {
114 0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa,
115 0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68,
116 0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53,
117 0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac,
118 0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12,
119 0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3
120};
121static const u8 pkex_init_y_p384[48] = {
Roshan Pius3a1667e2018-07-03 15:17:14 -0700122 0x76, 0x2f, 0x68, 0x84, 0xa6, 0xb0, 0x59, 0x29,
123 0x83, 0xa2, 0x6c, 0xa4, 0x6c, 0x3b, 0xf8, 0x56,
124 0x76, 0x11, 0x2a, 0x32, 0x90, 0xbd, 0x07, 0xc7,
125 0x37, 0x39, 0x9d, 0xdb, 0x96, 0xf3, 0x2b, 0xb6,
126 0x27, 0xbb, 0x29, 0x3c, 0x17, 0x33, 0x9d, 0x94,
127 0xc3, 0xda, 0xac, 0x46, 0xb0, 0x8e, 0x07, 0x18
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700128};
129static const u8 pkex_resp_x_p384[48] = {
130 0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98,
131 0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97,
132 0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92,
133 0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44,
134 0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf,
135 0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf
136};
137static const u8 pkex_resp_y_p384[48] = {
Roshan Pius3a1667e2018-07-03 15:17:14 -0700138 0xab, 0xa7, 0xdf, 0x52, 0xaa, 0xe2, 0x35, 0x0c,
139 0xe3, 0x75, 0x32, 0xe6, 0xbf, 0x06, 0xc8, 0x7c,
140 0x38, 0x29, 0x4c, 0xec, 0x82, 0xac, 0xd7, 0xa3,
141 0x09, 0xd2, 0x0e, 0x22, 0x5a, 0x74, 0x52, 0xa1,
142 0x7e, 0x54, 0x4e, 0xfe, 0xc6, 0x29, 0x33, 0x63,
143 0x15, 0xe1, 0x7b, 0xe3, 0x40, 0x1c, 0xca, 0x06
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700144};
145
146/* NIST P-521 */
147static const u8 pkex_init_x_p521[66] = {
148 0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23,
149 0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0,
150 0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76,
151 0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5,
152 0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38,
153 0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01,
154 0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e,
155 0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d,
156 0x97, 0x76
157};
158static const u8 pkex_init_y_p521[66] = {
Roshan Pius3a1667e2018-07-03 15:17:14 -0700159 0x00, 0xb3, 0x8e, 0x02, 0xe4, 0x2a, 0x63, 0x59,
160 0x12, 0xc6, 0x10, 0xba, 0x3a, 0xf9, 0x02, 0x99,
161 0x3f, 0x14, 0xf0, 0x40, 0xde, 0x5c, 0xc9, 0x8b,
162 0x02, 0x55, 0xfa, 0x91, 0xb1, 0xcc, 0x6a, 0xbd,
163 0xe5, 0x62, 0xc0, 0xc5, 0xe3, 0xa1, 0x57, 0x9f,
164 0x08, 0x1a, 0xa6, 0xe2, 0xf8, 0x55, 0x90, 0xbf,
165 0xf5, 0xa6, 0xc3, 0xd8, 0x52, 0x1f, 0xb7, 0x02,
166 0x2e, 0x7c, 0xc8, 0xb3, 0x20, 0x1e, 0x79, 0x8d,
167 0x03, 0xa8
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700168};
169static const u8 pkex_resp_x_p521[66] = {
170 0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a,
171 0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44,
172 0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f,
173 0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb,
174 0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48,
175 0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e,
176 0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a,
177 0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97,
178 0x84, 0xb4
179};
180static const u8 pkex_resp_y_p521[66] = {
Roshan Pius3a1667e2018-07-03 15:17:14 -0700181 0x00, 0x46, 0x63, 0x39, 0xbe, 0xcd, 0xa4, 0x2d,
182 0xca, 0x27, 0x74, 0xd4, 0x1b, 0x91, 0x33, 0x20,
183 0x83, 0xc7, 0x3b, 0xa4, 0x09, 0x8b, 0x8e, 0xa3,
184 0x88, 0xe9, 0x75, 0x7f, 0x56, 0x7b, 0x38, 0x84,
185 0x62, 0x02, 0x7c, 0x90, 0x51, 0x07, 0xdb, 0xe9,
186 0xd0, 0xde, 0xda, 0x9a, 0x5d, 0xe5, 0x94, 0xd2,
187 0xcf, 0x9d, 0x4c, 0x33, 0x91, 0xa6, 0xc3, 0x80,
188 0xa7, 0x6e, 0x7e, 0x8d, 0xf8, 0x73, 0x6e, 0x53,
189 0xce, 0xe1
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700190};
191
192/* Brainpool P-256r1 */
193static const u8 pkex_init_x_bp_p256r1[32] = {
194 0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10,
195 0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca,
196 0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75,
197 0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8
198};
199static const u8 pkex_init_y_bp_p256r1[32] = {
Roshan Pius3a1667e2018-07-03 15:17:14 -0700200 0x93, 0xca, 0xef, 0xa9, 0x66, 0x3e, 0x87, 0xcd,
201 0x52, 0x6e, 0x54, 0x13, 0xef, 0x31, 0x67, 0x30,
202 0x15, 0x13, 0x9d, 0x6d, 0xc0, 0x95, 0x32, 0xbe,
203 0x4f, 0xab, 0x5d, 0xf7, 0xbf, 0x5e, 0xaa, 0x0b
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700204};
205static const u8 pkex_resp_x_bp_p256r1[32] = {
206 0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f,
207 0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a,
208 0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a,
209 0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3
210};
211static const u8 pkex_resp_y_bp_p256r1[32] = {
212 0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd,
213 0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2,
214 0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e,
215 0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64
216};
217
218/* Brainpool P-384r1 */
219static const u8 pkex_init_x_bp_p384r1[48] = {
220 0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd,
221 0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19,
222 0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06,
223 0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62,
224 0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30,
225 0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe
226};
227static const u8 pkex_init_y_bp_p384r1[48] = {
228 0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99,
229 0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86,
230 0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32,
231 0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9,
232 0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e,
233 0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52
234};
235static const u8 pkex_resp_x_bp_p384r1[48] = {
236 0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0,
237 0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25,
238 0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b,
239 0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71,
240 0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce,
241 0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c
242};
243static const u8 pkex_resp_y_bp_p384r1[48] = {
244 0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65,
245 0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04,
246 0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70,
247 0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c,
248 0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb,
249 0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1
250};
251
252/* Brainpool P-512r1 */
253static const u8 pkex_init_x_bp_p512r1[64] = {
254 0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c,
255 0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51,
256 0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc,
257 0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95,
258 0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d,
259 0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff,
260 0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc,
261 0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f
262};
263static const u8 pkex_init_y_bp_p512r1[64] = {
Roshan Pius3a1667e2018-07-03 15:17:14 -0700264 0x50, 0xb5, 0x9b, 0xfa, 0x45, 0x67, 0x75, 0x94,
265 0x44, 0xe7, 0x68, 0xb0, 0xeb, 0x3e, 0xb3, 0xb8,
266 0xf9, 0x99, 0x05, 0xef, 0xae, 0x6c, 0xbc, 0xe3,
267 0xe1, 0xd2, 0x51, 0x54, 0xdf, 0x59, 0xd4, 0x45,
268 0x41, 0x3a, 0xa8, 0x0b, 0x76, 0x32, 0x44, 0x0e,
269 0x07, 0x60, 0x3a, 0x6e, 0xbe, 0xfe, 0xe0, 0x58,
270 0x52, 0xa0, 0xaa, 0x8b, 0xd8, 0x5b, 0xf2, 0x71,
271 0x11, 0x9a, 0x9e, 0x8f, 0x1a, 0xd1, 0xc9, 0x99
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700272};
273static const u8 pkex_resp_x_bp_p512r1[64] = {
274 0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72,
275 0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76,
276 0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19,
277 0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e,
278 0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9,
279 0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88,
280 0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29,
281 0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e
282};
283static const u8 pkex_resp_y_bp_p512r1[64] = {
Roshan Pius3a1667e2018-07-03 15:17:14 -0700284 0x80, 0x1f, 0x43, 0xd2, 0x17, 0x35, 0xec, 0x81,
285 0xd9, 0x4b, 0xdc, 0x81, 0x19, 0xd9, 0x5f, 0x68,
286 0x16, 0x84, 0xfe, 0x63, 0x4b, 0x8d, 0x5d, 0xaa,
287 0x88, 0x4a, 0x47, 0x48, 0xd4, 0xea, 0xab, 0x7d,
288 0x6a, 0xbf, 0xe1, 0x28, 0x99, 0x6a, 0x87, 0x1c,
289 0x30, 0xb4, 0x44, 0x2d, 0x75, 0xac, 0x35, 0x09,
290 0x73, 0x24, 0x3d, 0xb4, 0x43, 0xb1, 0xc1, 0x56,
291 0x56, 0xad, 0x30, 0x87, 0xf4, 0xc3, 0x00, 0xc7
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700292};
293
294
Roshan Pius3a1667e2018-07-03 15:17:14 -0700295static void dpp_debug_print_point(const char *title, const EC_GROUP *group,
296 const EC_POINT *point)
297{
298 BIGNUM *x, *y;
299 BN_CTX *ctx;
300 char *x_str = NULL, *y_str = NULL;
301
302 if (!wpa_debug_show_keys)
303 return;
304
305 ctx = BN_CTX_new();
306 x = BN_new();
307 y = BN_new();
308 if (!ctx || !x || !y ||
309 EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1)
310 goto fail;
311
312 x_str = BN_bn2hex(x);
313 y_str = BN_bn2hex(y);
314 if (!x_str || !y_str)
315 goto fail;
316
317 wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str);
318
319fail:
320 OPENSSL_free(x_str);
321 OPENSSL_free(y_str);
322 BN_free(x);
323 BN_free(y);
324 BN_CTX_free(ctx);
325}
326
327
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700328static int dpp_hash_vector(const struct dpp_curve_params *curve,
329 size_t num_elem, const u8 *addr[], const size_t *len,
330 u8 *mac)
331{
332 if (curve->hash_len == 32)
333 return sha256_vector(num_elem, addr, len, mac);
334 if (curve->hash_len == 48)
335 return sha384_vector(num_elem, addr, len, mac);
336 if (curve->hash_len == 64)
337 return sha512_vector(num_elem, addr, len, mac);
338 return -1;
339}
340
341
342static int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
343 const char *label, u8 *out, size_t outlen)
344{
345 if (hash_len == 32)
346 return hmac_sha256_kdf(secret, secret_len, NULL,
347 (const u8 *) label, os_strlen(label),
348 out, outlen);
349 if (hash_len == 48)
350 return hmac_sha384_kdf(secret, secret_len, NULL,
351 (const u8 *) label, os_strlen(label),
352 out, outlen);
353 if (hash_len == 64)
354 return hmac_sha512_kdf(secret, secret_len, NULL,
355 (const u8 *) label, os_strlen(label),
356 out, outlen);
357 return -1;
358}
359
360
361static int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
362 size_t num_elem, const u8 *addr[],
363 const size_t *len, u8 *mac)
364{
365 if (hash_len == 32)
366 return hmac_sha256_vector(key, key_len, num_elem, addr, len,
367 mac);
368 if (hash_len == 48)
369 return hmac_sha384_vector(key, key_len, num_elem, addr, len,
370 mac);
371 if (hash_len == 64)
372 return hmac_sha512_vector(key, key_len, num_elem, addr, len,
373 mac);
374 return -1;
375}
376
377
378static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len,
379 const u8 *data, size_t data_len, u8 *mac)
380{
381 if (hash_len == 32)
382 return hmac_sha256(key, key_len, data, data_len, mac);
383 if (hash_len == 48)
384 return hmac_sha384(key, key_len, data, data_len, mac);
385 if (hash_len == 64)
386 return hmac_sha512(key, key_len, data, data_len, mac);
387 return -1;
388}
389
390
Roshan Pius3a1667e2018-07-03 15:17:14 -0700391static int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
392{
393 int num_bytes, offset;
394
395 num_bytes = BN_num_bytes(bn);
396 if ((size_t) num_bytes > len)
397 return -1;
398 offset = len - num_bytes;
399 os_memset(pos, 0, offset);
400 BN_bn2bin(bn, pos + offset);
401 return 0;
402}
403
404
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700405static struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix)
406{
407 int len, res;
408 EC_KEY *eckey;
409 struct wpabuf *buf;
410 unsigned char *pos;
411
412 eckey = EVP_PKEY_get1_EC_KEY(pkey);
413 if (!eckey)
414 return NULL;
415 EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
416 len = i2o_ECPublicKey(eckey, NULL);
417 if (len <= 0) {
418 wpa_printf(MSG_ERROR,
419 "DDP: Failed to determine public key encoding length");
420 EC_KEY_free(eckey);
421 return NULL;
422 }
423
424 buf = wpabuf_alloc(len);
425 if (!buf) {
426 EC_KEY_free(eckey);
427 return NULL;
428 }
429
430 pos = wpabuf_put(buf, len);
431 res = i2o_ECPublicKey(eckey, &pos);
432 EC_KEY_free(eckey);
433 if (res != len) {
434 wpa_printf(MSG_ERROR,
435 "DDP: Failed to encode public key (res=%d/%d)",
436 res, len);
437 wpabuf_free(buf);
438 return NULL;
439 }
440
441 if (!prefix) {
442 /* Remove 0x04 prefix to match DPP definition */
443 pos = wpabuf_mhead(buf);
444 os_memmove(pos, pos + 1, len - 1);
445 buf->used--;
446 }
447
448 return buf;
449}
450
451
452static EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
453 const u8 *buf_x, const u8 *buf_y,
454 size_t len)
455{
456 EC_KEY *eckey = NULL;
457 BN_CTX *ctx;
458 EC_POINT *point = NULL;
459 BIGNUM *x = NULL, *y = NULL;
460 EVP_PKEY *pkey = NULL;
461
462 ctx = BN_CTX_new();
463 if (!ctx) {
464 wpa_printf(MSG_ERROR, "DPP: Out of memory");
465 return NULL;
466 }
467
468 point = EC_POINT_new(group);
469 x = BN_bin2bn(buf_x, len, NULL);
470 y = BN_bin2bn(buf_y, len, NULL);
471 if (!point || !x || !y) {
472 wpa_printf(MSG_ERROR, "DPP: Out of memory");
473 goto fail;
474 }
475
476 if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) {
477 wpa_printf(MSG_ERROR,
478 "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
479 ERR_error_string(ERR_get_error(), NULL));
480 goto fail;
481 }
482
483 if (!EC_POINT_is_on_curve(group, point, ctx) ||
484 EC_POINT_is_at_infinity(group, point)) {
485 wpa_printf(MSG_ERROR, "DPP: Invalid point");
486 goto fail;
487 }
Roshan Pius3a1667e2018-07-03 15:17:14 -0700488 dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700489
490 eckey = EC_KEY_new();
491 if (!eckey ||
492 EC_KEY_set_group(eckey, group) != 1 ||
493 EC_KEY_set_public_key(eckey, point) != 1) {
494 wpa_printf(MSG_ERROR,
495 "DPP: Failed to set EC_KEY: %s",
496 ERR_error_string(ERR_get_error(), NULL));
497 goto fail;
498 }
499 EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
500
501 pkey = EVP_PKEY_new();
502 if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
503 wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
504 goto fail;
505 }
506
507out:
508 BN_free(x);
509 BN_free(y);
510 EC_KEY_free(eckey);
511 EC_POINT_free(point);
512 BN_CTX_free(ctx);
513 return pkey;
514fail:
515 EVP_PKEY_free(pkey);
516 pkey = NULL;
517 goto out;
518}
519
520
521static EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key,
522 const u8 *buf, size_t len)
523{
524 EC_KEY *eckey;
525 const EC_GROUP *group;
526 EVP_PKEY *pkey = NULL;
527
528 if (len & 1)
529 return NULL;
530
531 eckey = EVP_PKEY_get1_EC_KEY(group_key);
532 if (!eckey) {
533 wpa_printf(MSG_ERROR,
534 "DPP: Could not get EC_KEY from group_key");
535 return NULL;
536 }
537
538 group = EC_KEY_get0_group(eckey);
539 if (group)
540 pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2,
541 len / 2);
542 else
543 wpa_printf(MSG_ERROR, "DPP: Could not get EC group");
544
545 EC_KEY_free(eckey);
546 return pkey;
547}
548
549
Roshan Pius3a1667e2018-07-03 15:17:14 -0700550static void dpp_auth_fail(struct dpp_authentication *auth, const char *txt)
551{
552 wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
553}
554
555
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700556struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
557 size_t len)
558{
559 struct wpabuf *msg;
560
561 msg = wpabuf_alloc(8 + len);
562 if (!msg)
563 return NULL;
564 wpabuf_put_u8(msg, WLAN_ACTION_PUBLIC);
565 wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC);
566 wpabuf_put_be24(msg, OUI_WFA);
567 wpabuf_put_u8(msg, DPP_OUI_TYPE);
568 wpabuf_put_u8(msg, 1); /* Crypto Suite */
569 wpabuf_put_u8(msg, type);
570 return msg;
571}
572
573
574const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len)
575{
576 u16 id, alen;
577 const u8 *pos = buf, *end = buf + len;
578
579 while (end - pos >= 4) {
580 id = WPA_GET_LE16(pos);
581 pos += 2;
582 alen = WPA_GET_LE16(pos);
583 pos += 2;
584 if (alen > end - pos)
585 return NULL;
586 if (id == req_id) {
587 *ret_len = alen;
588 return pos;
589 }
590 pos += alen;
591 }
592
593 return NULL;
594}
595
596
597int dpp_check_attrs(const u8 *buf, size_t len)
598{
599 const u8 *pos, *end;
Roshan Pius3a1667e2018-07-03 15:17:14 -0700600 int wrapped_data = 0;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700601
602 pos = buf;
603 end = buf + len;
604 while (end - pos >= 4) {
605 u16 id, alen;
606
607 id = WPA_GET_LE16(pos);
608 pos += 2;
609 alen = WPA_GET_LE16(pos);
610 pos += 2;
611 wpa_printf(MSG_MSGDUMP, "DPP: Attribute ID %04x len %u",
612 id, alen);
613 if (alen > end - pos) {
614 wpa_printf(MSG_DEBUG,
615 "DPP: Truncated message - not enough room for the attribute - dropped");
616 return -1;
617 }
Roshan Pius3a1667e2018-07-03 15:17:14 -0700618 if (wrapped_data) {
619 wpa_printf(MSG_DEBUG,
620 "DPP: An unexpected attribute included after the Wrapped Data attribute");
621 return -1;
622 }
623 if (id == DPP_ATTR_WRAPPED_DATA)
624 wrapped_data = 1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700625 pos += alen;
626 }
627
628 if (end != pos) {
629 wpa_printf(MSG_DEBUG,
630 "DPP: Unexpected octets (%d) after the last attribute",
631 (int) (end - pos));
632 return -1;
633 }
634
635 return 0;
636}
637
638
639void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info)
640{
641 if (!info)
642 return;
643 os_free(info->uri);
644 os_free(info->info);
645 EVP_PKEY_free(info->pubkey);
646 os_free(info);
647}
648
649
650const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type)
651{
652 switch (type) {
653 case DPP_BOOTSTRAP_QR_CODE:
654 return "QRCODE";
655 case DPP_BOOTSTRAP_PKEX:
656 return "PKEX";
657 }
658 return "??";
659}
660
661
662static int dpp_uri_valid_info(const char *info)
663{
664 while (*info) {
665 unsigned char val = *info++;
666
667 if (val < 0x20 || val > 0x7e || val == 0x3b)
668 return 0;
669 }
670
671 return 1;
672}
673
674
675static int dpp_clone_uri(struct dpp_bootstrap_info *bi, const char *uri)
676{
677 bi->uri = os_strdup(uri);
678 return bi->uri ? 0 : -1;
679}
680
681
682int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi,
683 const char *chan_list)
684{
685 const char *pos = chan_list;
686 int opclass, channel, freq;
687
688 while (pos && *pos && *pos != ';') {
689 opclass = atoi(pos);
690 if (opclass <= 0)
691 goto fail;
692 pos = os_strchr(pos, '/');
693 if (!pos)
694 goto fail;
695 pos++;
696 channel = atoi(pos);
697 if (channel <= 0)
698 goto fail;
699 while (*pos >= '0' && *pos <= '9')
700 pos++;
701 freq = ieee80211_chan_to_freq(NULL, opclass, channel);
702 wpa_printf(MSG_DEBUG,
703 "DPP: URI channel-list: opclass=%d channel=%d ==> freq=%d",
704 opclass, channel, freq);
705 if (freq < 0) {
706 wpa_printf(MSG_DEBUG,
707 "DPP: Ignore unknown URI channel-list channel (opclass=%d channel=%d)",
708 opclass, channel);
709 } else if (bi->num_freq == DPP_BOOTSTRAP_MAX_FREQ) {
710 wpa_printf(MSG_DEBUG,
711 "DPP: Too many channels in URI channel-list - ignore list");
712 bi->num_freq = 0;
713 break;
714 } else {
715 bi->freq[bi->num_freq++] = freq;
716 }
717
718 if (*pos == ';' || *pos == '\0')
719 break;
720 if (*pos != ',')
721 goto fail;
722 pos++;
723 }
724
725 return 0;
726fail:
727 wpa_printf(MSG_DEBUG, "DPP: Invalid URI channel-list");
728 return -1;
729}
730
731
732int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac)
733{
734 if (!mac)
735 return 0;
736
737 if (hwaddr_aton2(mac, bi->mac_addr) < 0) {
738 wpa_printf(MSG_DEBUG, "DPP: Invalid URI mac");
739 return -1;
740 }
741
742 wpa_printf(MSG_DEBUG, "DPP: URI mac: " MACSTR, MAC2STR(bi->mac_addr));
743
744 return 0;
745}
746
747
748int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info)
749{
750 const char *end;
751
752 if (!info)
753 return 0;
754
755 end = os_strchr(info, ';');
756 if (!end)
757 end = info + os_strlen(info);
758 bi->info = os_malloc(end - info + 1);
759 if (!bi->info)
760 return -1;
761 os_memcpy(bi->info, info, end - info);
762 bi->info[end - info] = '\0';
763 wpa_printf(MSG_DEBUG, "DPP: URI(information): %s", bi->info);
764 if (!dpp_uri_valid_info(bi->info)) {
765 wpa_printf(MSG_DEBUG, "DPP: Invalid URI information payload");
766 return -1;
767 }
768
769 return 0;
770}
771
772
773static const struct dpp_curve_params *
774dpp_get_curve_oid(const ASN1_OBJECT *poid)
775{
776 ASN1_OBJECT *oid;
777 int i;
778
779 for (i = 0; dpp_curves[i].name; i++) {
780 oid = OBJ_txt2obj(dpp_curves[i].name, 0);
781 if (oid && OBJ_cmp(poid, oid) == 0)
782 return &dpp_curves[i];
783 }
784 return NULL;
785}
786
787
788static const struct dpp_curve_params * dpp_get_curve_nid(int nid)
789{
790 int i, tmp;
791
792 if (!nid)
793 return NULL;
794 for (i = 0; dpp_curves[i].name; i++) {
795 tmp = OBJ_txt2nid(dpp_curves[i].name);
796 if (tmp == nid)
797 return &dpp_curves[i];
798 }
799 return NULL;
800}
801
802
803static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info)
804{
805 const char *end;
806 u8 *data;
807 size_t data_len;
808 EVP_PKEY *pkey;
809 const unsigned char *p;
810 int res;
811 X509_PUBKEY *pub = NULL;
812 ASN1_OBJECT *ppkalg;
813 const unsigned char *pk;
814 int ppklen;
815 X509_ALGOR *pa;
Roshan Pius3a1667e2018-07-03 15:17:14 -0700816#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700817 ASN1_OBJECT *pa_oid;
818#else
819 const ASN1_OBJECT *pa_oid;
820#endif
821 const void *pval;
822 int ptype;
823 const ASN1_OBJECT *poid;
824 char buf[100];
825
826 end = os_strchr(info, ';');
827 if (!end)
828 return -1;
829
830 data = base64_decode((const unsigned char *) info, end - info,
831 &data_len);
832 if (!data) {
833 wpa_printf(MSG_DEBUG,
834 "DPP: Invalid base64 encoding on URI public-key");
835 return -1;
836 }
837 wpa_hexdump(MSG_DEBUG, "DPP: Base64 decoded URI public-key",
838 data, data_len);
839
840 if (sha256_vector(1, (const u8 **) &data, &data_len,
841 bi->pubkey_hash) < 0) {
842 wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
843 return -1;
844 }
845 wpa_hexdump(MSG_DEBUG, "DPP: Public key hash",
846 bi->pubkey_hash, SHA256_MAC_LEN);
847
848 /* DER encoded ASN.1 SubjectPublicKeyInfo
849 *
850 * SubjectPublicKeyInfo ::= SEQUENCE {
851 * algorithm AlgorithmIdentifier,
852 * subjectPublicKey BIT STRING }
853 *
854 * AlgorithmIdentifier ::= SEQUENCE {
855 * algorithm OBJECT IDENTIFIER,
856 * parameters ANY DEFINED BY algorithm OPTIONAL }
857 *
858 * subjectPublicKey = compressed format public key per ANSI X9.63
859 * algorithm = ecPublicKey (1.2.840.10045.2.1)
860 * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g.,
861 * prime256v1 (1.2.840.10045.3.1.7)
862 */
863
864 p = data;
865 pkey = d2i_PUBKEY(NULL, &p, data_len);
866 os_free(data);
867
868 if (!pkey) {
869 wpa_printf(MSG_DEBUG,
870 "DPP: Could not parse URI public-key SubjectPublicKeyInfo");
871 return -1;
872 }
873
874 if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
875 wpa_printf(MSG_DEBUG,
876 "DPP: SubjectPublicKeyInfo does not describe an EC key");
877 EVP_PKEY_free(pkey);
878 return -1;
879 }
880
881 res = X509_PUBKEY_set(&pub, pkey);
882 if (res != 1) {
883 wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey");
884 goto fail;
885 }
886
887 res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub);
888 if (res != 1) {
889 wpa_printf(MSG_DEBUG,
890 "DPP: Could not extract SubjectPublicKeyInfo parameters");
891 goto fail;
892 }
893 res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0);
894 if (res < 0 || (size_t) res >= sizeof(buf)) {
895 wpa_printf(MSG_DEBUG,
896 "DPP: Could not extract SubjectPublicKeyInfo algorithm");
897 goto fail;
898 }
899 wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf);
900 if (os_strcmp(buf, "id-ecPublicKey") != 0) {
901 wpa_printf(MSG_DEBUG,
902 "DPP: Unsupported SubjectPublicKeyInfo algorithm");
903 goto fail;
904 }
905
906 X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa);
907 if (ptype != V_ASN1_OBJECT) {
908 wpa_printf(MSG_DEBUG,
909 "DPP: SubjectPublicKeyInfo parameters did not contain an OID");
910 goto fail;
911 }
912 poid = pval;
913 res = OBJ_obj2txt(buf, sizeof(buf), poid, 0);
914 if (res < 0 || (size_t) res >= sizeof(buf)) {
915 wpa_printf(MSG_DEBUG,
916 "DPP: Could not extract SubjectPublicKeyInfo parameters OID");
917 goto fail;
918 }
919 wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf);
920 bi->curve = dpp_get_curve_oid(poid);
921 if (!bi->curve) {
922 wpa_printf(MSG_DEBUG,
923 "DPP: Unsupported SubjectPublicKeyInfo curve: %s",
924 buf);
925 goto fail;
926 }
927
928 wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen);
929
930 X509_PUBKEY_free(pub);
931 bi->pubkey = pkey;
932 return 0;
933fail:
934 X509_PUBKEY_free(pub);
935 EVP_PKEY_free(pkey);
936 return -1;
937}
938
939
940static struct dpp_bootstrap_info * dpp_parse_uri(const char *uri)
941{
942 const char *pos = uri;
943 const char *end;
944 const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL;
945 struct dpp_bootstrap_info *bi;
946
947 wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri));
948
949 if (os_strncmp(pos, "DPP:", 4) != 0) {
950 wpa_printf(MSG_INFO, "DPP: Not a DPP URI");
951 return NULL;
952 }
953 pos += 4;
954
955 for (;;) {
956 end = os_strchr(pos, ';');
957 if (!end)
958 break;
959
960 if (end == pos) {
961 /* Handle terminating ";;" and ignore unexpected ";"
962 * for parsing robustness. */
963 pos++;
964 continue;
965 }
966
967 if (pos[0] == 'C' && pos[1] == ':' && !chan_list)
968 chan_list = pos + 2;
969 else if (pos[0] == 'M' && pos[1] == ':' && !mac)
970 mac = pos + 2;
971 else if (pos[0] == 'I' && pos[1] == ':' && !info)
972 info = pos + 2;
973 else if (pos[0] == 'K' && pos[1] == ':' && !pk)
974 pk = pos + 2;
975 else
976 wpa_hexdump_ascii(MSG_DEBUG,
977 "DPP: Ignore unrecognized URI parameter",
978 pos, end - pos);
979 pos = end + 1;
980 }
981
982 if (!pk) {
983 wpa_printf(MSG_INFO, "DPP: URI missing public-key");
984 return NULL;
985 }
986
987 bi = os_zalloc(sizeof(*bi));
988 if (!bi)
989 return NULL;
990
991 if (dpp_clone_uri(bi, uri) < 0 ||
992 dpp_parse_uri_chan_list(bi, chan_list) < 0 ||
993 dpp_parse_uri_mac(bi, mac) < 0 ||
994 dpp_parse_uri_info(bi, info) < 0 ||
995 dpp_parse_uri_pk(bi, pk) < 0) {
996 dpp_bootstrap_info_free(bi);
997 bi = NULL;
998 }
999
1000 return bi;
1001}
1002
1003
1004struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri)
1005{
1006 struct dpp_bootstrap_info *bi;
1007
1008 bi = dpp_parse_uri(uri);
1009 if (bi)
1010 bi->type = DPP_BOOTSTRAP_QR_CODE;
1011 return bi;
1012}
1013
1014
1015static void dpp_debug_print_key(const char *title, EVP_PKEY *key)
1016{
1017 EC_KEY *eckey;
1018 BIO *out;
1019 size_t rlen;
1020 char *txt;
1021 int res;
1022 unsigned char *der = NULL;
1023 int der_len;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001024 const EC_GROUP *group;
1025 const EC_POINT *point;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001026
1027 out = BIO_new(BIO_s_mem());
1028 if (!out)
1029 return;
1030
1031 EVP_PKEY_print_private(out, key, 0, NULL);
1032 rlen = BIO_ctrl_pending(out);
1033 txt = os_malloc(rlen + 1);
1034 if (txt) {
1035 res = BIO_read(out, txt, rlen);
1036 if (res > 0) {
1037 txt[res] = '\0';
1038 wpa_printf(MSG_DEBUG, "%s: %s", title, txt);
1039 }
1040 os_free(txt);
1041 }
1042 BIO_free(out);
1043
1044 eckey = EVP_PKEY_get1_EC_KEY(key);
1045 if (!eckey)
1046 return;
1047
Roshan Pius3a1667e2018-07-03 15:17:14 -07001048 group = EC_KEY_get0_group(eckey);
1049 point = EC_KEY_get0_public_key(eckey);
1050 if (group && point)
1051 dpp_debug_print_point(title, group, point);
1052
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001053 der_len = i2d_ECPrivateKey(eckey, &der);
1054 if (der_len > 0)
1055 wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len);
1056 OPENSSL_free(der);
1057 if (der_len <= 0) {
1058 der = NULL;
1059 der_len = i2d_EC_PUBKEY(eckey, &der);
1060 if (der_len > 0)
1061 wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len);
1062 OPENSSL_free(der);
1063 }
1064
1065 EC_KEY_free(eckey);
1066}
1067
1068
1069static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
1070{
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001071 EVP_PKEY_CTX *kctx = NULL;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001072 EC_KEY *ec_params;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001073 EVP_PKEY *params = NULL, *key = NULL;
1074 int nid;
1075
1076 wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
1077
1078 nid = OBJ_txt2nid(curve->name);
1079 if (nid == NID_undef) {
1080 wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name);
1081 return NULL;
1082 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07001083
1084 ec_params = EC_KEY_new_by_curve_name(nid);
1085 if (!ec_params) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001086 wpa_printf(MSG_ERROR,
1087 "DPP: Failed to generate EC_KEY parameters");
1088 goto fail;
1089 }
1090 EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
1091 params = EVP_PKEY_new();
1092 if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
1093 wpa_printf(MSG_ERROR,
1094 "DPP: Failed to generate EVP_PKEY parameters");
1095 goto fail;
1096 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001097
1098 kctx = EVP_PKEY_CTX_new(params, NULL);
1099 if (!kctx ||
1100 EVP_PKEY_keygen_init(kctx) != 1 ||
1101 EVP_PKEY_keygen(kctx, &key) != 1) {
1102 wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key");
1103 goto fail;
1104 }
1105
1106 if (wpa_debug_show_keys)
1107 dpp_debug_print_key("Own generated key", key);
1108
1109 EVP_PKEY_free(params);
1110 EVP_PKEY_CTX_free(kctx);
1111 return key;
1112fail:
1113 EVP_PKEY_CTX_free(kctx);
1114 EVP_PKEY_free(params);
1115 return NULL;
1116}
1117
1118
1119static const struct dpp_curve_params *
1120dpp_get_curve_name(const char *name)
1121{
1122 int i;
1123
1124 for (i = 0; dpp_curves[i].name; i++) {
1125 if (os_strcmp(name, dpp_curves[i].name) == 0 ||
1126 (dpp_curves[i].jwk_crv &&
1127 os_strcmp(name, dpp_curves[i].jwk_crv) == 0))
1128 return &dpp_curves[i];
1129 }
1130 return NULL;
1131}
1132
1133
1134static const struct dpp_curve_params *
1135dpp_get_curve_jwk_crv(const char *name)
1136{
1137 int i;
1138
1139 for (i = 0; dpp_curves[i].name; i++) {
1140 if (dpp_curves[i].jwk_crv &&
1141 os_strcmp(name, dpp_curves[i].jwk_crv) == 0)
1142 return &dpp_curves[i];
1143 }
1144 return NULL;
1145}
1146
1147
1148static EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
1149 const u8 *privkey, size_t privkey_len)
1150{
1151 EVP_PKEY *pkey;
1152 EC_KEY *eckey;
1153 const EC_GROUP *group;
1154 int nid;
1155
1156 pkey = EVP_PKEY_new();
1157 if (!pkey)
1158 return NULL;
1159 eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len);
1160 if (!eckey) {
1161 wpa_printf(MSG_INFO,
1162 "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
1163 ERR_error_string(ERR_get_error(), NULL));
1164 EVP_PKEY_free(pkey);
1165 return NULL;
1166 }
1167 group = EC_KEY_get0_group(eckey);
1168 if (!group) {
1169 EC_KEY_free(eckey);
1170 EVP_PKEY_free(pkey);
1171 return NULL;
1172 }
1173 nid = EC_GROUP_get_curve_name(group);
1174 *curve = dpp_get_curve_nid(nid);
1175 if (!*curve) {
1176 wpa_printf(MSG_INFO,
1177 "DPP: Unsupported curve (nid=%d) in pre-assigned key",
1178 nid);
1179 EC_KEY_free(eckey);
1180 EVP_PKEY_free(pkey);
1181 return NULL;
1182 }
1183
1184 if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
1185 EC_KEY_free(eckey);
1186 EVP_PKEY_free(pkey);
1187 return NULL;
1188 }
1189 return pkey;
1190}
1191
1192
Roshan Pius3a1667e2018-07-03 15:17:14 -07001193typedef struct {
1194 /* AlgorithmIdentifier ecPublicKey with optional parameters present
1195 * as an OID identifying the curve */
1196 X509_ALGOR *alg;
1197 /* Compressed format public key per ANSI X9.63 */
1198 ASN1_BIT_STRING *pub_key;
1199} DPP_BOOTSTRAPPING_KEY;
1200
1201ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = {
1202 ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR),
1203 ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING)
1204} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY);
1205
1206IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY);
1207
1208
1209static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001210{
1211 unsigned char *der = NULL;
1212 int der_len;
1213 EC_KEY *eckey;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001214 struct wpabuf *ret = NULL;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001215 size_t len;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001216 const EC_GROUP *group;
1217 const EC_POINT *point;
1218 BN_CTX *ctx;
1219 DPP_BOOTSTRAPPING_KEY *bootstrap = NULL;
1220 int nid;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001221
Roshan Pius3a1667e2018-07-03 15:17:14 -07001222 ctx = BN_CTX_new();
1223 eckey = EVP_PKEY_get1_EC_KEY(key);
1224 if (!ctx || !eckey)
1225 goto fail;
1226
1227 group = EC_KEY_get0_group(eckey);
1228 point = EC_KEY_get0_public_key(eckey);
1229 if (!group || !point)
1230 goto fail;
1231 dpp_debug_print_point("DPP: bootstrap public key", group, point);
1232 nid = EC_GROUP_get_curve_name(group);
1233
1234 bootstrap = DPP_BOOTSTRAPPING_KEY_new();
1235 if (!bootstrap ||
1236 X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC),
1237 V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1)
1238 goto fail;
1239
1240 len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
1241 NULL, 0, ctx);
1242 if (len == 0)
1243 goto fail;
1244
1245 der = OPENSSL_malloc(len);
1246 if (!der)
1247 goto fail;
1248 len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
1249 der, len, ctx);
1250
1251 OPENSSL_free(bootstrap->pub_key->data);
1252 bootstrap->pub_key->data = der;
1253 der = NULL;
1254 bootstrap->pub_key->length = len;
1255 /* No unused bits */
1256 bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
1257 bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
1258
1259 der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001260 if (der_len <= 0) {
1261 wpa_printf(MSG_ERROR,
1262 "DDP: Failed to build DER encoded public key");
Roshan Pius3a1667e2018-07-03 15:17:14 -07001263 goto fail;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001264 }
1265
Roshan Pius3a1667e2018-07-03 15:17:14 -07001266 ret = wpabuf_alloc_copy(der, der_len);
1267fail:
1268 DPP_BOOTSTRAPPING_KEY_free(bootstrap);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001269 OPENSSL_free(der);
Roshan Pius3a1667e2018-07-03 15:17:14 -07001270 EC_KEY_free(eckey);
1271 BN_CTX_free(ctx);
1272 return ret;
1273}
1274
1275
1276int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
1277{
1278 struct wpabuf *der;
1279 int res;
1280 const u8 *addr[1];
1281 size_t len[1];
1282
1283 der = dpp_bootstrap_key_der(bi->pubkey);
1284 if (!der)
1285 return -1;
1286 wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
1287 der);
1288
1289 addr[0] = wpabuf_head(der);
1290 len[0] = wpabuf_len(der);
1291 res = sha256_vector(1, addr, len, bi->pubkey_hash);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001292 if (res < 0)
1293 wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
Roshan Pius3a1667e2018-07-03 15:17:14 -07001294 else
1295 wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash,
1296 SHA256_MAC_LEN);
1297 wpabuf_free(der);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001298 return res;
1299}
1300
1301
1302char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
1303 const u8 *privkey, size_t privkey_len)
1304{
1305 unsigned char *base64 = NULL;
1306 char *pos, *end;
1307 size_t len;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001308 struct wpabuf *der = NULL;
1309 const u8 *addr[1];
1310 int res;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001311
1312 if (!curve) {
1313 bi->curve = &dpp_curves[0];
1314 } else {
1315 bi->curve = dpp_get_curve_name(curve);
1316 if (!bi->curve) {
1317 wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
1318 curve);
1319 return NULL;
1320 }
1321 }
1322 if (privkey)
1323 bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len);
1324 else
1325 bi->pubkey = dpp_gen_keypair(bi->curve);
1326 if (!bi->pubkey)
1327 goto fail;
1328 bi->own = 1;
1329
Roshan Pius3a1667e2018-07-03 15:17:14 -07001330 der = dpp_bootstrap_key_der(bi->pubkey);
1331 if (!der)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001332 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001333 wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
1334 der);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001335
Roshan Pius3a1667e2018-07-03 15:17:14 -07001336 addr[0] = wpabuf_head(der);
1337 len = wpabuf_len(der);
1338 res = sha256_vector(1, addr, &len, bi->pubkey_hash);
1339 if (res < 0) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001340 wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
1341 goto fail;
1342 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07001343 wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash,
1344 SHA256_MAC_LEN);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001345
Roshan Pius3a1667e2018-07-03 15:17:14 -07001346 base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len);
1347 wpabuf_free(der);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001348 der = NULL;
1349 if (!base64)
1350 goto fail;
1351 pos = (char *) base64;
1352 end = pos + len;
1353 for (;;) {
1354 pos = os_strchr(pos, '\n');
1355 if (!pos)
1356 break;
1357 os_memmove(pos, pos + 1, end - pos);
1358 }
1359 return (char *) base64;
1360fail:
1361 os_free(base64);
Roshan Pius3a1667e2018-07-03 15:17:14 -07001362 wpabuf_free(der);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001363 return NULL;
1364}
1365
1366
1367static int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1,
1368 unsigned int hash_len)
1369{
1370 u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
1371 const char *info = "first intermediate key";
1372 int res;
1373
1374 /* k1 = HKDF(<>, "first intermediate key", M.x) */
1375
1376 /* HKDF-Extract(<>, M.x) */
1377 os_memset(salt, 0, hash_len);
1378 if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0)
1379 return -1;
1380 wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)",
1381 prk, hash_len);
1382
1383 /* HKDF-Expand(PRK, info, L) */
1384 res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len);
1385 os_memset(prk, 0, hash_len);
1386 if (res < 0)
1387 return -1;
1388
1389 wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)",
1390 k1, hash_len);
1391 return 0;
1392}
1393
1394
1395static int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2,
1396 unsigned int hash_len)
1397{
1398 u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
1399 const char *info = "second intermediate key";
1400 int res;
1401
1402 /* k2 = HKDF(<>, "second intermediate key", N.x) */
1403
1404 /* HKDF-Extract(<>, N.x) */
1405 os_memset(salt, 0, hash_len);
1406 res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk);
1407 if (res < 0)
1408 return -1;
1409 wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
1410 prk, hash_len);
1411
1412 /* HKDF-Expand(PRK, info, L) */
1413 res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len);
1414 os_memset(prk, 0, hash_len);
1415 if (res < 0)
1416 return -1;
1417
1418 wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)",
1419 k2, hash_len);
1420 return 0;
1421}
1422
1423
1424static int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke,
1425 unsigned int hash_len)
1426{
1427 size_t nonce_len;
1428 u8 nonces[2 * DPP_MAX_NONCE_LEN];
1429 const char *info_ke = "DPP Key";
1430 u8 prk[DPP_MAX_HASH_LEN];
1431 int res;
1432 const u8 *addr[3];
1433 size_t len[3];
1434 size_t num_elem = 0;
1435
Roshan Pius3a1667e2018-07-03 15:17:14 -07001436 if (!auth->Mx_len || !auth->Nx_len) {
1437 wpa_printf(MSG_DEBUG,
1438 "DPP: Mx/Nx not available - cannot derive ke");
1439 return -1;
1440 }
1441
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001442 /* ke = HKDF(I-nonce | R-nonce, "DPP Key", M.x | N.x [| L.x]) */
1443
1444 /* HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */
1445 nonce_len = auth->curve->nonce_len;
1446 os_memcpy(nonces, auth->i_nonce, nonce_len);
1447 os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len);
1448 addr[num_elem] = auth->Mx;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001449 len[num_elem] = auth->Mx_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001450 num_elem++;
1451 addr[num_elem] = auth->Nx;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001452 len[num_elem] = auth->Nx_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001453 num_elem++;
1454 if (auth->peer_bi && auth->own_bi) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07001455 if (!auth->Lx_len) {
1456 wpa_printf(MSG_DEBUG,
1457 "DPP: Lx not available - cannot derive ke");
1458 return -1;
1459 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001460 addr[num_elem] = auth->Lx;
1461 len[num_elem] = auth->secret_len;
1462 num_elem++;
1463 }
1464 res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len,
1465 num_elem, addr, len, prk);
1466 if (res < 0)
1467 return -1;
1468 wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
1469 prk, hash_len);
1470
1471 /* HKDF-Expand(PRK, info, L) */
1472 res = dpp_hkdf_expand(hash_len, prk, hash_len, info_ke, ke, hash_len);
1473 os_memset(prk, 0, hash_len);
1474 if (res < 0)
1475 return -1;
1476
1477 wpa_hexdump_key(MSG_DEBUG, "DPP: ke = HKDF-Expand(PRK, info, L)",
1478 ke, hash_len);
1479 return 0;
1480}
1481
1482
Roshan Pius3a1667e2018-07-03 15:17:14 -07001483static void dpp_build_attr_status(struct wpabuf *msg,
1484 enum dpp_status_error status)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001485{
Roshan Pius3a1667e2018-07-03 15:17:14 -07001486 wpa_printf(MSG_DEBUG, "DPP: Status %d", status);
1487 wpabuf_put_le16(msg, DPP_ATTR_STATUS);
1488 wpabuf_put_le16(msg, 1);
1489 wpabuf_put_u8(msg, status);
1490}
1491
1492
1493static void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg,
1494 const u8 *hash)
1495{
1496 if (hash) {
1497 wpa_printf(MSG_DEBUG, "DPP: R-Bootstrap Key Hash");
1498 wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
1499 wpabuf_put_le16(msg, SHA256_MAC_LEN);
1500 wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
1501 }
1502}
1503
1504
1505static void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg,
1506 const u8 *hash)
1507{
1508 if (hash) {
1509 wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash");
1510 wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
1511 wpabuf_put_le16(msg, SHA256_MAC_LEN);
1512 wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
1513 }
1514}
1515
1516
1517static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
1518 const struct wpabuf *pi,
1519 size_t nonce_len,
1520 const u8 *r_pubkey_hash,
1521 const u8 *i_pubkey_hash,
1522 unsigned int neg_freq)
1523{
1524 struct wpabuf *msg;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001525 u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1];
1526 u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE];
1527 u8 *pos;
1528 const u8 *addr[2];
1529 size_t len[2], siv_len, attr_len;
1530 u8 *attr_start, *attr_end;
1531
Roshan Pius3a1667e2018-07-03 15:17:14 -07001532 /* Build DPP Authentication Request frame attributes */
1533 attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) +
1534 4 + sizeof(wrapped_data);
1535 if (neg_freq > 0)
1536 attr_len += 4 + 2;
1537#ifdef CONFIG_TESTING_OPTIONS
1538 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ)
1539 attr_len += 5;
1540#endif /* CONFIG_TESTING_OPTIONS */
1541 msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len);
1542 if (!msg)
1543 return NULL;
1544
1545 attr_start = wpabuf_put(msg, 0);
1546
1547 /* Responder Bootstrapping Key Hash */
1548 dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
1549
1550 /* Initiator Bootstrapping Key Hash */
1551 dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
1552
1553 /* Initiator Protocol Key */
1554 if (pi) {
1555 wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
1556 wpabuf_put_le16(msg, wpabuf_len(pi));
1557 wpabuf_put_buf(msg, pi);
1558 }
1559
1560 /* Channel */
1561 if (neg_freq > 0) {
1562 u8 op_class, channel;
1563
1564 if (ieee80211_freq_to_channel_ext(neg_freq, 0, 0, &op_class,
1565 &channel) ==
1566 NUM_HOSTAPD_MODES) {
1567 wpa_printf(MSG_INFO,
1568 "DPP: Unsupported negotiation frequency request: %d",
1569 neg_freq);
1570 wpabuf_free(msg);
1571 return NULL;
1572 }
1573 wpabuf_put_le16(msg, DPP_ATTR_CHANNEL);
1574 wpabuf_put_le16(msg, 2);
1575 wpabuf_put_u8(msg, op_class);
1576 wpabuf_put_u8(msg, channel);
1577 }
1578
1579#ifdef CONFIG_TESTING_OPTIONS
1580 if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) {
1581 wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
1582 goto skip_wrapped_data;
1583 }
1584#endif /* CONFIG_TESTING_OPTIONS */
1585
1586 /* Wrapped data ({I-nonce, I-capabilities}k1) */
1587 pos = clear;
1588
1589#ifdef CONFIG_TESTING_OPTIONS
1590 if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_REQ) {
1591 wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
1592 goto skip_i_nonce;
1593 }
1594 if (dpp_test == DPP_TEST_INVALID_I_NONCE_AUTH_REQ) {
1595 wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-nonce");
1596 WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
1597 pos += 2;
1598 WPA_PUT_LE16(pos, nonce_len - 1);
1599 pos += 2;
1600 os_memcpy(pos, auth->i_nonce, nonce_len - 1);
1601 pos += nonce_len - 1;
1602 goto skip_i_nonce;
1603 }
1604#endif /* CONFIG_TESTING_OPTIONS */
1605
1606 /* I-nonce */
1607 WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
1608 pos += 2;
1609 WPA_PUT_LE16(pos, nonce_len);
1610 pos += 2;
1611 os_memcpy(pos, auth->i_nonce, nonce_len);
1612 pos += nonce_len;
1613
1614#ifdef CONFIG_TESTING_OPTIONS
1615skip_i_nonce:
1616 if (dpp_test == DPP_TEST_NO_I_CAPAB_AUTH_REQ) {
1617 wpa_printf(MSG_INFO, "DPP: TESTING - no I-capab");
1618 goto skip_i_capab;
1619 }
1620#endif /* CONFIG_TESTING_OPTIONS */
1621
1622 /* I-capabilities */
1623 WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES);
1624 pos += 2;
1625 WPA_PUT_LE16(pos, 1);
1626 pos += 2;
1627 auth->i_capab = auth->allowed_roles;
1628 *pos++ = auth->i_capab;
1629#ifdef CONFIG_TESTING_OPTIONS
1630 if (dpp_test == DPP_TEST_ZERO_I_CAPAB) {
1631 wpa_printf(MSG_INFO, "DPP: TESTING - zero I-capabilities");
1632 pos[-1] = 0;
1633 }
1634skip_i_capab:
1635#endif /* CONFIG_TESTING_OPTIONS */
1636
1637 attr_end = wpabuf_put(msg, 0);
1638
1639 /* OUI, OUI type, Crypto Suite, DPP frame type */
1640 addr[0] = wpabuf_head_u8(msg) + 2;
1641 len[0] = 3 + 1 + 1 + 1;
1642 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
1643
1644 /* Attributes before Wrapped Data */
1645 addr[1] = attr_start;
1646 len[1] = attr_end - attr_start;
1647 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
1648
1649 siv_len = pos - clear;
1650 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
1651 if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
1652 2, addr, len, wrapped_data) < 0) {
1653 wpabuf_free(msg);
1654 return NULL;
1655 }
1656 siv_len += AES_BLOCK_SIZE;
1657 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
1658 wrapped_data, siv_len);
1659
1660 wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
1661 wpabuf_put_le16(msg, siv_len);
1662 wpabuf_put_data(msg, wrapped_data, siv_len);
1663
1664#ifdef CONFIG_TESTING_OPTIONS
1665 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) {
1666 wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
1667 dpp_build_attr_status(msg, DPP_STATUS_OK);
1668 }
1669skip_wrapped_data:
1670#endif /* CONFIG_TESTING_OPTIONS */
1671
1672 wpa_hexdump_buf(MSG_DEBUG,
1673 "DPP: Authentication Request frame attributes", msg);
1674
1675 return msg;
1676}
1677
1678
1679static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
1680 enum dpp_status_error status,
1681 const struct wpabuf *pr,
1682 size_t nonce_len,
1683 const u8 *r_pubkey_hash,
1684 const u8 *i_pubkey_hash,
1685 const u8 *r_nonce, const u8 *i_nonce,
1686 const u8 *wrapped_r_auth,
1687 size_t wrapped_r_auth_len,
1688 const u8 *siv_key)
1689{
1690 struct wpabuf *msg;
1691#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
1692 4 + 4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE
1693 u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
1694 u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
1695 const u8 *addr[2];
1696 size_t len[2], siv_len, attr_len;
1697 u8 *attr_start, *attr_end, *pos;
1698
1699 auth->waiting_auth_conf = 1;
1700 auth->auth_resp_tries = 0;
1701
1702 /* Build DPP Authentication Response frame attributes */
1703 attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
1704 4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data);
1705#ifdef CONFIG_TESTING_OPTIONS
1706 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP)
1707 attr_len += 5;
1708#endif /* CONFIG_TESTING_OPTIONS */
1709 msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
1710 if (!msg)
1711 return NULL;
1712
1713 attr_start = wpabuf_put(msg, 0);
1714
1715 /* DPP Status */
1716 if (status != 255)
1717 dpp_build_attr_status(msg, status);
1718
1719 /* Responder Bootstrapping Key Hash */
1720 dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
1721
1722 /* Initiator Bootstrapping Key Hash (mutual authentication) */
1723 dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
1724
1725 /* Responder Protocol Key */
1726 if (pr) {
1727 wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
1728 wpabuf_put_le16(msg, wpabuf_len(pr));
1729 wpabuf_put_buf(msg, pr);
1730 }
1731
1732 attr_end = wpabuf_put(msg, 0);
1733
1734#ifdef CONFIG_TESTING_OPTIONS
1735 if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) {
1736 wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
1737 goto skip_wrapped_data;
1738 }
1739#endif /* CONFIG_TESTING_OPTIONS */
1740
1741 /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
1742 pos = clear;
1743
1744 if (r_nonce) {
1745 /* R-nonce */
1746 WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
1747 pos += 2;
1748 WPA_PUT_LE16(pos, nonce_len);
1749 pos += 2;
1750 os_memcpy(pos, r_nonce, nonce_len);
1751 pos += nonce_len;
1752 }
1753
1754 if (i_nonce) {
1755 /* I-nonce */
1756 WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
1757 pos += 2;
1758 WPA_PUT_LE16(pos, nonce_len);
1759 pos += 2;
1760 os_memcpy(pos, i_nonce, nonce_len);
1761#ifdef CONFIG_TESTING_OPTIONS
1762 if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) {
1763 wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch");
1764 pos[nonce_len / 2] ^= 0x01;
1765 }
1766#endif /* CONFIG_TESTING_OPTIONS */
1767 pos += nonce_len;
1768 }
1769
1770#ifdef CONFIG_TESTING_OPTIONS
1771 if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) {
1772 wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab");
1773 goto skip_r_capab;
1774 }
1775#endif /* CONFIG_TESTING_OPTIONS */
1776
1777 /* R-capabilities */
1778 WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
1779 pos += 2;
1780 WPA_PUT_LE16(pos, 1);
1781 pos += 2;
1782 auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
1783 DPP_CAPAB_ENROLLEE;
1784 *pos++ = auth->r_capab;
1785#ifdef CONFIG_TESTING_OPTIONS
1786 if (dpp_test == DPP_TEST_ZERO_R_CAPAB) {
1787 wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities");
1788 pos[-1] = 0;
1789 } else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) {
1790 wpa_printf(MSG_INFO,
1791 "DPP: TESTING - incompatible R-capabilities");
1792 if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) ==
1793 (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE))
1794 pos[-1] = 0;
1795 else
1796 pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE :
1797 DPP_CAPAB_CONFIGURATOR;
1798 }
1799skip_r_capab:
1800#endif /* CONFIG_TESTING_OPTIONS */
1801
1802 if (wrapped_r_auth) {
1803 /* {R-auth}ke */
1804 WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
1805 pos += 2;
1806 WPA_PUT_LE16(pos, wrapped_r_auth_len);
1807 pos += 2;
1808 os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
1809 pos += wrapped_r_auth_len;
1810 }
1811
1812 /* OUI, OUI type, Crypto Suite, DPP frame type */
1813 addr[0] = wpabuf_head_u8(msg) + 2;
1814 len[0] = 3 + 1 + 1 + 1;
1815 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
1816
1817 /* Attributes before Wrapped Data */
1818 addr[1] = attr_start;
1819 len[1] = attr_end - attr_start;
1820 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
1821
1822 siv_len = pos - clear;
1823 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
1824 if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len,
1825 2, addr, len, wrapped_data) < 0) {
1826 wpabuf_free(msg);
1827 return NULL;
1828 }
1829 siv_len += AES_BLOCK_SIZE;
1830 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
1831 wrapped_data, siv_len);
1832
1833 wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
1834 wpabuf_put_le16(msg, siv_len);
1835 wpabuf_put_data(msg, wrapped_data, siv_len);
1836
1837#ifdef CONFIG_TESTING_OPTIONS
1838 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) {
1839 wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
1840 dpp_build_attr_status(msg, DPP_STATUS_OK);
1841 }
1842skip_wrapped_data:
1843#endif /* CONFIG_TESTING_OPTIONS */
1844
1845 wpa_hexdump_buf(MSG_DEBUG,
1846 "DPP: Authentication Response frame attributes", msg);
1847 return msg;
1848}
1849
1850
1851static int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes,
1852 u16 num_modes, unsigned int freq)
1853{
1854 u16 m;
1855 int c, flag;
1856
1857 if (!own_modes || !num_modes)
1858 return 1;
1859
1860 for (m = 0; m < num_modes; m++) {
1861 for (c = 0; c < own_modes[m].num_channels; c++) {
1862 if ((unsigned int) own_modes[m].channels[c].freq !=
1863 freq)
1864 continue;
1865 flag = own_modes[m].channels[c].flag;
1866 if (!(flag & (HOSTAPD_CHAN_DISABLED |
1867 HOSTAPD_CHAN_NO_IR |
1868 HOSTAPD_CHAN_RADAR)))
1869 return 1;
1870 }
1871 }
1872
1873 wpa_printf(MSG_DEBUG, "DPP: Peer channel %u MHz not supported", freq);
1874 return 0;
1875}
1876
1877
1878static int freq_included(const unsigned int freqs[], unsigned int num,
1879 unsigned int freq)
1880{
1881 while (num > 0) {
1882 if (freqs[--num] == freq)
1883 return 1;
1884 }
1885 return 0;
1886}
1887
1888
1889static void freq_to_start(unsigned int freqs[], unsigned int num,
1890 unsigned int freq)
1891{
1892 unsigned int i;
1893
1894 for (i = 0; i < num; i++) {
1895 if (freqs[i] == freq)
1896 break;
1897 }
1898 if (i == 0 || i >= num)
1899 return;
1900 os_memmove(&freqs[1], &freqs[0], i * sizeof(freqs[0]));
1901 freqs[0] = freq;
1902}
1903
1904
1905static int dpp_channel_intersect(struct dpp_authentication *auth,
1906 struct hostapd_hw_modes *own_modes,
1907 u16 num_modes)
1908{
1909 struct dpp_bootstrap_info *peer_bi = auth->peer_bi;
1910 unsigned int i, freq;
1911
1912 for (i = 0; i < peer_bi->num_freq; i++) {
1913 freq = peer_bi->freq[i];
1914 if (freq_included(auth->freq, auth->num_freq, freq))
1915 continue;
1916 if (dpp_channel_ok_init(own_modes, num_modes, freq))
1917 auth->freq[auth->num_freq++] = freq;
1918 }
1919 if (!auth->num_freq) {
1920 wpa_printf(MSG_INFO,
1921 "DPP: No available channels for initiating DPP Authentication");
1922 return -1;
1923 }
1924 auth->curr_freq = auth->freq[0];
1925 return 0;
1926}
1927
1928
1929static int dpp_channel_local_list(struct dpp_authentication *auth,
1930 struct hostapd_hw_modes *own_modes,
1931 u16 num_modes)
1932{
1933 u16 m;
1934 int c, flag;
1935 unsigned int freq;
1936
1937 auth->num_freq = 0;
1938
1939 if (!own_modes || !num_modes) {
1940 auth->freq[0] = 2412;
1941 auth->freq[1] = 2437;
1942 auth->freq[2] = 2462;
1943 auth->num_freq = 3;
1944 return 0;
1945 }
1946
1947 for (m = 0; m < num_modes; m++) {
1948 for (c = 0; c < own_modes[m].num_channels; c++) {
1949 freq = own_modes[m].channels[c].freq;
1950 flag = own_modes[m].channels[c].flag;
1951 if (flag & (HOSTAPD_CHAN_DISABLED |
1952 HOSTAPD_CHAN_NO_IR |
1953 HOSTAPD_CHAN_RADAR))
1954 continue;
1955 if (freq_included(auth->freq, auth->num_freq, freq))
1956 continue;
1957 auth->freq[auth->num_freq++] = freq;
1958 if (auth->num_freq == DPP_BOOTSTRAP_MAX_FREQ) {
1959 m = num_modes;
1960 break;
1961 }
1962 }
1963 }
1964
1965 return auth->num_freq == 0 ? -1 : 0;
1966}
1967
1968
1969static int dpp_prepare_channel_list(struct dpp_authentication *auth,
1970 struct hostapd_hw_modes *own_modes,
1971 u16 num_modes)
1972{
1973 int res;
1974 char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end;
1975 unsigned int i;
1976
1977 if (auth->peer_bi->num_freq > 0)
1978 res = dpp_channel_intersect(auth, own_modes, num_modes);
1979 else
1980 res = dpp_channel_local_list(auth, own_modes, num_modes);
1981 if (res < 0)
1982 return res;
1983
1984 /* Prioritize 2.4 GHz channels 6, 1, 11 (in this order) to hit the most
1985 * likely channels first. */
1986 freq_to_start(auth->freq, auth->num_freq, 2462);
1987 freq_to_start(auth->freq, auth->num_freq, 2412);
1988 freq_to_start(auth->freq, auth->num_freq, 2437);
1989
1990 auth->freq_idx = 0;
1991 auth->curr_freq = auth->freq[0];
1992
1993 pos = freqs;
1994 end = pos + sizeof(freqs);
1995 for (i = 0; i < auth->num_freq; i++) {
1996 res = os_snprintf(pos, end - pos, " %u", auth->freq[i]);
1997 if (os_snprintf_error(end - pos, res))
1998 break;
1999 pos += res;
2000 }
2001 *pos = '\0';
2002 wpa_printf(MSG_DEBUG, "DPP: Possible frequencies for initiating:%s",
2003 freqs);
2004
2005 return 0;
2006}
2007
2008
2009static int dpp_autogen_bootstrap_key(struct dpp_authentication *auth)
2010{
2011 struct dpp_bootstrap_info *bi;
2012 char *pk = NULL;
2013 size_t len;
2014
2015 if (auth->own_bi)
2016 return 0; /* already generated */
2017
2018 bi = os_zalloc(sizeof(*bi));
2019 if (!bi)
2020 return -1;
2021 bi->type = DPP_BOOTSTRAP_QR_CODE;
2022 pk = dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0);
2023 if (!pk)
2024 goto fail;
2025
2026 len = 4; /* "DPP:" */
2027 len += 4 + os_strlen(pk);
2028 bi->uri = os_malloc(len + 1);
2029 if (!bi->uri)
2030 goto fail;
2031 os_snprintf(bi->uri, len + 1, "DPP:K:%s;;", pk);
2032 wpa_printf(MSG_DEBUG,
2033 "DPP: Auto-generated own bootstrapping key info: URI %s",
2034 bi->uri);
2035
2036 auth->tmp_own_bi = auth->own_bi = bi;
2037
2038 os_free(pk);
2039
2040 return 0;
2041fail:
2042 os_free(pk);
2043 dpp_bootstrap_info_free(bi);
2044 return -1;
2045}
2046
2047
2048struct dpp_authentication * dpp_auth_init(void *msg_ctx,
2049 struct dpp_bootstrap_info *peer_bi,
2050 struct dpp_bootstrap_info *own_bi,
2051 u8 dpp_allowed_roles,
2052 unsigned int neg_freq,
2053 struct hostapd_hw_modes *own_modes,
2054 u16 num_modes)
2055{
2056 struct dpp_authentication *auth;
2057 size_t nonce_len;
2058 EVP_PKEY_CTX *ctx = NULL;
2059 size_t secret_len;
2060 struct wpabuf *pi = NULL;
2061 const u8 *r_pubkey_hash, *i_pubkey_hash;
2062#ifdef CONFIG_TESTING_OPTIONS
2063 u8 test_hash[SHA256_MAC_LEN];
2064#endif /* CONFIG_TESTING_OPTIONS */
2065
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002066 auth = os_zalloc(sizeof(*auth));
2067 if (!auth)
2068 return NULL;
2069 auth->msg_ctx = msg_ctx;
2070 auth->initiator = 1;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002071 auth->waiting_auth_resp = 1;
2072 auth->allowed_roles = dpp_allowed_roles;
2073 auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002074 auth->peer_bi = peer_bi;
2075 auth->own_bi = own_bi;
2076 auth->curve = peer_bi->curve;
2077
Roshan Pius3a1667e2018-07-03 15:17:14 -07002078 if (dpp_autogen_bootstrap_key(auth) < 0 ||
2079 dpp_prepare_channel_list(auth, own_modes, num_modes) < 0)
2080 goto fail;
2081
2082#ifdef CONFIG_TESTING_OPTIONS
2083 if (dpp_nonce_override_len > 0) {
2084 wpa_printf(MSG_INFO, "DPP: TESTING - override I-nonce");
2085 nonce_len = dpp_nonce_override_len;
2086 os_memcpy(auth->i_nonce, dpp_nonce_override, nonce_len);
2087 } else {
2088 nonce_len = auth->curve->nonce_len;
2089 if (random_get_bytes(auth->i_nonce, nonce_len)) {
2090 wpa_printf(MSG_ERROR,
2091 "DPP: Failed to generate I-nonce");
2092 goto fail;
2093 }
2094 }
2095#else /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002096 nonce_len = auth->curve->nonce_len;
2097 if (random_get_bytes(auth->i_nonce, nonce_len)) {
2098 wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
2099 goto fail;
2100 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07002101#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002102 wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len);
2103
Roshan Pius3a1667e2018-07-03 15:17:14 -07002104#ifdef CONFIG_TESTING_OPTIONS
2105 if (dpp_protocol_key_override_len) {
2106 const struct dpp_curve_params *tmp_curve;
2107
2108 wpa_printf(MSG_INFO,
2109 "DPP: TESTING - override protocol key");
2110 auth->own_protocol_key = dpp_set_keypair(
2111 &tmp_curve, dpp_protocol_key_override,
2112 dpp_protocol_key_override_len);
2113 } else {
2114 auth->own_protocol_key = dpp_gen_keypair(auth->curve);
2115 }
2116#else /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002117 auth->own_protocol_key = dpp_gen_keypair(auth->curve);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002118#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002119 if (!auth->own_protocol_key)
2120 goto fail;
2121
2122 pi = dpp_get_pubkey_point(auth->own_protocol_key, 0);
2123 if (!pi)
2124 goto fail;
2125
2126 /* ECDH: M = pI * BR */
2127 ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
2128 if (!ctx ||
2129 EVP_PKEY_derive_init(ctx) != 1 ||
2130 EVP_PKEY_derive_set_peer(ctx, auth->peer_bi->pubkey) != 1 ||
2131 EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
2132 secret_len > DPP_MAX_SHARED_SECRET_LEN ||
2133 EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
2134 wpa_printf(MSG_ERROR,
2135 "DPP: Failed to derive ECDH shared secret: %s",
2136 ERR_error_string(ERR_get_error(), NULL));
2137 goto fail;
2138 }
2139 auth->secret_len = secret_len;
2140 EVP_PKEY_CTX_free(ctx);
2141 ctx = NULL;
2142
2143 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
2144 auth->Mx, auth->secret_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002145 auth->Mx_len = auth->secret_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002146
2147 if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
2148 auth->curve->hash_len) < 0)
2149 goto fail;
2150
Roshan Pius3a1667e2018-07-03 15:17:14 -07002151 r_pubkey_hash = auth->peer_bi->pubkey_hash;
2152 i_pubkey_hash = auth->own_bi->pubkey_hash;
2153
2154#ifdef CONFIG_TESTING_OPTIONS
2155 if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
2156 wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
2157 r_pubkey_hash = NULL;
2158 } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
2159 wpa_printf(MSG_INFO,
2160 "DPP: TESTING - invalid R-Bootstrap Key Hash");
2161 os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
2162 test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
2163 r_pubkey_hash = test_hash;
2164 } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
2165 wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
2166 i_pubkey_hash = NULL;
2167 } else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
2168 wpa_printf(MSG_INFO,
2169 "DPP: TESTING - invalid I-Bootstrap Key Hash");
2170 os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
2171 test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
2172 i_pubkey_hash = test_hash;
2173 } else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) {
2174 wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key");
2175 wpabuf_free(pi);
2176 pi = NULL;
2177 } else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) {
2178 wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key");
2179 wpabuf_free(pi);
2180 pi = wpabuf_alloc(2 * auth->curve->prime_len);
2181 if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0)
2182 goto fail;
2183 }
2184#endif /* CONFIG_TESTING_OPTIONS */
2185
2186 auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash,
2187 i_pubkey_hash, neg_freq);
2188 if (!auth->req_msg)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002189 goto fail;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002190
Roshan Pius3a1667e2018-07-03 15:17:14 -07002191out:
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002192 wpabuf_free(pi);
2193 EVP_PKEY_CTX_free(ctx);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002194 return auth;
2195fail:
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002196 dpp_auth_deinit(auth);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002197 auth = NULL;
2198 goto out;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002199}
2200
2201
2202struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
2203 const char *json)
2204{
2205 size_t nonce_len;
2206 size_t json_len, clear_len;
2207 struct wpabuf *clear = NULL, *msg = NULL;
2208 u8 *wrapped;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002209 size_t attr_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002210
2211 wpa_printf(MSG_DEBUG, "DPP: Build configuration request");
2212
2213 nonce_len = auth->curve->nonce_len;
2214 if (random_get_bytes(auth->e_nonce, nonce_len)) {
2215 wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce");
2216 goto fail;
2217 }
2218 wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", auth->e_nonce, nonce_len);
2219 json_len = os_strlen(json);
2220 wpa_hexdump_ascii(MSG_DEBUG, "DPP: configAttr JSON", json, json_len);
2221
2222 /* { E-nonce, configAttrib }ke */
2223 clear_len = 4 + nonce_len + 4 + json_len;
2224 clear = wpabuf_alloc(clear_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002225 attr_len = 4 + clear_len + AES_BLOCK_SIZE;
2226#ifdef CONFIG_TESTING_OPTIONS
2227 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ)
2228 attr_len += 5;
2229#endif /* CONFIG_TESTING_OPTIONS */
2230 msg = wpabuf_alloc(attr_len);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002231 if (!clear || !msg)
2232 goto fail;
2233
Roshan Pius3a1667e2018-07-03 15:17:14 -07002234#ifdef CONFIG_TESTING_OPTIONS
2235 if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_REQ) {
2236 wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce");
2237 goto skip_e_nonce;
2238 }
2239 if (dpp_test == DPP_TEST_INVALID_E_NONCE_CONF_REQ) {
2240 wpa_printf(MSG_INFO, "DPP: TESTING - invalid E-nonce");
2241 wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
2242 wpabuf_put_le16(clear, nonce_len - 1);
2243 wpabuf_put_data(clear, auth->e_nonce, nonce_len - 1);
2244 goto skip_e_nonce;
2245 }
2246 if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_REQ) {
2247 wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
2248 goto skip_wrapped_data;
2249 }
2250#endif /* CONFIG_TESTING_OPTIONS */
2251
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002252 /* E-nonce */
2253 wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
2254 wpabuf_put_le16(clear, nonce_len);
2255 wpabuf_put_data(clear, auth->e_nonce, nonce_len);
2256
Roshan Pius3a1667e2018-07-03 15:17:14 -07002257#ifdef CONFIG_TESTING_OPTIONS
2258skip_e_nonce:
2259 if (dpp_test == DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ) {
2260 wpa_printf(MSG_INFO, "DPP: TESTING - no configAttrib");
2261 goto skip_conf_attr_obj;
2262 }
2263#endif /* CONFIG_TESTING_OPTIONS */
2264
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002265 /* configAttrib */
2266 wpabuf_put_le16(clear, DPP_ATTR_CONFIG_ATTR_OBJ);
2267 wpabuf_put_le16(clear, json_len);
2268 wpabuf_put_data(clear, json, json_len);
2269
Roshan Pius3a1667e2018-07-03 15:17:14 -07002270#ifdef CONFIG_TESTING_OPTIONS
2271skip_conf_attr_obj:
2272#endif /* CONFIG_TESTING_OPTIONS */
2273
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002274 wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
2275 wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
2276 wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
2277
2278 /* No AES-SIV AD */
2279 wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
2280 if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
2281 wpabuf_head(clear), wpabuf_len(clear),
2282 0, NULL, NULL, wrapped) < 0)
2283 goto fail;
2284 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
2285 wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
2286
Roshan Pius3a1667e2018-07-03 15:17:14 -07002287#ifdef CONFIG_TESTING_OPTIONS
2288 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) {
2289 wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
2290 dpp_build_attr_status(msg, DPP_STATUS_OK);
2291 }
2292skip_wrapped_data:
2293#endif /* CONFIG_TESTING_OPTIONS */
2294
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002295 wpa_hexdump_buf(MSG_DEBUG,
2296 "DPP: Configuration Request frame attributes", msg);
2297 wpabuf_free(clear);
2298 return msg;
2299
2300fail:
2301 wpabuf_free(clear);
2302 wpabuf_free(msg);
2303 return NULL;
2304}
2305
2306
2307static void dpp_auth_success(struct dpp_authentication *auth)
2308{
2309 wpa_printf(MSG_DEBUG,
2310 "DPP: Authentication success - clear temporary keys");
2311 os_memset(auth->Mx, 0, sizeof(auth->Mx));
Roshan Pius3a1667e2018-07-03 15:17:14 -07002312 auth->Mx_len = 0;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002313 os_memset(auth->Nx, 0, sizeof(auth->Nx));
Roshan Pius3a1667e2018-07-03 15:17:14 -07002314 auth->Nx_len = 0;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002315 os_memset(auth->Lx, 0, sizeof(auth->Lx));
Roshan Pius3a1667e2018-07-03 15:17:14 -07002316 auth->Lx_len = 0;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002317 os_memset(auth->k1, 0, sizeof(auth->k1));
2318 os_memset(auth->k2, 0, sizeof(auth->k2));
2319
2320 auth->auth_success = 1;
2321}
2322
2323
2324static int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth)
2325{
2326 struct wpabuf *pix, *prx, *bix, *brx;
2327 const u8 *addr[7];
2328 size_t len[7];
2329 size_t i, num_elem = 0;
2330 size_t nonce_len;
2331 u8 zero = 0;
2332 int res = -1;
2333
2334 /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
2335 nonce_len = auth->curve->nonce_len;
2336
2337 if (auth->initiator) {
2338 pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
2339 prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
2340 if (auth->own_bi)
2341 bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
2342 else
2343 bix = NULL;
2344 brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
2345 } else {
2346 pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
2347 prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
2348 if (auth->peer_bi)
2349 bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
2350 else
2351 bix = NULL;
2352 brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
2353 }
2354 if (!pix || !prx || !brx)
2355 goto fail;
2356
2357 addr[num_elem] = auth->i_nonce;
2358 len[num_elem] = nonce_len;
2359 num_elem++;
2360
2361 addr[num_elem] = auth->r_nonce;
2362 len[num_elem] = nonce_len;
2363 num_elem++;
2364
2365 addr[num_elem] = wpabuf_head(pix);
2366 len[num_elem] = wpabuf_len(pix) / 2;
2367 num_elem++;
2368
2369 addr[num_elem] = wpabuf_head(prx);
2370 len[num_elem] = wpabuf_len(prx) / 2;
2371 num_elem++;
2372
2373 if (bix) {
2374 addr[num_elem] = wpabuf_head(bix);
2375 len[num_elem] = wpabuf_len(bix) / 2;
2376 num_elem++;
2377 }
2378
2379 addr[num_elem] = wpabuf_head(brx);
2380 len[num_elem] = wpabuf_len(brx) / 2;
2381 num_elem++;
2382
2383 addr[num_elem] = &zero;
2384 len[num_elem] = 1;
2385 num_elem++;
2386
2387 wpa_printf(MSG_DEBUG, "DPP: R-auth hash components");
2388 for (i = 0; i < num_elem; i++)
2389 wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
2390 res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth);
2391 if (res == 0)
2392 wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth,
2393 auth->curve->hash_len);
2394fail:
2395 wpabuf_free(pix);
2396 wpabuf_free(prx);
2397 wpabuf_free(bix);
2398 wpabuf_free(brx);
2399 return res;
2400}
2401
2402
2403static int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth)
2404{
2405 struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL;
2406 const u8 *addr[7];
2407 size_t len[7];
2408 size_t i, num_elem = 0;
2409 size_t nonce_len;
2410 u8 one = 1;
2411 int res = -1;
2412
2413 /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
2414 nonce_len = auth->curve->nonce_len;
2415
2416 if (auth->initiator) {
2417 pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
2418 prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
2419 if (auth->own_bi)
2420 bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
2421 else
2422 bix = NULL;
2423 if (!auth->peer_bi)
2424 goto fail;
2425 brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
2426 } else {
2427 pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
2428 prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
2429 if (auth->peer_bi)
2430 bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
2431 else
2432 bix = NULL;
2433 if (!auth->own_bi)
2434 goto fail;
2435 brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
2436 }
2437 if (!pix || !prx || !brx)
2438 goto fail;
2439
2440 addr[num_elem] = auth->r_nonce;
2441 len[num_elem] = nonce_len;
2442 num_elem++;
2443
2444 addr[num_elem] = auth->i_nonce;
2445 len[num_elem] = nonce_len;
2446 num_elem++;
2447
2448 addr[num_elem] = wpabuf_head(prx);
2449 len[num_elem] = wpabuf_len(prx) / 2;
2450 num_elem++;
2451
2452 addr[num_elem] = wpabuf_head(pix);
2453 len[num_elem] = wpabuf_len(pix) / 2;
2454 num_elem++;
2455
2456 addr[num_elem] = wpabuf_head(brx);
2457 len[num_elem] = wpabuf_len(brx) / 2;
2458 num_elem++;
2459
2460 if (bix) {
2461 addr[num_elem] = wpabuf_head(bix);
2462 len[num_elem] = wpabuf_len(bix) / 2;
2463 num_elem++;
2464 }
2465
2466 addr[num_elem] = &one;
2467 len[num_elem] = 1;
2468 num_elem++;
2469
2470 wpa_printf(MSG_DEBUG, "DPP: I-auth hash components");
2471 for (i = 0; i < num_elem; i++)
2472 wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
2473 res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth);
2474 if (res == 0)
2475 wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth,
2476 auth->curve->hash_len);
2477fail:
2478 wpabuf_free(pix);
2479 wpabuf_free(prx);
2480 wpabuf_free(bix);
2481 wpabuf_free(brx);
2482 return res;
2483}
2484
2485
2486static int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
2487{
2488 const EC_GROUP *group;
2489 EC_POINT *l = NULL;
2490 EC_KEY *BI = NULL, *bR = NULL, *pR = NULL;
2491 const EC_POINT *BI_point;
2492 BN_CTX *bnctx;
2493 BIGNUM *lx, *sum, *q;
2494 const BIGNUM *bR_bn, *pR_bn;
2495 int ret = -1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002496
2497 /* L = ((bR + pR) modulo q) * BI */
2498
2499 bnctx = BN_CTX_new();
2500 sum = BN_new();
2501 q = BN_new();
2502 lx = BN_new();
2503 if (!bnctx || !sum || !q || !lx)
2504 goto fail;
2505 BI = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
2506 if (!BI)
2507 goto fail;
2508 BI_point = EC_KEY_get0_public_key(BI);
2509 group = EC_KEY_get0_group(BI);
2510 if (!group)
2511 goto fail;
2512
2513 bR = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
2514 pR = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
2515 if (!bR || !pR)
2516 goto fail;
2517 bR_bn = EC_KEY_get0_private_key(bR);
2518 pR_bn = EC_KEY_get0_private_key(pR);
2519 if (!bR_bn || !pR_bn)
2520 goto fail;
2521 if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
2522 BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1)
2523 goto fail;
2524 l = EC_POINT_new(group);
2525 if (!l ||
2526 EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 ||
2527 EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
2528 bnctx) != 1) {
2529 wpa_printf(MSG_ERROR,
2530 "OpenSSL: failed: %s",
2531 ERR_error_string(ERR_get_error(), NULL));
2532 goto fail;
2533 }
2534
Roshan Pius3a1667e2018-07-03 15:17:14 -07002535 if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002536 goto fail;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002537 wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002538 auth->Lx_len = auth->secret_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002539 ret = 0;
2540fail:
2541 EC_POINT_clear_free(l);
2542 EC_KEY_free(BI);
2543 EC_KEY_free(bR);
2544 EC_KEY_free(pR);
2545 BN_clear_free(lx);
2546 BN_clear_free(sum);
2547 BN_free(q);
2548 BN_CTX_free(bnctx);
2549 return ret;
2550}
2551
2552
2553static int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
2554{
2555 const EC_GROUP *group;
2556 EC_POINT *l = NULL, *sum = NULL;
2557 EC_KEY *bI = NULL, *BR = NULL, *PR = NULL;
2558 const EC_POINT *BR_point, *PR_point;
2559 BN_CTX *bnctx;
2560 BIGNUM *lx;
2561 const BIGNUM *bI_bn;
2562 int ret = -1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002563
2564 /* L = bI * (BR + PR) */
2565
2566 bnctx = BN_CTX_new();
2567 lx = BN_new();
2568 if (!bnctx || !lx)
2569 goto fail;
2570 BR = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
2571 PR = EVP_PKEY_get1_EC_KEY(auth->peer_protocol_key);
2572 if (!BR || !PR)
2573 goto fail;
2574 BR_point = EC_KEY_get0_public_key(BR);
2575 PR_point = EC_KEY_get0_public_key(PR);
2576
2577 bI = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
2578 if (!bI)
2579 goto fail;
2580 group = EC_KEY_get0_group(bI);
2581 bI_bn = EC_KEY_get0_private_key(bI);
2582 if (!group || !bI_bn)
2583 goto fail;
2584 sum = EC_POINT_new(group);
2585 l = EC_POINT_new(group);
2586 if (!sum || !l ||
2587 EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 ||
2588 EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 ||
2589 EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
2590 bnctx) != 1) {
2591 wpa_printf(MSG_ERROR,
2592 "OpenSSL: failed: %s",
2593 ERR_error_string(ERR_get_error(), NULL));
2594 goto fail;
2595 }
2596
Roshan Pius3a1667e2018-07-03 15:17:14 -07002597 if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002598 goto fail;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002599 wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002600 auth->Lx_len = auth->secret_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002601 ret = 0;
2602fail:
2603 EC_POINT_clear_free(l);
2604 EC_KEY_free(bI);
2605 EC_KEY_free(BR);
2606 EC_KEY_free(PR);
2607 BN_clear_free(lx);
2608 BN_CTX_free(bnctx);
2609 return ret;
2610}
2611
2612
Roshan Pius3a1667e2018-07-03 15:17:14 -07002613static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002614{
2615 size_t nonce_len;
2616 EVP_PKEY_CTX *ctx = NULL;
2617 size_t secret_len;
2618 struct wpabuf *msg, *pr = NULL;
2619 u8 r_auth[4 + DPP_MAX_HASH_LEN];
Roshan Pius3a1667e2018-07-03 15:17:14 -07002620 u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002621 size_t wrapped_r_auth_len;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002622 int ret = -1;
2623 const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce;
2624 enum dpp_status_error status = DPP_STATUS_OK;
2625#ifdef CONFIG_TESTING_OPTIONS
2626 u8 test_hash[SHA256_MAC_LEN];
2627#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002628
2629 wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
Roshan Pius3a1667e2018-07-03 15:17:14 -07002630 if (!auth->own_bi)
2631 return -1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002632
Roshan Pius3a1667e2018-07-03 15:17:14 -07002633#ifdef CONFIG_TESTING_OPTIONS
2634 if (dpp_nonce_override_len > 0) {
2635 wpa_printf(MSG_INFO, "DPP: TESTING - override R-nonce");
2636 nonce_len = dpp_nonce_override_len;
2637 os_memcpy(auth->r_nonce, dpp_nonce_override, nonce_len);
2638 } else {
2639 nonce_len = auth->curve->nonce_len;
2640 if (random_get_bytes(auth->r_nonce, nonce_len)) {
2641 wpa_printf(MSG_ERROR,
2642 "DPP: Failed to generate R-nonce");
2643 goto fail;
2644 }
2645 }
2646#else /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002647 nonce_len = auth->curve->nonce_len;
2648 if (random_get_bytes(auth->r_nonce, nonce_len)) {
2649 wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
2650 goto fail;
2651 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07002652#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002653 wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len);
2654
Roshan Pius3a1667e2018-07-03 15:17:14 -07002655#ifdef CONFIG_TESTING_OPTIONS
2656 if (dpp_protocol_key_override_len) {
2657 const struct dpp_curve_params *tmp_curve;
2658
2659 wpa_printf(MSG_INFO,
2660 "DPP: TESTING - override protocol key");
2661 auth->own_protocol_key = dpp_set_keypair(
2662 &tmp_curve, dpp_protocol_key_override,
2663 dpp_protocol_key_override_len);
2664 } else {
2665 auth->own_protocol_key = dpp_gen_keypair(auth->curve);
2666 }
2667#else /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002668 auth->own_protocol_key = dpp_gen_keypair(auth->curve);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002669#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002670 if (!auth->own_protocol_key)
2671 goto fail;
2672
2673 pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
2674 if (!pr)
2675 goto fail;
2676
2677 /* ECDH: N = pR * PI */
2678 ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
2679 if (!ctx ||
2680 EVP_PKEY_derive_init(ctx) != 1 ||
2681 EVP_PKEY_derive_set_peer(ctx, auth->peer_protocol_key) != 1 ||
2682 EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
2683 secret_len > DPP_MAX_SHARED_SECRET_LEN ||
2684 EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) {
2685 wpa_printf(MSG_ERROR,
2686 "DPP: Failed to derive ECDH shared secret: %s",
2687 ERR_error_string(ERR_get_error(), NULL));
2688 goto fail;
2689 }
2690 EVP_PKEY_CTX_free(ctx);
2691 ctx = NULL;
2692
2693 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
2694 auth->Nx, auth->secret_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002695 auth->Nx_len = auth->secret_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002696
2697 if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
2698 auth->curve->hash_len) < 0)
2699 goto fail;
2700
2701 if (auth->own_bi && auth->peer_bi) {
2702 /* Mutual authentication */
2703 if (dpp_auth_derive_l_responder(auth) < 0)
2704 goto fail;
2705 }
2706
2707 if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
2708 goto fail;
2709
2710 /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
2711 WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG);
2712 WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002713 if (dpp_gen_r_auth(auth, r_auth + 4) < 0)
2714 goto fail;
2715#ifdef CONFIG_TESTING_OPTIONS
2716 if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) {
2717 wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch");
2718 r_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
2719 }
2720#endif /* CONFIG_TESTING_OPTIONS */
2721 if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002722 r_auth, 4 + auth->curve->hash_len,
2723 0, NULL, NULL, wrapped_r_auth) < 0)
2724 goto fail;
2725 wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE;
2726 wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke",
2727 wrapped_r_auth, wrapped_r_auth_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002728 w_r_auth = wrapped_r_auth;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002729
Roshan Pius3a1667e2018-07-03 15:17:14 -07002730 r_pubkey_hash = auth->own_bi->pubkey_hash;
2731 if (auth->peer_bi)
2732 i_pubkey_hash = auth->peer_bi->pubkey_hash;
2733 else
2734 i_pubkey_hash = NULL;
2735
2736 i_nonce = auth->i_nonce;
2737 r_nonce = auth->r_nonce;
2738
2739#ifdef CONFIG_TESTING_OPTIONS
2740 if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
2741 wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
2742 r_pubkey_hash = NULL;
2743 } else if (dpp_test ==
2744 DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
2745 wpa_printf(MSG_INFO,
2746 "DPP: TESTING - invalid R-Bootstrap Key Hash");
2747 os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
2748 test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
2749 r_pubkey_hash = test_hash;
2750 } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
2751 wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
2752 i_pubkey_hash = NULL;
2753 } else if (dpp_test ==
2754 DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
2755 wpa_printf(MSG_INFO,
2756 "DPP: TESTING - invalid I-Bootstrap Key Hash");
2757 if (i_pubkey_hash)
2758 os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
2759 else
2760 os_memset(test_hash, 0, SHA256_MAC_LEN);
2761 test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
2762 i_pubkey_hash = test_hash;
2763 } else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) {
2764 wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key");
2765 wpabuf_free(pr);
2766 pr = NULL;
2767 } else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) {
2768 wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key");
2769 wpabuf_free(pr);
2770 pr = wpabuf_alloc(2 * auth->curve->prime_len);
2771 if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0)
2772 goto fail;
2773 } else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) {
2774 wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth");
2775 w_r_auth = NULL;
2776 wrapped_r_auth_len = 0;
2777 } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
2778 wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
2779 status = 255;
2780 } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) {
2781 wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
2782 status = 254;
2783 } else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) {
2784 wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce");
2785 r_nonce = NULL;
2786 } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
2787 wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
2788 i_nonce = NULL;
2789 }
2790#endif /* CONFIG_TESTING_OPTIONS */
2791
2792 msg = dpp_auth_build_resp(auth, status, pr, nonce_len,
2793 r_pubkey_hash, i_pubkey_hash,
2794 r_nonce, i_nonce,
2795 w_r_auth, wrapped_r_auth_len,
2796 auth->k2);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002797 if (!msg)
2798 goto fail;
2799 wpabuf_free(auth->resp_msg);
2800 auth->resp_msg = msg;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002801 ret = 0;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002802fail:
2803 wpabuf_free(pr);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002804 return ret;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002805}
2806
2807
2808static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
2809 enum dpp_status_error status)
2810{
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002811 struct wpabuf *msg;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002812 const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce;
2813#ifdef CONFIG_TESTING_OPTIONS
2814 u8 test_hash[SHA256_MAC_LEN];
2815#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002816
Roshan Pius3a1667e2018-07-03 15:17:14 -07002817 if (!auth->own_bi)
2818 return -1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002819 wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
2820
Roshan Pius3a1667e2018-07-03 15:17:14 -07002821 r_pubkey_hash = auth->own_bi->pubkey_hash;
2822 if (auth->peer_bi)
2823 i_pubkey_hash = auth->peer_bi->pubkey_hash;
2824 else
2825 i_pubkey_hash = NULL;
2826
2827 i_nonce = auth->i_nonce;
2828
2829#ifdef CONFIG_TESTING_OPTIONS
2830 if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
2831 wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
2832 r_pubkey_hash = NULL;
2833 } else if (dpp_test ==
2834 DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
2835 wpa_printf(MSG_INFO,
2836 "DPP: TESTING - invalid R-Bootstrap Key Hash");
2837 os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
2838 test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
2839 r_pubkey_hash = test_hash;
2840 } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
2841 wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
2842 i_pubkey_hash = NULL;
2843 } else if (dpp_test ==
2844 DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
2845 wpa_printf(MSG_INFO,
2846 "DPP: TESTING - invalid I-Bootstrap Key Hash");
2847 if (i_pubkey_hash)
2848 os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
2849 else
2850 os_memset(test_hash, 0, SHA256_MAC_LEN);
2851 test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
2852 i_pubkey_hash = test_hash;
2853 } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
2854 wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
2855 status = -1;
2856 } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
2857 wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
2858 i_nonce = NULL;
2859 }
2860#endif /* CONFIG_TESTING_OPTIONS */
2861
2862 msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len,
2863 r_pubkey_hash, i_pubkey_hash,
2864 NULL, i_nonce, NULL, 0, auth->k1);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002865 if (!msg)
Roshan Pius3a1667e2018-07-03 15:17:14 -07002866 return -1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002867 wpabuf_free(auth->resp_msg);
2868 auth->resp_msg = msg;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002869 return 0;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002870}
2871
2872
2873struct dpp_authentication *
2874dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
2875 struct dpp_bootstrap_info *peer_bi,
2876 struct dpp_bootstrap_info *own_bi,
2877 unsigned int freq, const u8 *hdr, const u8 *attr_start,
Roshan Pius3a1667e2018-07-03 15:17:14 -07002878 size_t attr_len)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002879{
2880 EVP_PKEY *pi = NULL;
2881 EVP_PKEY_CTX *ctx = NULL;
2882 size_t secret_len;
2883 const u8 *addr[2];
2884 size_t len[2];
2885 u8 *unwrapped = NULL;
2886 size_t unwrapped_len = 0;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002887 const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap,
2888 *channel;
2889 u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len,
2890 i_bootstrap_len, channel_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002891 struct dpp_authentication *auth = NULL;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002892
Roshan Pius3a1667e2018-07-03 15:17:14 -07002893#ifdef CONFIG_TESTING_OPTIONS
2894 if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) {
2895 wpa_printf(MSG_INFO,
2896 "DPP: TESTING - stop at Authentication Request");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002897 return NULL;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002898 }
2899#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002900
Roshan Pius3a1667e2018-07-03 15:17:14 -07002901 wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
2902 &wrapped_data_len);
2903 if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
2904 wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
2905 "Missing or invalid required Wrapped Data attribute");
2906 return NULL;
2907 }
2908 wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
2909 wrapped_data, wrapped_data_len);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002910 attr_len = wrapped_data - 4 - attr_start;
2911
2912 auth = os_zalloc(sizeof(*auth));
2913 if (!auth)
2914 goto fail;
2915 auth->msg_ctx = msg_ctx;
2916 auth->peer_bi = peer_bi;
2917 auth->own_bi = own_bi;
2918 auth->curve = own_bi->curve;
2919 auth->curr_freq = freq;
2920
Roshan Pius3a1667e2018-07-03 15:17:14 -07002921 channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL,
2922 &channel_len);
2923 if (channel) {
2924 int neg_freq;
2925
2926 if (channel_len < 2) {
2927 dpp_auth_fail(auth, "Too short Channel attribute");
2928 goto fail;
2929 }
2930
2931 neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]);
2932 wpa_printf(MSG_DEBUG,
2933 "DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d",
2934 channel[0], channel[1], neg_freq);
2935 if (neg_freq < 0) {
2936 dpp_auth_fail(auth,
2937 "Unsupported Channel attribute value");
2938 goto fail;
2939 }
2940
2941 if (auth->curr_freq != (unsigned int) neg_freq) {
2942 wpa_printf(MSG_DEBUG,
2943 "DPP: Changing negotiation channel from %u MHz to %u MHz",
2944 freq, neg_freq);
2945 auth->curr_freq = neg_freq;
2946 }
2947 }
2948
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002949 i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY,
2950 &i_proto_len);
2951 if (!i_proto) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07002952 dpp_auth_fail(auth,
2953 "Missing required Initiator Protocol Key attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002954 goto fail;
2955 }
2956 wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key",
2957 i_proto, i_proto_len);
2958
2959 /* M = bR * PI */
2960 pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len);
2961 if (!pi) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07002962 dpp_auth_fail(auth, "Invalid Initiator Protocol Key");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002963 goto fail;
2964 }
2965 dpp_debug_print_key("Peer (Initiator) Protocol Key", pi);
2966
2967 ctx = EVP_PKEY_CTX_new(own_bi->pubkey, NULL);
2968 if (!ctx ||
2969 EVP_PKEY_derive_init(ctx) != 1 ||
2970 EVP_PKEY_derive_set_peer(ctx, pi) != 1 ||
2971 EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
2972 secret_len > DPP_MAX_SHARED_SECRET_LEN ||
2973 EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
2974 wpa_printf(MSG_ERROR,
2975 "DPP: Failed to derive ECDH shared secret: %s",
2976 ERR_error_string(ERR_get_error(), NULL));
Roshan Pius3a1667e2018-07-03 15:17:14 -07002977 dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002978 goto fail;
2979 }
2980 auth->secret_len = secret_len;
2981 EVP_PKEY_CTX_free(ctx);
2982 ctx = NULL;
2983
2984 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
2985 auth->Mx, auth->secret_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002986 auth->Mx_len = auth->secret_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002987
2988 if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
2989 auth->curve->hash_len) < 0)
2990 goto fail;
2991
2992 addr[0] = hdr;
2993 len[0] = DPP_HDR_LEN;
2994 addr[1] = attr_start;
2995 len[1] = attr_len;
2996 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
2997 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
2998 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
2999 wrapped_data, wrapped_data_len);
3000 unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
3001 unwrapped = os_malloc(unwrapped_len);
3002 if (!unwrapped)
3003 goto fail;
3004 if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
3005 wrapped_data, wrapped_data_len,
3006 2, addr, len, unwrapped) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003007 dpp_auth_fail(auth, "AES-SIV decryption failed");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003008 goto fail;
3009 }
3010 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
3011 unwrapped, unwrapped_len);
3012
3013 if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003014 dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003015 goto fail;
3016 }
3017
3018 i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
3019 &i_nonce_len);
3020 if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003021 dpp_auth_fail(auth, "Missing or invalid I-nonce");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003022 goto fail;
3023 }
3024 wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
3025 os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
3026
3027 i_capab = dpp_get_attr(unwrapped, unwrapped_len,
3028 DPP_ATTR_I_CAPABILITIES,
3029 &i_capab_len);
3030 if (!i_capab || i_capab_len < 1) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003031 dpp_auth_fail(auth, "Missing or invalid I-capabilities");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003032 goto fail;
3033 }
3034 auth->i_capab = i_capab[0];
3035 wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab);
3036
3037 bin_clear_free(unwrapped, unwrapped_len);
3038 unwrapped = NULL;
3039
3040 switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) {
3041 case DPP_CAPAB_ENROLLEE:
3042 if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) {
3043 wpa_printf(MSG_DEBUG,
3044 "DPP: Local policy does not allow Configurator role");
3045 goto not_compatible;
3046 }
3047 wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
3048 auth->configurator = 1;
3049 break;
3050 case DPP_CAPAB_CONFIGURATOR:
3051 if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) {
3052 wpa_printf(MSG_DEBUG,
3053 "DPP: Local policy does not allow Enrollee role");
3054 goto not_compatible;
3055 }
3056 wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
3057 auth->configurator = 0;
3058 break;
Roshan Pius3a1667e2018-07-03 15:17:14 -07003059 case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE:
3060 if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) {
3061 wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
3062 auth->configurator = 0;
3063 } else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) {
3064 wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
3065 auth->configurator = 1;
3066 } else {
3067 wpa_printf(MSG_DEBUG,
3068 "DPP: Local policy does not allow Configurator/Enrollee role");
3069 goto not_compatible;
3070 }
3071 break;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003072 default:
3073 wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities");
Roshan Pius3a1667e2018-07-03 15:17:14 -07003074 wpa_msg(auth->msg_ctx, MSG_INFO,
3075 DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x",
3076 auth->i_capab & DPP_CAPAB_ROLE_MASK);
3077 goto fail;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003078 }
3079
3080 auth->peer_protocol_key = pi;
3081 pi = NULL;
3082 if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) {
3083 char hex[SHA256_MAC_LEN * 2 + 1];
3084
3085 wpa_printf(MSG_DEBUG,
3086 "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time");
3087 if (dpp_auth_build_resp_status(auth,
3088 DPP_STATUS_RESPONSE_PENDING) < 0)
3089 goto fail;
3090 i_bootstrap = dpp_get_attr(attr_start, attr_len,
3091 DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
3092 &i_bootstrap_len);
3093 if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) {
3094 auth->response_pending = 1;
3095 os_memcpy(auth->waiting_pubkey_hash,
3096 i_bootstrap, i_bootstrap_len);
3097 wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap,
3098 i_bootstrap_len);
3099 } else {
3100 hex[0] = '\0';
3101 }
3102
3103 wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE
3104 "%s", hex);
3105 return auth;
3106 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07003107 if (dpp_auth_build_resp_ok(auth) < 0)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003108 goto fail;
3109
3110 return auth;
3111
3112not_compatible:
3113 wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
3114 "i-capab=0x%02x", auth->i_capab);
3115 if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)
3116 auth->configurator = 1;
3117 else
3118 auth->configurator = 0;
3119 auth->peer_protocol_key = pi;
3120 pi = NULL;
3121 if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0)
3122 goto fail;
3123
3124 auth->remove_on_tx_status = 1;
3125 return auth;
3126fail:
3127 bin_clear_free(unwrapped, unwrapped_len);
3128 EVP_PKEY_free(pi);
3129 EVP_PKEY_CTX_free(ctx);
3130 dpp_auth_deinit(auth);
3131 return NULL;
3132}
3133
3134
3135int dpp_notify_new_qr_code(struct dpp_authentication *auth,
3136 struct dpp_bootstrap_info *peer_bi)
3137{
3138 if (!auth || !auth->response_pending ||
3139 os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash,
3140 SHA256_MAC_LEN) != 0)
3141 return 0;
3142
3143 wpa_printf(MSG_DEBUG,
3144 "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with "
3145 MACSTR, MAC2STR(auth->peer_mac_addr));
3146 auth->peer_bi = peer_bi;
3147
Roshan Pius3a1667e2018-07-03 15:17:14 -07003148 if (dpp_auth_build_resp_ok(auth) < 0)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003149 return -1;
3150
3151 return 1;
3152}
3153
3154
Roshan Pius3a1667e2018-07-03 15:17:14 -07003155static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth,
3156 enum dpp_status_error status)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003157{
3158 struct wpabuf *msg;
3159 u8 i_auth[4 + DPP_MAX_HASH_LEN];
3160 size_t i_auth_len;
Roshan Pius3a1667e2018-07-03 15:17:14 -07003161 u8 r_nonce[4 + DPP_MAX_NONCE_LEN];
3162 size_t r_nonce_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003163 const u8 *addr[2];
3164 size_t len[2], attr_len;
3165 u8 *wrapped_i_auth;
Roshan Pius3a1667e2018-07-03 15:17:14 -07003166 u8 *wrapped_r_nonce;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003167 u8 *attr_start, *attr_end;
Roshan Pius3a1667e2018-07-03 15:17:14 -07003168 const u8 *r_pubkey_hash, *i_pubkey_hash;
3169#ifdef CONFIG_TESTING_OPTIONS
3170 u8 test_hash[SHA256_MAC_LEN];
3171#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003172
3173 wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation");
3174
3175 i_auth_len = 4 + auth->curve->hash_len;
Roshan Pius3a1667e2018-07-03 15:17:14 -07003176 r_nonce_len = 4 + auth->curve->nonce_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003177 /* Build DPP Authentication Confirmation frame attributes */
3178 attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
Roshan Pius3a1667e2018-07-03 15:17:14 -07003179 4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE;
3180#ifdef CONFIG_TESTING_OPTIONS
3181 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF)
3182 attr_len += 5;
3183#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003184 msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len);
3185 if (!msg)
3186 goto fail;
3187
3188 attr_start = wpabuf_put(msg, 0);
3189
Roshan Pius3a1667e2018-07-03 15:17:14 -07003190 r_pubkey_hash = auth->peer_bi->pubkey_hash;
3191 if (auth->own_bi)
3192 i_pubkey_hash = auth->own_bi->pubkey_hash;
3193 else
3194 i_pubkey_hash = NULL;
3195
3196#ifdef CONFIG_TESTING_OPTIONS
3197 if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) {
3198 wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
3199 goto skip_status;
3200 } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) {
3201 wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
3202 status = 254;
3203 }
3204#endif /* CONFIG_TESTING_OPTIONS */
3205
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003206 /* DPP Status */
Roshan Pius3a1667e2018-07-03 15:17:14 -07003207 dpp_build_attr_status(msg, status);
3208
3209#ifdef CONFIG_TESTING_OPTIONS
3210skip_status:
3211 if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
3212 wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
3213 r_pubkey_hash = NULL;
3214 } else if (dpp_test ==
3215 DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
3216 wpa_printf(MSG_INFO,
3217 "DPP: TESTING - invalid R-Bootstrap Key Hash");
3218 os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
3219 test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
3220 r_pubkey_hash = test_hash;
3221 } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
3222 wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
3223 i_pubkey_hash = NULL;
3224 } else if (dpp_test ==
3225 DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
3226 wpa_printf(MSG_INFO,
3227 "DPP: TESTING - invalid I-Bootstrap Key Hash");
3228 if (i_pubkey_hash)
3229 os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
3230 else
3231 os_memset(test_hash, 0, SHA256_MAC_LEN);
3232 test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
3233 i_pubkey_hash = test_hash;
3234 }
3235#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003236
3237 /* Responder Bootstrapping Key Hash */
Roshan Pius3a1667e2018-07-03 15:17:14 -07003238 dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003239
Roshan Pius3a1667e2018-07-03 15:17:14 -07003240 /* Initiator Bootstrapping Key Hash (mutual authentication) */
3241 dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
3242
3243#ifdef CONFIG_TESTING_OPTIONS
3244 if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF)
3245 goto skip_wrapped_data;
3246 if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
3247 i_auth_len = 0;
3248#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003249
3250 attr_end = wpabuf_put(msg, 0);
3251
3252 /* OUI, OUI type, Crypto Suite, DPP frame type */
3253 addr[0] = wpabuf_head_u8(msg) + 2;
3254 len[0] = 3 + 1 + 1 + 1;
3255 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
3256
3257 /* Attributes before Wrapped Data */
3258 addr[1] = attr_start;
3259 len[1] = attr_end - attr_start;
3260 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
3261
Roshan Pius3a1667e2018-07-03 15:17:14 -07003262 if (status == DPP_STATUS_OK) {
3263 /* I-auth wrapped with ke */
3264 wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
3265 wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE);
3266 wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
3267
3268#ifdef CONFIG_TESTING_OPTIONS
3269 if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
3270 goto skip_i_auth;
3271#endif /* CONFIG_TESTING_OPTIONS */
3272
3273 /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |]
3274 * 1) */
3275 WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
3276 WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
3277 if (dpp_gen_i_auth(auth, i_auth + 4) < 0)
3278 goto fail;
3279
3280#ifdef CONFIG_TESTING_OPTIONS
3281 if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) {
3282 wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch");
3283 i_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
3284 }
3285skip_i_auth:
3286#endif /* CONFIG_TESTING_OPTIONS */
3287 if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
3288 i_auth, i_auth_len,
3289 2, addr, len, wrapped_i_auth) < 0)
3290 goto fail;
3291 wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
3292 wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
3293 } else {
3294 /* R-nonce wrapped with k2 */
3295 wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
3296 wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE);
3297 wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE);
3298
3299 WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE);
3300 WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len);
3301 os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len);
3302
3303 if (aes_siv_encrypt(auth->k2, auth->curve->hash_len,
3304 r_nonce, r_nonce_len,
3305 2, addr, len, wrapped_r_nonce) < 0)
3306 goto fail;
3307 wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2",
3308 wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE);
3309 }
3310
3311#ifdef CONFIG_TESTING_OPTIONS
3312 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) {
3313 wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
3314 dpp_build_attr_status(msg, DPP_STATUS_OK);
3315 }
3316skip_wrapped_data:
3317#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003318
3319 wpa_hexdump_buf(MSG_DEBUG,
3320 "DPP: Authentication Confirmation frame attributes",
3321 msg);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003322 if (status == DPP_STATUS_OK)
3323 dpp_auth_success(auth);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003324
3325 return msg;
3326
3327fail:
Roshan Pius3a1667e2018-07-03 15:17:14 -07003328 wpabuf_free(msg);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003329 return NULL;
3330}
3331
3332
3333static void
3334dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
3335 const u8 *attr_start, size_t attr_len,
3336 const u8 *wrapped_data, u16 wrapped_data_len,
3337 enum dpp_status_error status)
3338{
3339 const u8 *addr[2];
3340 size_t len[2];
3341 u8 *unwrapped = NULL;
3342 size_t unwrapped_len = 0;
3343 const u8 *i_nonce, *r_capab;
3344 u16 i_nonce_len, r_capab_len;
3345
3346 if (status == DPP_STATUS_NOT_COMPATIBLE) {
3347 wpa_printf(MSG_DEBUG,
3348 "DPP: Responder reported incompatible roles");
3349 } else if (status == DPP_STATUS_RESPONSE_PENDING) {
3350 wpa_printf(MSG_DEBUG,
3351 "DPP: Responder reported more time needed");
3352 } else {
3353 wpa_printf(MSG_DEBUG,
3354 "DPP: Responder reported failure (status %d)",
3355 status);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003356 dpp_auth_fail(auth, "Responder reported failure");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003357 return;
3358 }
3359
3360 addr[0] = hdr;
3361 len[0] = DPP_HDR_LEN;
3362 addr[1] = attr_start;
3363 len[1] = attr_len;
3364 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
3365 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
3366 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
3367 wrapped_data, wrapped_data_len);
3368 unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
3369 unwrapped = os_malloc(unwrapped_len);
3370 if (!unwrapped)
3371 goto fail;
3372 if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
3373 wrapped_data, wrapped_data_len,
3374 2, addr, len, unwrapped) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003375 dpp_auth_fail(auth, "AES-SIV decryption failed");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003376 goto fail;
3377 }
3378 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
3379 unwrapped, unwrapped_len);
3380
3381 if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003382 dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003383 goto fail;
3384 }
3385
3386 i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
3387 &i_nonce_len);
3388 if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003389 dpp_auth_fail(auth, "Missing or invalid I-nonce");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003390 goto fail;
3391 }
3392 wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
3393 if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003394 dpp_auth_fail(auth, "I-nonce mismatch");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003395 goto fail;
3396 }
3397
3398 r_capab = dpp_get_attr(unwrapped, unwrapped_len,
3399 DPP_ATTR_R_CAPABILITIES,
3400 &r_capab_len);
3401 if (!r_capab || r_capab_len < 1) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003402 dpp_auth_fail(auth, "Missing or invalid R-capabilities");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003403 goto fail;
3404 }
3405 auth->r_capab = r_capab[0];
3406 wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
3407 if (status == DPP_STATUS_NOT_COMPATIBLE) {
3408 wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
3409 "r-capab=0x%02x", auth->r_capab);
3410 } else if (status == DPP_STATUS_RESPONSE_PENDING) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003411 u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
3412
3413 if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
3414 (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
3415 wpa_msg(auth->msg_ctx, MSG_INFO,
3416 DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x",
3417 role);
3418 } else {
3419 wpa_printf(MSG_DEBUG,
3420 "DPP: Continue waiting for full DPP Authentication Response");
3421 wpa_msg(auth->msg_ctx, MSG_INFO,
3422 DPP_EVENT_RESPONSE_PENDING "%s",
3423 auth->tmp_own_bi ? auth->tmp_own_bi->uri : "");
3424 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003425 }
3426fail:
3427 bin_clear_free(unwrapped, unwrapped_len);
3428}
3429
3430
3431struct wpabuf *
3432dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
3433 const u8 *attr_start, size_t attr_len)
3434{
3435 EVP_PKEY *pr;
3436 EVP_PKEY_CTX *ctx = NULL;
3437 size_t secret_len;
3438 const u8 *addr[2];
3439 size_t len[2];
3440 u8 *unwrapped = NULL, *unwrapped2 = NULL;
3441 size_t unwrapped_len = 0, unwrapped2_len = 0;
3442 const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto,
3443 *r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth;
3444 u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
3445 r_proto_len, r_nonce_len, i_nonce_len, r_capab_len,
3446 wrapped2_len, r_auth_len;
3447 u8 r_auth2[DPP_MAX_HASH_LEN];
Roshan Pius3a1667e2018-07-03 15:17:14 -07003448 u8 role;
3449
3450#ifdef CONFIG_TESTING_OPTIONS
3451 if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) {
3452 wpa_printf(MSG_INFO,
3453 "DPP: TESTING - stop at Authentication Response");
3454 return NULL;
3455 }
3456#endif /* CONFIG_TESTING_OPTIONS */
3457
3458 if (!auth->initiator) {
3459 dpp_auth_fail(auth, "Unexpected Authentication Response");
3460 return NULL;
3461 }
3462
3463 auth->waiting_auth_resp = 0;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003464
3465 wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
3466 &wrapped_data_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003467 if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
3468 dpp_auth_fail(auth,
3469 "Missing or invalid required Wrapped Data attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003470 return NULL;
3471 }
3472 wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
3473 wrapped_data, wrapped_data_len);
3474
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003475 attr_len = wrapped_data - 4 - attr_start;
3476
3477 r_bootstrap = dpp_get_attr(attr_start, attr_len,
3478 DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
3479 &r_bootstrap_len);
3480 if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003481 dpp_auth_fail(auth,
3482 "Missing or invalid required Responder Bootstrapping Key Hash attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003483 return NULL;
3484 }
3485 wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
3486 r_bootstrap, r_bootstrap_len);
3487 if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash,
3488 SHA256_MAC_LEN) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003489 dpp_auth_fail(auth,
3490 "Unexpected Responder Bootstrapping Key Hash value");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003491 wpa_hexdump(MSG_DEBUG,
3492 "DPP: Expected Responder Bootstrapping Key Hash",
3493 auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
3494 return NULL;
3495 }
3496
3497 i_bootstrap = dpp_get_attr(attr_start, attr_len,
3498 DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
3499 &i_bootstrap_len);
3500 if (i_bootstrap) {
3501 if (i_bootstrap_len != SHA256_MAC_LEN) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003502 dpp_auth_fail(auth,
3503 "Invalid Initiator Bootstrapping Key Hash attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003504 return NULL;
3505 }
3506 wpa_hexdump(MSG_MSGDUMP,
3507 "DPP: Initiator Bootstrapping Key Hash",
3508 i_bootstrap, i_bootstrap_len);
3509 if (!auth->own_bi ||
3510 os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash,
3511 SHA256_MAC_LEN) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003512 dpp_auth_fail(auth,
3513 "Initiator Bootstrapping Key Hash attribute did not match");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003514 return NULL;
3515 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07003516 } else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) {
3517 /* PKEX bootstrapping mandates use of mutual authentication */
3518 dpp_auth_fail(auth,
3519 "Missing Initiator Bootstrapping Key Hash attribute");
3520 return NULL;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003521 }
3522
3523 status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
3524 &status_len);
3525 if (!status || status_len < 1) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003526 dpp_auth_fail(auth,
3527 "Missing or invalid required DPP Status attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003528 return NULL;
3529 }
3530 wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
3531 auth->auth_resp_status = status[0];
3532 if (status[0] != DPP_STATUS_OK) {
3533 dpp_auth_resp_rx_status(auth, hdr, attr_start,
3534 attr_len, wrapped_data,
3535 wrapped_data_len, status[0]);
3536 return NULL;
3537 }
3538
Roshan Pius3a1667e2018-07-03 15:17:14 -07003539 if (!i_bootstrap && auth->own_bi) {
3540 wpa_printf(MSG_DEBUG,
3541 "DPP: Responder decided not to use mutual authentication");
3542 auth->own_bi = NULL;
3543 }
3544
3545 wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_DIRECTION "mutual=%d",
3546 auth->own_bi != NULL);
3547
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003548 r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
3549 &r_proto_len);
3550 if (!r_proto) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003551 dpp_auth_fail(auth,
3552 "Missing required Responder Protocol Key attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003553 return NULL;
3554 }
3555 wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
3556 r_proto, r_proto_len);
3557
3558 /* N = pI * PR */
3559 pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len);
3560 if (!pr) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003561 dpp_auth_fail(auth, "Invalid Responder Protocol Key");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003562 return NULL;
3563 }
3564 dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
3565
3566 ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
3567 if (!ctx ||
3568 EVP_PKEY_derive_init(ctx) != 1 ||
3569 EVP_PKEY_derive_set_peer(ctx, pr) != 1 ||
3570 EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
3571 secret_len > DPP_MAX_SHARED_SECRET_LEN ||
3572 EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) {
3573 wpa_printf(MSG_ERROR,
3574 "DPP: Failed to derive ECDH shared secret: %s",
3575 ERR_error_string(ERR_get_error(), NULL));
Roshan Pius3a1667e2018-07-03 15:17:14 -07003576 dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003577 goto fail;
3578 }
3579 EVP_PKEY_CTX_free(ctx);
3580 ctx = NULL;
3581 auth->peer_protocol_key = pr;
3582 pr = NULL;
3583
3584 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
3585 auth->Nx, auth->secret_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003586 auth->Nx_len = auth->secret_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003587
3588 if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
3589 auth->curve->hash_len) < 0)
3590 goto fail;
3591
3592 addr[0] = hdr;
3593 len[0] = DPP_HDR_LEN;
3594 addr[1] = attr_start;
3595 len[1] = attr_len;
3596 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
3597 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
3598 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
3599 wrapped_data, wrapped_data_len);
3600 unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
3601 unwrapped = os_malloc(unwrapped_len);
3602 if (!unwrapped)
3603 goto fail;
3604 if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
3605 wrapped_data, wrapped_data_len,
3606 2, addr, len, unwrapped) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003607 dpp_auth_fail(auth, "AES-SIV decryption failed");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003608 goto fail;
3609 }
3610 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
3611 unwrapped, unwrapped_len);
3612
3613 if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003614 dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003615 goto fail;
3616 }
3617
3618 r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
3619 &r_nonce_len);
3620 if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003621 dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003622 goto fail;
3623 }
3624 wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
3625 os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
3626
3627 i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
3628 &i_nonce_len);
3629 if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003630 dpp_auth_fail(auth, "Missing or invalid I-nonce");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003631 goto fail;
3632 }
3633 wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
3634 if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003635 dpp_auth_fail(auth, "I-nonce mismatch");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003636 goto fail;
3637 }
3638
3639 if (auth->own_bi && auth->peer_bi) {
3640 /* Mutual authentication */
3641 if (dpp_auth_derive_l_initiator(auth) < 0)
3642 goto fail;
3643 }
3644
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003645 r_capab = dpp_get_attr(unwrapped, unwrapped_len,
3646 DPP_ATTR_R_CAPABILITIES,
3647 &r_capab_len);
3648 if (!r_capab || r_capab_len < 1) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003649 dpp_auth_fail(auth, "Missing or invalid R-capabilities");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003650 goto fail;
3651 }
3652 auth->r_capab = r_capab[0];
3653 wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003654 role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
3655 if ((auth->allowed_roles ==
3656 (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) &&
3657 (role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) {
3658 /* Peer selected its role, so move from "either role" to the
3659 * role that is compatible with peer's selection. */
3660 auth->configurator = role == DPP_CAPAB_ENROLLEE;
3661 wpa_printf(MSG_DEBUG, "DPP: Acting as %s",
3662 auth->configurator ? "Configurator" : "Enrollee");
3663 } else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
3664 (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003665 wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection");
Roshan Pius3a1667e2018-07-03 15:17:14 -07003666 wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
3667 "Unexpected role in R-capabilities 0x%02x",
3668 role);
3669 if (role != DPP_CAPAB_ENROLLEE &&
3670 role != DPP_CAPAB_CONFIGURATOR)
3671 goto fail;
3672 bin_clear_free(unwrapped, unwrapped_len);
3673 auth->remove_on_tx_status = 1;
3674 return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003675 }
3676
3677 wrapped2 = dpp_get_attr(unwrapped, unwrapped_len,
3678 DPP_ATTR_WRAPPED_DATA, &wrapped2_len);
3679 if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003680 dpp_auth_fail(auth,
3681 "Missing or invalid Secondary Wrapped Data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003682 goto fail;
3683 }
3684
3685 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
3686 wrapped2, wrapped2_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003687
3688 if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
3689 goto fail;
3690
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003691 unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE;
3692 unwrapped2 = os_malloc(unwrapped2_len);
3693 if (!unwrapped2)
3694 goto fail;
3695 if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
3696 wrapped2, wrapped2_len,
3697 0, NULL, NULL, unwrapped2) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003698 dpp_auth_fail(auth, "AES-SIV decryption failed");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003699 goto fail;
3700 }
3701 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
3702 unwrapped2, unwrapped2_len);
3703
3704 if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003705 dpp_auth_fail(auth,
3706 "Invalid attribute in secondary unwrapped data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003707 goto fail;
3708 }
3709
3710 r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG,
3711 &r_auth_len);
3712 if (!r_auth || r_auth_len != auth->curve->hash_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003713 dpp_auth_fail(auth,
3714 "Missing or invalid Responder Authenticating Tag");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003715 goto fail;
3716 }
3717 wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag",
3718 r_auth, r_auth_len);
3719 /* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
3720 if (dpp_gen_r_auth(auth, r_auth2) < 0)
3721 goto fail;
3722 wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag",
3723 r_auth2, r_auth_len);
3724 if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003725 dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag");
3726 bin_clear_free(unwrapped, unwrapped_len);
3727 bin_clear_free(unwrapped2, unwrapped2_len);
3728 auth->remove_on_tx_status = 1;
3729 return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003730 }
3731
3732 bin_clear_free(unwrapped, unwrapped_len);
3733 bin_clear_free(unwrapped2, unwrapped2_len);
3734
Roshan Pius3a1667e2018-07-03 15:17:14 -07003735#ifdef CONFIG_TESTING_OPTIONS
3736 if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) {
3737 wpa_printf(MSG_INFO,
3738 "DPP: TESTING - Authentication Response in place of Confirm");
3739 if (dpp_auth_build_resp_ok(auth) < 0)
3740 return NULL;
3741 return wpabuf_dup(auth->resp_msg);
3742 }
3743#endif /* CONFIG_TESTING_OPTIONS */
3744
3745 return dpp_auth_build_conf(auth, DPP_STATUS_OK);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003746
3747fail:
3748 bin_clear_free(unwrapped, unwrapped_len);
3749 bin_clear_free(unwrapped2, unwrapped2_len);
3750 EVP_PKEY_free(pr);
3751 EVP_PKEY_CTX_free(ctx);
3752 return NULL;
3753}
3754
3755
Roshan Pius3a1667e2018-07-03 15:17:14 -07003756static int dpp_auth_conf_rx_failure(struct dpp_authentication *auth,
3757 const u8 *hdr,
3758 const u8 *attr_start, size_t attr_len,
3759 const u8 *wrapped_data,
3760 u16 wrapped_data_len,
3761 enum dpp_status_error status)
3762{
3763 const u8 *addr[2];
3764 size_t len[2];
3765 u8 *unwrapped = NULL;
3766 size_t unwrapped_len = 0;
3767 const u8 *r_nonce;
3768 u16 r_nonce_len;
3769
3770 /* Authentication Confirm failure cases are expected to include
3771 * {R-nonce}k2 in the Wrapped Data attribute. */
3772
3773 addr[0] = hdr;
3774 len[0] = DPP_HDR_LEN;
3775 addr[1] = attr_start;
3776 len[1] = attr_len;
3777 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
3778 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
3779 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
3780 wrapped_data, wrapped_data_len);
3781 unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
3782 unwrapped = os_malloc(unwrapped_len);
3783 if (!unwrapped) {
3784 dpp_auth_fail(auth, "Authentication failed");
3785 goto fail;
3786 }
3787 if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
3788 wrapped_data, wrapped_data_len,
3789 2, addr, len, unwrapped) < 0) {
3790 dpp_auth_fail(auth, "AES-SIV decryption failed");
3791 goto fail;
3792 }
3793 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
3794 unwrapped, unwrapped_len);
3795
3796 if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
3797 dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
3798 goto fail;
3799 }
3800
3801 r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
3802 &r_nonce_len);
3803 if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
3804 dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
3805 goto fail;
3806 }
3807 if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) {
3808 wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce",
3809 r_nonce, r_nonce_len);
3810 wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce",
3811 auth->r_nonce, r_nonce_len);
3812 dpp_auth_fail(auth, "R-nonce mismatch");
3813 goto fail;
3814 }
3815
3816 if (status == DPP_STATUS_NOT_COMPATIBLE)
3817 dpp_auth_fail(auth, "Peer reported incompatible R-capab role");
3818 else if (status == DPP_STATUS_AUTH_FAILURE)
3819 dpp_auth_fail(auth, "Peer reported authentication failure)");
3820
3821fail:
3822 bin_clear_free(unwrapped, unwrapped_len);
3823 return -1;
3824}
3825
3826
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003827int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
3828 const u8 *attr_start, size_t attr_len)
3829{
3830 const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth;
3831 u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
3832 i_auth_len;
3833 const u8 *addr[2];
3834 size_t len[2];
3835 u8 *unwrapped = NULL;
3836 size_t unwrapped_len = 0;
3837 u8 i_auth2[DPP_MAX_HASH_LEN];
3838
Roshan Pius3a1667e2018-07-03 15:17:14 -07003839#ifdef CONFIG_TESTING_OPTIONS
3840 if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
3841 wpa_printf(MSG_INFO,
3842 "DPP: TESTING - stop at Authentication Confirm");
3843 return -1;
3844 }
3845#endif /* CONFIG_TESTING_OPTIONS */
3846
3847 if (auth->initiator) {
3848 dpp_auth_fail(auth, "Unexpected Authentication Confirm");
3849 return -1;
3850 }
3851
3852 auth->waiting_auth_conf = 0;
3853
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003854 wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
3855 &wrapped_data_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003856 if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
3857 dpp_auth_fail(auth,
3858 "Missing or invalid required Wrapped Data attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003859 return -1;
3860 }
3861 wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
3862 wrapped_data, wrapped_data_len);
3863
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003864 attr_len = wrapped_data - 4 - attr_start;
3865
3866 r_bootstrap = dpp_get_attr(attr_start, attr_len,
3867 DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
3868 &r_bootstrap_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003869 if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
3870 dpp_auth_fail(auth,
3871 "Missing or invalid required Responder Bootstrapping Key Hash attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003872 return -1;
3873 }
3874 wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
3875 r_bootstrap, r_bootstrap_len);
3876 if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash,
3877 SHA256_MAC_LEN) != 0) {
3878 wpa_hexdump(MSG_DEBUG,
3879 "DPP: Expected Responder Bootstrapping Key Hash",
3880 auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003881 dpp_auth_fail(auth,
3882 "Responder Bootstrapping Key Hash mismatch");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003883 return -1;
3884 }
3885
3886 i_bootstrap = dpp_get_attr(attr_start, attr_len,
3887 DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
3888 &i_bootstrap_len);
3889 if (i_bootstrap) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003890 if (i_bootstrap_len != SHA256_MAC_LEN) {
3891 dpp_auth_fail(auth,
3892 "Invalid Initiator Bootstrapping Key Hash attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003893 return -1;
3894 }
3895 wpa_hexdump(MSG_MSGDUMP,
3896 "DPP: Initiator Bootstrapping Key Hash",
3897 i_bootstrap, i_bootstrap_len);
3898 if (!auth->peer_bi ||
3899 os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash,
3900 SHA256_MAC_LEN) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003901 dpp_auth_fail(auth,
3902 "Initiator Bootstrapping Key Hash mismatch");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003903 return -1;
3904 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07003905 } else if (auth->own_bi && auth->peer_bi) {
3906 /* Mutual authentication and peer did not include its
3907 * Bootstrapping Key Hash attribute. */
3908 dpp_auth_fail(auth,
3909 "Missing Initiator Bootstrapping Key Hash attribute");
3910 return -1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003911 }
3912
3913 status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
3914 &status_len);
3915 if (!status || status_len < 1) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003916 dpp_auth_fail(auth,
3917 "Missing or invalid required DPP Status attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003918 return -1;
3919 }
3920 wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003921 if (status[0] == DPP_STATUS_NOT_COMPATIBLE ||
3922 status[0] == DPP_STATUS_AUTH_FAILURE)
3923 return dpp_auth_conf_rx_failure(auth, hdr, attr_start,
3924 attr_len, wrapped_data,
3925 wrapped_data_len, status[0]);
3926
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003927 if (status[0] != DPP_STATUS_OK) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003928 dpp_auth_fail(auth, "Authentication failed");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003929 return -1;
3930 }
3931
3932 addr[0] = hdr;
3933 len[0] = DPP_HDR_LEN;
3934 addr[1] = attr_start;
3935 len[1] = attr_len;
3936 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
3937 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
3938 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
3939 wrapped_data, wrapped_data_len);
3940 unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
3941 unwrapped = os_malloc(unwrapped_len);
3942 if (!unwrapped)
3943 return -1;
3944 if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
3945 wrapped_data, wrapped_data_len,
3946 2, addr, len, unwrapped) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003947 dpp_auth_fail(auth, "AES-SIV decryption failed");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003948 goto fail;
3949 }
3950 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
3951 unwrapped, unwrapped_len);
3952
3953 if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003954 dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003955 goto fail;
3956 }
3957
3958 i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
3959 &i_auth_len);
3960 if (!i_auth || i_auth_len != auth->curve->hash_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003961 dpp_auth_fail(auth,
3962 "Missing or invalid Initiator Authenticating Tag");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003963 goto fail;
3964 }
3965 wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag",
3966 i_auth, i_auth_len);
3967 /* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
3968 if (dpp_gen_i_auth(auth, i_auth2) < 0)
3969 goto fail;
3970 wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag",
3971 i_auth2, i_auth_len);
3972 if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003973 dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003974 goto fail;
3975 }
3976
3977 bin_clear_free(unwrapped, unwrapped_len);
3978 dpp_auth_success(auth);
3979 return 0;
3980fail:
3981 bin_clear_free(unwrapped, unwrapped_len);
3982 return -1;
3983}
3984
3985
3986void dpp_configuration_free(struct dpp_configuration *conf)
3987{
3988 if (!conf)
3989 return;
3990 str_clear_free(conf->passphrase);
Hai Shalomce48b4a2018-09-05 11:41:35 -07003991 os_free(conf->group_id);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003992 bin_clear_free(conf, sizeof(*conf));
3993}
3994
3995
3996void dpp_auth_deinit(struct dpp_authentication *auth)
3997{
3998 if (!auth)
3999 return;
4000 dpp_configuration_free(auth->conf_ap);
4001 dpp_configuration_free(auth->conf_sta);
4002 EVP_PKEY_free(auth->own_protocol_key);
4003 EVP_PKEY_free(auth->peer_protocol_key);
4004 wpabuf_free(auth->req_msg);
4005 wpabuf_free(auth->resp_msg);
4006 wpabuf_free(auth->conf_req);
4007 os_free(auth->connector);
4008 wpabuf_free(auth->net_access_key);
4009 wpabuf_free(auth->c_sign_key);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004010 dpp_bootstrap_info_free(auth->tmp_own_bi);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004011#ifdef CONFIG_TESTING_OPTIONS
4012 os_free(auth->config_obj_override);
4013 os_free(auth->discovery_override);
4014 os_free(auth->groups_override);
4015#endif /* CONFIG_TESTING_OPTIONS */
4016 bin_clear_free(auth, sizeof(*auth));
4017}
4018
4019
4020static struct wpabuf *
4021dpp_build_conf_start(struct dpp_authentication *auth,
4022 struct dpp_configuration *conf, size_t tailroom)
4023{
4024 struct wpabuf *buf;
4025 char ssid[6 * sizeof(conf->ssid) + 1];
4026
4027#ifdef CONFIG_TESTING_OPTIONS
4028 if (auth->discovery_override)
4029 tailroom += os_strlen(auth->discovery_override);
4030#endif /* CONFIG_TESTING_OPTIONS */
4031
4032 buf = wpabuf_alloc(200 + tailroom);
4033 if (!buf)
4034 return NULL;
4035 wpabuf_put_str(buf, "{\"wi-fi_tech\":\"infra\",\"discovery\":");
4036#ifdef CONFIG_TESTING_OPTIONS
4037 if (auth->discovery_override) {
4038 wpa_printf(MSG_DEBUG, "DPP: TESTING - discovery override: '%s'",
4039 auth->discovery_override);
4040 wpabuf_put_str(buf, auth->discovery_override);
4041 wpabuf_put_u8(buf, ',');
4042 return buf;
4043 }
4044#endif /* CONFIG_TESTING_OPTIONS */
4045 wpabuf_put_str(buf, "{\"ssid\":\"");
4046 json_escape_string(ssid, sizeof(ssid),
4047 (const char *) conf->ssid, conf->ssid_len);
4048 wpabuf_put_str(buf, ssid);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004049 wpabuf_put_str(buf, "\"},");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004050
4051 return buf;
4052}
4053
4054
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004055static int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
4056 const char *kid, const struct dpp_curve_params *curve)
4057{
4058 struct wpabuf *pub;
4059 const u8 *pos;
4060 char *x = NULL, *y = NULL;
4061 int ret = -1;
4062
4063 pub = dpp_get_pubkey_point(key, 0);
4064 if (!pub)
4065 goto fail;
4066 pos = wpabuf_head(pub);
4067 x = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0);
4068 pos += curve->prime_len;
4069 y = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0);
4070 if (!x || !y)
4071 goto fail;
4072
4073 wpabuf_put_str(buf, "\"");
4074 wpabuf_put_str(buf, name);
4075 wpabuf_put_str(buf, "\":{\"kty\":\"EC\",\"crv\":\"");
4076 wpabuf_put_str(buf, curve->jwk_crv);
4077 wpabuf_put_str(buf, "\",\"x\":\"");
4078 wpabuf_put_str(buf, x);
4079 wpabuf_put_str(buf, "\",\"y\":\"");
4080 wpabuf_put_str(buf, y);
4081 if (kid) {
4082 wpabuf_put_str(buf, "\",\"kid\":\"");
4083 wpabuf_put_str(buf, kid);
4084 }
4085 wpabuf_put_str(buf, "\"}");
4086 ret = 0;
4087fail:
4088 wpabuf_free(pub);
4089 os_free(x);
4090 os_free(y);
4091 return ret;
4092}
4093
4094
4095static struct wpabuf *
4096dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap,
4097 struct dpp_configuration *conf)
4098{
4099 struct wpabuf *buf = NULL;
4100 char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL;
4101 size_t tailroom;
4102 const struct dpp_curve_params *curve;
4103 char jws_prot_hdr[100];
4104 size_t signed1_len, signed2_len, signed3_len;
4105 struct wpabuf *dppcon = NULL;
4106 unsigned char *signature = NULL;
4107 const unsigned char *p;
4108 size_t signature_len;
4109 EVP_MD_CTX *md_ctx = NULL;
4110 ECDSA_SIG *sig = NULL;
4111 char *dot = ".";
4112 const EVP_MD *sign_md;
4113 const BIGNUM *r, *s;
4114 size_t extra_len = 1000;
4115
4116 if (!auth->conf) {
4117 wpa_printf(MSG_INFO,
4118 "DPP: No configurator specified - cannot generate DPP config object");
4119 goto fail;
4120 }
4121 curve = auth->conf->curve;
4122 if (curve->hash_len == SHA256_MAC_LEN) {
4123 sign_md = EVP_sha256();
4124 } else if (curve->hash_len == SHA384_MAC_LEN) {
4125 sign_md = EVP_sha384();
4126 } else if (curve->hash_len == SHA512_MAC_LEN) {
4127 sign_md = EVP_sha512();
4128 } else {
4129 wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
4130 goto fail;
4131 }
4132
4133#ifdef CONFIG_TESTING_OPTIONS
4134 if (auth->groups_override)
4135 extra_len += os_strlen(auth->groups_override);
4136#endif /* CONFIG_TESTING_OPTIONS */
4137
Hai Shalomce48b4a2018-09-05 11:41:35 -07004138 if (conf->group_id)
4139 extra_len += os_strlen(conf->group_id);
4140
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004141 /* Connector (JSON dppCon object) */
4142 dppcon = wpabuf_alloc(extra_len + 2 * auth->curve->prime_len * 4 / 3);
4143 if (!dppcon)
4144 goto fail;
4145#ifdef CONFIG_TESTING_OPTIONS
4146 if (auth->groups_override) {
4147 wpabuf_put_u8(dppcon, '{');
4148 if (auth->groups_override) {
4149 wpa_printf(MSG_DEBUG,
4150 "DPP: TESTING - groups override: '%s'",
4151 auth->groups_override);
4152 wpabuf_put_str(dppcon, "\"groups\":");
4153 wpabuf_put_str(dppcon, auth->groups_override);
4154 wpabuf_put_u8(dppcon, ',');
4155 }
4156 goto skip_groups;
4157 }
4158#endif /* CONFIG_TESTING_OPTIONS */
Hai Shalomce48b4a2018-09-05 11:41:35 -07004159 wpabuf_printf(dppcon, "{\"groups\":[{\"groupId\":\"%s\",",
4160 conf->group_id ? conf->group_id : "*");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004161 wpabuf_printf(dppcon, "\"netRole\":\"%s\"}],", ap ? "ap" : "sta");
4162#ifdef CONFIG_TESTING_OPTIONS
4163skip_groups:
4164#endif /* CONFIG_TESTING_OPTIONS */
4165 if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL,
4166 auth->curve) < 0) {
4167 wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
4168 goto fail;
4169 }
4170 if (conf->netaccesskey_expiry) {
4171 struct os_tm tm;
4172
4173 if (os_gmtime(conf->netaccesskey_expiry, &tm) < 0) {
4174 wpa_printf(MSG_DEBUG,
4175 "DPP: Failed to generate expiry string");
4176 goto fail;
4177 }
4178 wpabuf_printf(dppcon,
4179 ",\"expiry\":\"%04u-%02u-%02uT%02u:%02u:%02uZ\"",
4180 tm.year, tm.month, tm.day,
4181 tm.hour, tm.min, tm.sec);
4182 }
4183 wpabuf_put_u8(dppcon, '}');
4184 wpa_printf(MSG_DEBUG, "DPP: dppCon: %s",
4185 (const char *) wpabuf_head(dppcon));
4186
4187 os_snprintf(jws_prot_hdr, sizeof(jws_prot_hdr),
4188 "{\"typ\":\"dppCon\",\"kid\":\"%s\",\"alg\":\"%s\"}",
4189 auth->conf->kid, curve->jws_alg);
4190 signed1 = (char *) base64_url_encode((unsigned char *) jws_prot_hdr,
4191 os_strlen(jws_prot_hdr),
4192 &signed1_len, 0);
4193 signed2 = (char *) base64_url_encode(wpabuf_head(dppcon),
4194 wpabuf_len(dppcon),
4195 &signed2_len, 0);
4196 if (!signed1 || !signed2)
4197 goto fail;
4198
4199 md_ctx = EVP_MD_CTX_create();
4200 if (!md_ctx)
4201 goto fail;
4202
4203 ERR_clear_error();
4204 if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL,
4205 auth->conf->csign) != 1) {
4206 wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s",
4207 ERR_error_string(ERR_get_error(), NULL));
4208 goto fail;
4209 }
4210 if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 ||
4211 EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 ||
4212 EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) {
4213 wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s",
4214 ERR_error_string(ERR_get_error(), NULL));
4215 goto fail;
4216 }
4217 if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) {
4218 wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
4219 ERR_error_string(ERR_get_error(), NULL));
4220 goto fail;
4221 }
4222 signature = os_malloc(signature_len);
4223 if (!signature)
4224 goto fail;
4225 if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) {
4226 wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
4227 ERR_error_string(ERR_get_error(), NULL));
4228 goto fail;
4229 }
4230 wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)",
4231 signature, signature_len);
4232 /* Convert to raw coordinates r,s */
4233 p = signature;
4234 sig = d2i_ECDSA_SIG(NULL, &p, signature_len);
4235 if (!sig)
4236 goto fail;
4237 ECDSA_SIG_get0(sig, &r, &s);
4238 if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 ||
4239 dpp_bn2bin_pad(s, signature + curve->prime_len,
4240 curve->prime_len) < 0)
4241 goto fail;
4242 signature_len = 2 * curve->prime_len;
4243 wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)",
4244 signature, signature_len);
4245 signed3 = (char *) base64_url_encode(signature, signature_len,
4246 &signed3_len, 0);
4247 if (!signed3)
4248 goto fail;
4249
4250 tailroom = 1000;
4251 tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid);
4252 tailroom += signed1_len + signed2_len + signed3_len;
4253 buf = dpp_build_conf_start(auth, conf, tailroom);
4254 if (!buf)
Roshan Pius3a1667e2018-07-03 15:17:14 -07004255 goto fail;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004256
4257 wpabuf_put_str(buf, "\"cred\":{\"akm\":\"dpp\",\"signedConnector\":\"");
4258 wpabuf_put_str(buf, signed1);
4259 wpabuf_put_u8(buf, '.');
4260 wpabuf_put_str(buf, signed2);
4261 wpabuf_put_u8(buf, '.');
4262 wpabuf_put_str(buf, signed3);
4263 wpabuf_put_str(buf, "\",");
4264 if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid,
4265 curve) < 0) {
4266 wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK");
4267 goto fail;
4268 }
4269
4270 wpabuf_put_str(buf, "}}");
4271
4272 wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object",
4273 wpabuf_head(buf), wpabuf_len(buf));
4274
4275out:
4276 EVP_MD_CTX_destroy(md_ctx);
4277 ECDSA_SIG_free(sig);
4278 os_free(signed1);
4279 os_free(signed2);
4280 os_free(signed3);
4281 os_free(signature);
4282 wpabuf_free(dppcon);
4283 return buf;
4284fail:
4285 wpa_printf(MSG_DEBUG, "DPP: Failed to build configuration object");
4286 wpabuf_free(buf);
4287 buf = NULL;
4288 goto out;
4289}
4290
4291
4292static struct wpabuf *
4293dpp_build_conf_obj_legacy(struct dpp_authentication *auth, int ap,
4294 struct dpp_configuration *conf)
4295{
4296 struct wpabuf *buf;
4297
4298 buf = dpp_build_conf_start(auth, conf, 1000);
4299 if (!buf)
4300 return NULL;
4301
Roshan Pius3a1667e2018-07-03 15:17:14 -07004302 wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(conf->akm));
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004303 if (conf->passphrase) {
4304 char pass[63 * 6 + 1];
4305
4306 if (os_strlen(conf->passphrase) > 63) {
4307 wpabuf_free(buf);
4308 return NULL;
4309 }
4310
4311 json_escape_string(pass, sizeof(pass), conf->passphrase,
4312 os_strlen(conf->passphrase));
4313 wpabuf_put_str(buf, "\"pass\":\"");
4314 wpabuf_put_str(buf, pass);
4315 wpabuf_put_str(buf, "\"");
4316 } else {
4317 char psk[2 * sizeof(conf->psk) + 1];
4318
4319 wpa_snprintf_hex(psk, sizeof(psk),
4320 conf->psk, sizeof(conf->psk));
4321 wpabuf_put_str(buf, "\"psk_hex\":\"");
4322 wpabuf_put_str(buf, psk);
4323 wpabuf_put_str(buf, "\"");
4324 }
4325 wpabuf_put_str(buf, "}}");
4326
4327 wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)",
4328 wpabuf_head(buf), wpabuf_len(buf));
4329
4330 return buf;
4331}
4332
4333
4334static struct wpabuf *
4335dpp_build_conf_obj(struct dpp_authentication *auth, int ap)
4336{
4337 struct dpp_configuration *conf;
4338
4339#ifdef CONFIG_TESTING_OPTIONS
4340 if (auth->config_obj_override) {
4341 wpa_printf(MSG_DEBUG, "DPP: Testing - Config Object override");
4342 return wpabuf_alloc_copy(auth->config_obj_override,
4343 os_strlen(auth->config_obj_override));
4344 }
4345#endif /* CONFIG_TESTING_OPTIONS */
4346
4347 conf = ap ? auth->conf_ap : auth->conf_sta;
4348 if (!conf) {
4349 wpa_printf(MSG_DEBUG,
4350 "DPP: No configuration available for Enrollee(%s) - reject configuration request",
4351 ap ? "ap" : "sta");
4352 return NULL;
4353 }
4354
Roshan Pius3a1667e2018-07-03 15:17:14 -07004355 if (conf->akm == DPP_AKM_DPP)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004356 return dpp_build_conf_obj_dpp(auth, ap, conf);
4357 return dpp_build_conf_obj_legacy(auth, ap, conf);
4358}
4359
4360
4361static struct wpabuf *
4362dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
4363 u16 e_nonce_len, int ap)
4364{
4365 struct wpabuf *conf;
Roshan Pius3a1667e2018-07-03 15:17:14 -07004366 size_t clear_len, attr_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004367 struct wpabuf *clear = NULL, *msg = NULL;
4368 u8 *wrapped;
4369 const u8 *addr[1];
4370 size_t len[1];
4371 enum dpp_status_error status;
4372
4373 conf = dpp_build_conf_obj(auth, ap);
4374 if (conf) {
4375 wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
4376 wpabuf_head(conf), wpabuf_len(conf));
4377 }
4378 status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE;
4379
4380 /* { E-nonce, configurationObject}ke */
4381 clear_len = 4 + e_nonce_len;
4382 if (conf)
4383 clear_len += 4 + wpabuf_len(conf);
4384 clear = wpabuf_alloc(clear_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004385 attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE;
4386#ifdef CONFIG_TESTING_OPTIONS
4387 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP)
4388 attr_len += 5;
4389#endif /* CONFIG_TESTING_OPTIONS */
4390 msg = wpabuf_alloc(attr_len);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004391 if (!clear || !msg)
4392 goto fail;
4393
Roshan Pius3a1667e2018-07-03 15:17:14 -07004394#ifdef CONFIG_TESTING_OPTIONS
4395 if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_RESP) {
4396 wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce");
4397 goto skip_e_nonce;
4398 }
4399 if (dpp_test == DPP_TEST_E_NONCE_MISMATCH_CONF_RESP) {
4400 wpa_printf(MSG_INFO, "DPP: TESTING - E-nonce mismatch");
4401 wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
4402 wpabuf_put_le16(clear, e_nonce_len);
4403 wpabuf_put_data(clear, e_nonce, e_nonce_len - 1);
4404 wpabuf_put_u8(clear, e_nonce[e_nonce_len - 1] ^ 0x01);
4405 goto skip_e_nonce;
4406 }
4407 if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_RESP) {
4408 wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
4409 goto skip_wrapped_data;
4410 }
4411#endif /* CONFIG_TESTING_OPTIONS */
4412
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004413 /* E-nonce */
4414 wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
4415 wpabuf_put_le16(clear, e_nonce_len);
4416 wpabuf_put_data(clear, e_nonce, e_nonce_len);
4417
Roshan Pius3a1667e2018-07-03 15:17:14 -07004418#ifdef CONFIG_TESTING_OPTIONS
4419skip_e_nonce:
4420 if (dpp_test == DPP_TEST_NO_CONFIG_OBJ_CONF_RESP) {
4421 wpa_printf(MSG_INFO, "DPP: TESTING - Config Object");
4422 goto skip_config_obj;
4423 }
4424#endif /* CONFIG_TESTING_OPTIONS */
4425
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004426 if (conf) {
4427 wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ);
4428 wpabuf_put_le16(clear, wpabuf_len(conf));
4429 wpabuf_put_buf(clear, conf);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004430 }
4431
Roshan Pius3a1667e2018-07-03 15:17:14 -07004432#ifdef CONFIG_TESTING_OPTIONS
4433skip_config_obj:
4434 if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) {
4435 wpa_printf(MSG_INFO, "DPP: TESTING - Status");
4436 goto skip_status;
4437 }
4438 if (dpp_test == DPP_TEST_INVALID_STATUS_CONF_RESP) {
4439 wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
4440 status = 255;
4441 }
4442#endif /* CONFIG_TESTING_OPTIONS */
4443
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004444 /* DPP Status */
Roshan Pius3a1667e2018-07-03 15:17:14 -07004445 dpp_build_attr_status(msg, status);
4446
4447#ifdef CONFIG_TESTING_OPTIONS
4448skip_status:
4449#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004450
4451 addr[0] = wpabuf_head(msg);
4452 len[0] = wpabuf_len(msg);
4453 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
4454
4455 wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
4456 wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
4457 wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
4458
4459 wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
4460 if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
4461 wpabuf_head(clear), wpabuf_len(clear),
4462 1, addr, len, wrapped) < 0)
4463 goto fail;
4464 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
4465 wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004466
4467#ifdef CONFIG_TESTING_OPTIONS
4468 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) {
4469 wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
4470 dpp_build_attr_status(msg, DPP_STATUS_OK);
4471 }
4472skip_wrapped_data:
4473#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004474
4475 wpa_hexdump_buf(MSG_DEBUG,
4476 "DPP: Configuration Response attributes", msg);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004477out:
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004478 wpabuf_free(conf);
4479 wpabuf_free(clear);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004480
4481 return msg;
4482fail:
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004483 wpabuf_free(msg);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004484 msg = NULL;
4485 goto out;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004486}
4487
4488
4489struct wpabuf *
4490dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
4491 size_t attr_len)
4492{
4493 const u8 *wrapped_data, *e_nonce, *config_attr;
4494 u16 wrapped_data_len, e_nonce_len, config_attr_len;
4495 u8 *unwrapped = NULL;
4496 size_t unwrapped_len = 0;
4497 struct wpabuf *resp = NULL;
4498 struct json_token *root = NULL, *token;
4499 int ap;
4500
Roshan Pius3a1667e2018-07-03 15:17:14 -07004501#ifdef CONFIG_TESTING_OPTIONS
4502 if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) {
4503 wpa_printf(MSG_INFO,
4504 "DPP: TESTING - stop at Config Request");
4505 return NULL;
4506 }
4507#endif /* CONFIG_TESTING_OPTIONS */
4508
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004509 if (dpp_check_attrs(attr_start, attr_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004510 dpp_auth_fail(auth, "Invalid attribute in config request");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004511 return NULL;
4512 }
4513
4514 wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
4515 &wrapped_data_len);
4516 if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004517 dpp_auth_fail(auth,
4518 "Missing or invalid required Wrapped Data attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004519 return NULL;
4520 }
4521
4522 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
4523 wrapped_data, wrapped_data_len);
4524 unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
4525 unwrapped = os_malloc(unwrapped_len);
4526 if (!unwrapped)
4527 return NULL;
4528 if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
4529 wrapped_data, wrapped_data_len,
4530 0, NULL, NULL, unwrapped) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004531 dpp_auth_fail(auth, "AES-SIV decryption failed");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004532 goto fail;
4533 }
4534 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
4535 unwrapped, unwrapped_len);
4536
4537 if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004538 dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004539 goto fail;
4540 }
4541
4542 e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
4543 DPP_ATTR_ENROLLEE_NONCE,
4544 &e_nonce_len);
4545 if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004546 dpp_auth_fail(auth,
4547 "Missing or invalid Enrollee Nonce attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004548 goto fail;
4549 }
4550 wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
4551
4552 config_attr = dpp_get_attr(unwrapped, unwrapped_len,
4553 DPP_ATTR_CONFIG_ATTR_OBJ,
4554 &config_attr_len);
4555 if (!config_attr) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004556 dpp_auth_fail(auth,
4557 "Missing or invalid Config Attributes attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004558 goto fail;
4559 }
4560 wpa_hexdump_ascii(MSG_DEBUG, "DPP: Config Attributes",
4561 config_attr, config_attr_len);
4562
4563 root = json_parse((const char *) config_attr, config_attr_len);
4564 if (!root) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004565 dpp_auth_fail(auth, "Could not parse Config Attributes");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004566 goto fail;
4567 }
4568
4569 token = json_get_member(root, "name");
4570 if (!token || token->type != JSON_STRING) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004571 dpp_auth_fail(auth, "No Config Attributes - name");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004572 goto fail;
4573 }
4574 wpa_printf(MSG_DEBUG, "DPP: Enrollee name = '%s'", token->string);
4575
4576 token = json_get_member(root, "wi-fi_tech");
4577 if (!token || token->type != JSON_STRING) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004578 dpp_auth_fail(auth, "No Config Attributes - wi-fi_tech");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004579 goto fail;
4580 }
4581 wpa_printf(MSG_DEBUG, "DPP: wi-fi_tech = '%s'", token->string);
4582 if (os_strcmp(token->string, "infra") != 0) {
4583 wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech '%s'",
4584 token->string);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004585 dpp_auth_fail(auth, "Unsupported wi-fi_tech");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004586 goto fail;
4587 }
4588
4589 token = json_get_member(root, "netRole");
4590 if (!token || token->type != JSON_STRING) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004591 dpp_auth_fail(auth, "No Config Attributes - netRole");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004592 goto fail;
4593 }
4594 wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string);
4595 if (os_strcmp(token->string, "sta") == 0) {
4596 ap = 0;
4597 } else if (os_strcmp(token->string, "ap") == 0) {
4598 ap = 1;
4599 } else {
4600 wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'",
4601 token->string);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004602 dpp_auth_fail(auth, "Unsupported netRole");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004603 goto fail;
4604 }
4605
4606 resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, ap);
4607
4608fail:
4609 json_free(root);
4610 os_free(unwrapped);
4611 return resp;
4612}
4613
4614
4615static struct wpabuf *
4616dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve,
4617 const u8 *prot_hdr, u16 prot_hdr_len,
4618 const EVP_MD **ret_md)
4619{
4620 struct json_token *root, *token;
4621 struct wpabuf *kid = NULL;
4622
4623 root = json_parse((const char *) prot_hdr, prot_hdr_len);
4624 if (!root) {
4625 wpa_printf(MSG_DEBUG,
4626 "DPP: JSON parsing failed for JWS Protected Header");
4627 goto fail;
4628 }
4629
4630 if (root->type != JSON_OBJECT) {
4631 wpa_printf(MSG_DEBUG,
4632 "DPP: JWS Protected Header root is not an object");
4633 goto fail;
4634 }
4635
4636 token = json_get_member(root, "typ");
4637 if (!token || token->type != JSON_STRING) {
4638 wpa_printf(MSG_DEBUG, "DPP: No typ string value found");
4639 goto fail;
4640 }
4641 wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s",
4642 token->string);
4643 if (os_strcmp(token->string, "dppCon") != 0) {
4644 wpa_printf(MSG_DEBUG,
4645 "DPP: Unsupported JWS Protected Header typ=%s",
4646 token->string);
4647 goto fail;
4648 }
4649
4650 token = json_get_member(root, "alg");
4651 if (!token || token->type != JSON_STRING) {
4652 wpa_printf(MSG_DEBUG, "DPP: No alg string value found");
4653 goto fail;
4654 }
4655 wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s",
4656 token->string);
4657 if (os_strcmp(token->string, curve->jws_alg) != 0) {
4658 wpa_printf(MSG_DEBUG,
4659 "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)",
4660 token->string, curve->jws_alg);
4661 goto fail;
4662 }
4663 if (os_strcmp(token->string, "ES256") == 0 ||
4664 os_strcmp(token->string, "BS256") == 0)
4665 *ret_md = EVP_sha256();
4666 else if (os_strcmp(token->string, "ES384") == 0 ||
4667 os_strcmp(token->string, "BS384") == 0)
4668 *ret_md = EVP_sha384();
4669 else if (os_strcmp(token->string, "ES512") == 0 ||
4670 os_strcmp(token->string, "BS512") == 0)
4671 *ret_md = EVP_sha512();
4672 else
4673 *ret_md = NULL;
4674 if (!*ret_md) {
4675 wpa_printf(MSG_DEBUG,
4676 "DPP: Unsupported JWS Protected Header alg=%s",
4677 token->string);
4678 goto fail;
4679 }
4680
4681 kid = json_get_member_base64url(root, "kid");
4682 if (!kid) {
4683 wpa_printf(MSG_DEBUG, "DPP: No kid string value found");
4684 goto fail;
4685 }
4686 wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)",
4687 kid);
4688
4689fail:
4690 json_free(root);
4691 return kid;
4692}
4693
4694
4695static int dpp_parse_cred_legacy(struct dpp_authentication *auth,
4696 struct json_token *cred)
4697{
4698 struct json_token *pass, *psk_hex;
4699
4700 wpa_printf(MSG_DEBUG, "DPP: Legacy akm=psk credential");
4701
4702 pass = json_get_member(cred, "pass");
4703 psk_hex = json_get_member(cred, "psk_hex");
4704
4705 if (pass && pass->type == JSON_STRING) {
4706 size_t len = os_strlen(pass->string);
4707
4708 wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Legacy passphrase",
4709 pass->string, len);
4710 if (len < 8 || len > 63)
4711 return -1;
4712 os_strlcpy(auth->passphrase, pass->string,
4713 sizeof(auth->passphrase));
4714 } else if (psk_hex && psk_hex->type == JSON_STRING) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004715 if (auth->akm == DPP_AKM_SAE) {
4716 wpa_printf(MSG_DEBUG,
4717 "DPP: Unexpected psk_hex with akm=sae");
4718 return -1;
4719 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004720 if (os_strlen(psk_hex->string) != PMK_LEN * 2 ||
4721 hexstr2bin(psk_hex->string, auth->psk, PMK_LEN) < 0) {
4722 wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding");
4723 return -1;
4724 }
4725 wpa_hexdump_key(MSG_DEBUG, "DPP: Legacy PSK",
4726 auth->psk, PMK_LEN);
4727 auth->psk_set = 1;
4728 } else {
4729 wpa_printf(MSG_DEBUG, "DPP: No pass or psk_hex strings found");
4730 return -1;
4731 }
4732
Roshan Pius3a1667e2018-07-03 15:17:14 -07004733 if ((auth->akm == DPP_AKM_SAE || auth->akm == DPP_AKM_PSK_SAE) &&
4734 !auth->passphrase[0]) {
4735 wpa_printf(MSG_DEBUG, "DPP: No pass for sae found");
4736 return -1;
4737 }
4738
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004739 return 0;
4740}
4741
4742
4743static EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
4744 const struct dpp_curve_params **key_curve)
4745{
4746 struct json_token *token;
4747 const struct dpp_curve_params *curve;
4748 struct wpabuf *x = NULL, *y = NULL;
4749 EC_GROUP *group;
4750 EVP_PKEY *pkey = NULL;
4751
4752 token = json_get_member(jwk, "kty");
4753 if (!token || token->type != JSON_STRING) {
4754 wpa_printf(MSG_DEBUG, "DPP: No kty in JWK");
4755 goto fail;
4756 }
4757 if (os_strcmp(token->string, "EC") != 0) {
4758 wpa_printf(MSG_DEBUG, "DPP: Unexpected JWK kty '%s",
4759 token->string);
4760 goto fail;
4761 }
4762
4763 token = json_get_member(jwk, "crv");
4764 if (!token || token->type != JSON_STRING) {
4765 wpa_printf(MSG_DEBUG, "DPP: No crv in JWK");
4766 goto fail;
4767 }
4768 curve = dpp_get_curve_jwk_crv(token->string);
4769 if (!curve) {
4770 wpa_printf(MSG_DEBUG, "DPP: Unsupported JWK crv '%s'",
4771 token->string);
4772 goto fail;
4773 }
4774
4775 x = json_get_member_base64url(jwk, "x");
4776 if (!x) {
4777 wpa_printf(MSG_DEBUG, "DPP: No x in JWK");
4778 goto fail;
4779 }
4780 wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK x", x);
4781 if (wpabuf_len(x) != curve->prime_len) {
4782 wpa_printf(MSG_DEBUG,
4783 "DPP: Unexpected JWK x length %u (expected %u for curve %s)",
4784 (unsigned int) wpabuf_len(x),
4785 (unsigned int) curve->prime_len, curve->name);
4786 goto fail;
4787 }
4788
4789 y = json_get_member_base64url(jwk, "y");
4790 if (!y) {
4791 wpa_printf(MSG_DEBUG, "DPP: No y in JWK");
4792 goto fail;
4793 }
4794 wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK y", y);
4795 if (wpabuf_len(y) != curve->prime_len) {
4796 wpa_printf(MSG_DEBUG,
4797 "DPP: Unexpected JWK y length %u (expected %u for curve %s)",
4798 (unsigned int) wpabuf_len(y),
4799 (unsigned int) curve->prime_len, curve->name);
4800 goto fail;
4801 }
4802
4803 group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
4804 if (!group) {
4805 wpa_printf(MSG_DEBUG, "DPP: Could not prepare group for JWK");
4806 goto fail;
4807 }
4808
4809 pkey = dpp_set_pubkey_point_group(group, wpabuf_head(x), wpabuf_head(y),
4810 wpabuf_len(x));
4811 *key_curve = curve;
4812
4813fail:
4814 wpabuf_free(x);
4815 wpabuf_free(y);
4816
4817 return pkey;
4818}
4819
4820
4821int dpp_key_expired(const char *timestamp, os_time_t *expiry)
4822{
4823 struct os_time now;
4824 unsigned int year, month, day, hour, min, sec;
4825 os_time_t utime;
4826 const char *pos;
4827
4828 /* ISO 8601 date and time:
4829 * <date>T<time>
4830 * YYYY-MM-DDTHH:MM:SSZ
4831 * YYYY-MM-DDTHH:MM:SS+03:00
4832 */
4833 if (os_strlen(timestamp) < 19) {
4834 wpa_printf(MSG_DEBUG,
4835 "DPP: Too short timestamp - assume expired key");
4836 return 1;
4837 }
4838 if (sscanf(timestamp, "%04u-%02u-%02uT%02u:%02u:%02u",
4839 &year, &month, &day, &hour, &min, &sec) != 6) {
4840 wpa_printf(MSG_DEBUG,
4841 "DPP: Failed to parse expiration day - assume expired key");
4842 return 1;
4843 }
4844
4845 if (os_mktime(year, month, day, hour, min, sec, &utime) < 0) {
4846 wpa_printf(MSG_DEBUG,
4847 "DPP: Invalid date/time information - assume expired key");
4848 return 1;
4849 }
4850
4851 pos = timestamp + 19;
4852 if (*pos == 'Z' || *pos == '\0') {
4853 /* In UTC - no need to adjust */
4854 } else if (*pos == '-' || *pos == '+') {
4855 int items;
4856
4857 /* Adjust local time to UTC */
4858 items = sscanf(pos + 1, "%02u:%02u", &hour, &min);
4859 if (items < 1) {
4860 wpa_printf(MSG_DEBUG,
4861 "DPP: Invalid time zone designator (%s) - assume expired key",
4862 pos);
4863 return 1;
4864 }
4865 if (*pos == '-')
4866 utime += 3600 * hour;
4867 if (*pos == '+')
4868 utime -= 3600 * hour;
4869 if (items > 1) {
4870 if (*pos == '-')
4871 utime += 60 * min;
4872 if (*pos == '+')
4873 utime -= 60 * min;
4874 }
4875 } else {
4876 wpa_printf(MSG_DEBUG,
4877 "DPP: Invalid time zone designator (%s) - assume expired key",
4878 pos);
4879 return 1;
4880 }
4881 if (expiry)
4882 *expiry = utime;
4883
4884 if (os_get_time(&now) < 0) {
4885 wpa_printf(MSG_DEBUG,
4886 "DPP: Cannot get current time - assume expired key");
4887 return 1;
4888 }
4889
4890 if (now.sec > utime) {
4891 wpa_printf(MSG_DEBUG, "DPP: Key has expired (%lu < %lu)",
4892 utime, now.sec);
4893 return 1;
4894 }
4895
4896 return 0;
4897}
4898
4899
4900static int dpp_parse_connector(struct dpp_authentication *auth,
4901 const unsigned char *payload,
4902 u16 payload_len)
4903{
4904 struct json_token *root, *groups, *netkey, *token;
4905 int ret = -1;
4906 EVP_PKEY *key = NULL;
4907 const struct dpp_curve_params *curve;
4908 unsigned int rules = 0;
4909
4910 root = json_parse((const char *) payload, payload_len);
4911 if (!root) {
4912 wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed");
4913 goto fail;
4914 }
4915
4916 groups = json_get_member(root, "groups");
4917 if (!groups || groups->type != JSON_ARRAY) {
4918 wpa_printf(MSG_DEBUG, "DPP: No groups array found");
4919 goto skip_groups;
4920 }
4921 for (token = groups->child; token; token = token->sibling) {
4922 struct json_token *id, *role;
4923
4924 id = json_get_member(token, "groupId");
4925 if (!id || id->type != JSON_STRING) {
4926 wpa_printf(MSG_DEBUG, "DPP: Missing groupId string");
4927 goto fail;
4928 }
4929
4930 role = json_get_member(token, "netRole");
4931 if (!role || role->type != JSON_STRING) {
4932 wpa_printf(MSG_DEBUG, "DPP: Missing netRole string");
4933 goto fail;
4934 }
4935 wpa_printf(MSG_DEBUG,
4936 "DPP: connector group: groupId='%s' netRole='%s'",
4937 id->string, role->string);
4938 rules++;
4939 }
4940skip_groups:
4941
4942 if (!rules) {
4943 wpa_printf(MSG_DEBUG,
4944 "DPP: Connector includes no groups");
4945 goto fail;
4946 }
4947
4948 token = json_get_member(root, "expiry");
4949 if (!token || token->type != JSON_STRING) {
4950 wpa_printf(MSG_DEBUG,
4951 "DPP: No expiry string found - connector does not expire");
4952 } else {
4953 wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string);
4954 if (dpp_key_expired(token->string,
4955 &auth->net_access_key_expiry)) {
4956 wpa_printf(MSG_DEBUG,
4957 "DPP: Connector (netAccessKey) has expired");
4958 goto fail;
4959 }
4960 }
4961
4962 netkey = json_get_member(root, "netAccessKey");
4963 if (!netkey || netkey->type != JSON_OBJECT) {
4964 wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
4965 goto fail;
4966 }
4967
4968 key = dpp_parse_jwk(netkey, &curve);
4969 if (!key)
4970 goto fail;
4971 dpp_debug_print_key("DPP: Received netAccessKey", key);
4972
4973 if (EVP_PKEY_cmp(key, auth->own_protocol_key) != 1) {
4974 wpa_printf(MSG_DEBUG,
4975 "DPP: netAccessKey in connector does not match own protocol key");
4976#ifdef CONFIG_TESTING_OPTIONS
4977 if (auth->ignore_netaccesskey_mismatch) {
4978 wpa_printf(MSG_DEBUG,
4979 "DPP: TESTING - skip netAccessKey mismatch");
4980 } else {
4981 goto fail;
4982 }
4983#else /* CONFIG_TESTING_OPTIONS */
4984 goto fail;
4985#endif /* CONFIG_TESTING_OPTIONS */
4986 }
4987
4988 ret = 0;
4989fail:
4990 EVP_PKEY_free(key);
4991 json_free(root);
4992 return ret;
4993}
4994
4995
4996static int dpp_check_pubkey_match(EVP_PKEY *pub, struct wpabuf *r_hash)
4997{
4998 struct wpabuf *uncomp;
4999 int res;
5000 u8 hash[SHA256_MAC_LEN];
5001 const u8 *addr[1];
5002 size_t len[1];
5003
5004 if (wpabuf_len(r_hash) != SHA256_MAC_LEN)
5005 return -1;
5006 uncomp = dpp_get_pubkey_point(pub, 1);
5007 if (!uncomp)
5008 return -1;
5009 addr[0] = wpabuf_head(uncomp);
5010 len[0] = wpabuf_len(uncomp);
5011 wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed public key",
5012 addr[0], len[0]);
5013 res = sha256_vector(1, addr, len, hash);
5014 wpabuf_free(uncomp);
5015 if (res < 0)
5016 return -1;
5017 if (os_memcmp(hash, wpabuf_head(r_hash), SHA256_MAC_LEN) != 0) {
5018 wpa_printf(MSG_DEBUG,
5019 "DPP: Received hash value does not match calculated public key hash value");
5020 wpa_hexdump(MSG_DEBUG, "DPP: Calculated hash",
5021 hash, SHA256_MAC_LEN);
5022 return -1;
5023 }
5024 return 0;
5025}
5026
5027
5028static void dpp_copy_csign(struct dpp_authentication *auth, EVP_PKEY *csign)
5029{
5030 unsigned char *der = NULL;
5031 int der_len;
5032
5033 der_len = i2d_PUBKEY(csign, &der);
5034 if (der_len <= 0)
5035 return;
5036 wpabuf_free(auth->c_sign_key);
5037 auth->c_sign_key = wpabuf_alloc_copy(der, der_len);
5038 OPENSSL_free(der);
5039}
5040
5041
5042static void dpp_copy_netaccesskey(struct dpp_authentication *auth)
5043{
5044 unsigned char *der = NULL;
5045 int der_len;
5046 EC_KEY *eckey;
5047
5048 eckey = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
5049 if (!eckey)
5050 return;
5051
5052 der_len = i2d_ECPrivateKey(eckey, &der);
5053 if (der_len <= 0) {
5054 EC_KEY_free(eckey);
5055 return;
5056 }
5057 wpabuf_free(auth->net_access_key);
5058 auth->net_access_key = wpabuf_alloc_copy(der, der_len);
5059 OPENSSL_free(der);
5060 EC_KEY_free(eckey);
5061}
5062
5063
5064struct dpp_signed_connector_info {
5065 unsigned char *payload;
5066 size_t payload_len;
5067};
5068
Roshan Pius3a1667e2018-07-03 15:17:14 -07005069static enum dpp_status_error
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005070dpp_process_signed_connector(struct dpp_signed_connector_info *info,
5071 EVP_PKEY *csign_pub, const char *connector)
5072{
Roshan Pius3a1667e2018-07-03 15:17:14 -07005073 enum dpp_status_error ret = 255;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005074 const char *pos, *end, *signed_start, *signed_end;
5075 struct wpabuf *kid = NULL;
5076 unsigned char *prot_hdr = NULL, *signature = NULL;
5077 size_t prot_hdr_len = 0, signature_len = 0;
5078 const EVP_MD *sign_md = NULL;
5079 unsigned char *der = NULL;
5080 int der_len;
5081 int res;
5082 EVP_MD_CTX *md_ctx = NULL;
5083 ECDSA_SIG *sig = NULL;
5084 BIGNUM *r = NULL, *s = NULL;
5085 const struct dpp_curve_params *curve;
5086 EC_KEY *eckey;
5087 const EC_GROUP *group;
5088 int nid;
5089
5090 eckey = EVP_PKEY_get1_EC_KEY(csign_pub);
5091 if (!eckey)
5092 goto fail;
5093 group = EC_KEY_get0_group(eckey);
5094 if (!group)
5095 goto fail;
5096 nid = EC_GROUP_get_curve_name(group);
5097 curve = dpp_get_curve_nid(nid);
5098 if (!curve)
5099 goto fail;
5100 wpa_printf(MSG_DEBUG, "DPP: C-sign-key group: %s", curve->jwk_crv);
5101 os_memset(info, 0, sizeof(*info));
5102
5103 signed_start = pos = connector;
5104 end = os_strchr(pos, '.');
5105 if (!end) {
5106 wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector");
Roshan Pius3a1667e2018-07-03 15:17:14 -07005107 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005108 goto fail;
5109 }
5110 prot_hdr = base64_url_decode((const unsigned char *) pos,
5111 end - pos, &prot_hdr_len);
5112 if (!prot_hdr) {
5113 wpa_printf(MSG_DEBUG,
5114 "DPP: Failed to base64url decode signedConnector JWS Protected Header");
Roshan Pius3a1667e2018-07-03 15:17:14 -07005115 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005116 goto fail;
5117 }
5118 wpa_hexdump_ascii(MSG_DEBUG,
5119 "DPP: signedConnector - JWS Protected Header",
5120 prot_hdr, prot_hdr_len);
5121 kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md);
Roshan Pius3a1667e2018-07-03 15:17:14 -07005122 if (!kid) {
5123 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005124 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005125 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005126 if (wpabuf_len(kid) != SHA256_MAC_LEN) {
5127 wpa_printf(MSG_DEBUG,
5128 "DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)",
5129 (unsigned int) wpabuf_len(kid), SHA256_MAC_LEN);
Roshan Pius3a1667e2018-07-03 15:17:14 -07005130 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005131 goto fail;
5132 }
5133
5134 pos = end + 1;
5135 end = os_strchr(pos, '.');
5136 if (!end) {
5137 wpa_printf(MSG_DEBUG,
5138 "DPP: Missing dot(2) in signedConnector");
Roshan Pius3a1667e2018-07-03 15:17:14 -07005139 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005140 goto fail;
5141 }
5142 signed_end = end - 1;
5143 info->payload = base64_url_decode((const unsigned char *) pos,
5144 end - pos, &info->payload_len);
5145 if (!info->payload) {
5146 wpa_printf(MSG_DEBUG,
5147 "DPP: Failed to base64url decode signedConnector JWS Payload");
Roshan Pius3a1667e2018-07-03 15:17:14 -07005148 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005149 goto fail;
5150 }
5151 wpa_hexdump_ascii(MSG_DEBUG,
5152 "DPP: signedConnector - JWS Payload",
5153 info->payload, info->payload_len);
5154 pos = end + 1;
5155 signature = base64_url_decode((const unsigned char *) pos,
5156 os_strlen(pos), &signature_len);
5157 if (!signature) {
5158 wpa_printf(MSG_DEBUG,
5159 "DPP: Failed to base64url decode signedConnector signature");
Roshan Pius3a1667e2018-07-03 15:17:14 -07005160 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005161 goto fail;
5162 }
5163 wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature",
5164 signature, signature_len);
5165
Roshan Pius3a1667e2018-07-03 15:17:14 -07005166 if (dpp_check_pubkey_match(csign_pub, kid) < 0) {
5167 ret = DPP_STATUS_NO_MATCH;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005168 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005169 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005170
5171 if (signature_len & 0x01) {
5172 wpa_printf(MSG_DEBUG,
5173 "DPP: Unexpected signedConnector signature length (%d)",
5174 (int) signature_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07005175 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005176 goto fail;
5177 }
5178
5179 /* JWS Signature encodes the signature (r,s) as two octet strings. Need
5180 * to convert that to DER encoded ECDSA_SIG for OpenSSL EVP routines. */
5181 r = BN_bin2bn(signature, signature_len / 2, NULL);
5182 s = BN_bin2bn(signature + signature_len / 2, signature_len / 2, NULL);
5183 sig = ECDSA_SIG_new();
5184 if (!r || !s || !sig || ECDSA_SIG_set0(sig, r, s) != 1)
5185 goto fail;
5186 r = NULL;
5187 s = NULL;
5188
5189 der_len = i2d_ECDSA_SIG(sig, &der);
5190 if (der_len <= 0) {
5191 wpa_printf(MSG_DEBUG, "DPP: Could not DER encode signature");
5192 goto fail;
5193 }
5194 wpa_hexdump(MSG_DEBUG, "DPP: DER encoded signature", der, der_len);
5195 md_ctx = EVP_MD_CTX_create();
5196 if (!md_ctx)
5197 goto fail;
5198
5199 ERR_clear_error();
5200 if (EVP_DigestVerifyInit(md_ctx, NULL, sign_md, NULL, csign_pub) != 1) {
5201 wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyInit failed: %s",
5202 ERR_error_string(ERR_get_error(), NULL));
5203 goto fail;
5204 }
5205 if (EVP_DigestVerifyUpdate(md_ctx, signed_start,
5206 signed_end - signed_start + 1) != 1) {
5207 wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyUpdate failed: %s",
5208 ERR_error_string(ERR_get_error(), NULL));
5209 goto fail;
5210 }
5211 res = EVP_DigestVerifyFinal(md_ctx, der, der_len);
5212 if (res != 1) {
5213 wpa_printf(MSG_DEBUG,
5214 "DPP: EVP_DigestVerifyFinal failed (res=%d): %s",
5215 res, ERR_error_string(ERR_get_error(), NULL));
Roshan Pius3a1667e2018-07-03 15:17:14 -07005216 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005217 goto fail;
5218 }
5219
Roshan Pius3a1667e2018-07-03 15:17:14 -07005220 ret = DPP_STATUS_OK;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005221fail:
5222 EC_KEY_free(eckey);
5223 EVP_MD_CTX_destroy(md_ctx);
5224 os_free(prot_hdr);
5225 wpabuf_free(kid);
5226 os_free(signature);
5227 ECDSA_SIG_free(sig);
5228 BN_free(r);
5229 BN_free(s);
5230 OPENSSL_free(der);
5231 return ret;
5232}
5233
5234
5235static int dpp_parse_cred_dpp(struct dpp_authentication *auth,
5236 struct json_token *cred)
5237{
5238 struct dpp_signed_connector_info info;
5239 struct json_token *token, *csign;
5240 int ret = -1;
5241 EVP_PKEY *csign_pub = NULL;
5242 const struct dpp_curve_params *key_curve = NULL;
5243 const char *signed_connector;
5244
5245 os_memset(&info, 0, sizeof(info));
5246
5247 wpa_printf(MSG_DEBUG, "DPP: Connector credential");
5248
5249 csign = json_get_member(cred, "csign");
5250 if (!csign || csign->type != JSON_OBJECT) {
5251 wpa_printf(MSG_DEBUG, "DPP: No csign JWK in JSON");
5252 goto fail;
5253 }
5254
5255 csign_pub = dpp_parse_jwk(csign, &key_curve);
5256 if (!csign_pub) {
5257 wpa_printf(MSG_DEBUG, "DPP: Failed to parse csign JWK");
5258 goto fail;
5259 }
5260 dpp_debug_print_key("DPP: Received C-sign-key", csign_pub);
5261
5262 token = json_get_member(cred, "signedConnector");
5263 if (!token || token->type != JSON_STRING) {
5264 wpa_printf(MSG_DEBUG, "DPP: No signedConnector string found");
5265 goto fail;
5266 }
5267 wpa_hexdump_ascii(MSG_DEBUG, "DPP: signedConnector",
5268 token->string, os_strlen(token->string));
5269 signed_connector = token->string;
5270
5271 if (os_strchr(signed_connector, '"') ||
5272 os_strchr(signed_connector, '\n')) {
5273 wpa_printf(MSG_DEBUG,
5274 "DPP: Unexpected character in signedConnector");
5275 goto fail;
5276 }
5277
5278 if (dpp_process_signed_connector(&info, csign_pub,
Roshan Pius3a1667e2018-07-03 15:17:14 -07005279 signed_connector) != DPP_STATUS_OK)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005280 goto fail;
5281
5282 if (dpp_parse_connector(auth, info.payload, info.payload_len) < 0) {
5283 wpa_printf(MSG_DEBUG, "DPP: Failed to parse connector");
5284 goto fail;
5285 }
5286
5287 os_free(auth->connector);
5288 auth->connector = os_strdup(signed_connector);
5289
5290 dpp_copy_csign(auth, csign_pub);
5291 dpp_copy_netaccesskey(auth);
5292
5293 ret = 0;
5294fail:
5295 EVP_PKEY_free(csign_pub);
5296 os_free(info.payload);
5297 return ret;
5298}
5299
5300
Roshan Pius3a1667e2018-07-03 15:17:14 -07005301const char * dpp_akm_str(enum dpp_akm akm)
5302{
5303 switch (akm) {
5304 case DPP_AKM_DPP:
5305 return "dpp";
5306 case DPP_AKM_PSK:
5307 return "psk";
5308 case DPP_AKM_SAE:
5309 return "sae";
5310 case DPP_AKM_PSK_SAE:
5311 return "psk+sae";
5312 default:
5313 return "??";
5314 }
5315}
5316
5317
5318static enum dpp_akm dpp_akm_from_str(const char *akm)
5319{
5320 if (os_strcmp(akm, "psk") == 0)
5321 return DPP_AKM_PSK;
5322 if (os_strcmp(akm, "sae") == 0)
5323 return DPP_AKM_SAE;
5324 if (os_strcmp(akm, "psk+sae") == 0)
5325 return DPP_AKM_PSK_SAE;
5326 if (os_strcmp(akm, "dpp") == 0)
5327 return DPP_AKM_DPP;
5328 return DPP_AKM_UNKNOWN;
5329}
5330
5331
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005332static int dpp_parse_conf_obj(struct dpp_authentication *auth,
5333 const u8 *conf_obj, u16 conf_obj_len)
5334{
5335 int ret = -1;
5336 struct json_token *root, *token, *discovery, *cred;
5337
5338 root = json_parse((const char *) conf_obj, conf_obj_len);
5339 if (!root)
5340 return -1;
5341 if (root->type != JSON_OBJECT) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005342 dpp_auth_fail(auth, "JSON root is not an object");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005343 goto fail;
5344 }
5345
5346 token = json_get_member(root, "wi-fi_tech");
5347 if (!token || token->type != JSON_STRING) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005348 dpp_auth_fail(auth, "No wi-fi_tech string value found");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005349 goto fail;
5350 }
5351 if (os_strcmp(token->string, "infra") != 0) {
5352 wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech value: '%s'",
5353 token->string);
Roshan Pius3a1667e2018-07-03 15:17:14 -07005354 dpp_auth_fail(auth, "Unsupported wi-fi_tech value");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005355 goto fail;
5356 }
5357
5358 discovery = json_get_member(root, "discovery");
5359 if (!discovery || discovery->type != JSON_OBJECT) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005360 dpp_auth_fail(auth, "No discovery object in JSON");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005361 goto fail;
5362 }
5363
5364 token = json_get_member(discovery, "ssid");
5365 if (!token || token->type != JSON_STRING) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005366 dpp_auth_fail(auth, "No discovery::ssid string value found");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005367 goto fail;
5368 }
5369 wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid",
5370 token->string, os_strlen(token->string));
5371 if (os_strlen(token->string) > SSID_MAX_LEN) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005372 dpp_auth_fail(auth, "Too long discovery::ssid string value");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005373 goto fail;
5374 }
5375 auth->ssid_len = os_strlen(token->string);
5376 os_memcpy(auth->ssid, token->string, auth->ssid_len);
5377
5378 cred = json_get_member(root, "cred");
5379 if (!cred || cred->type != JSON_OBJECT) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005380 dpp_auth_fail(auth, "No cred object in JSON");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005381 goto fail;
5382 }
5383
5384 token = json_get_member(cred, "akm");
5385 if (!token || token->type != JSON_STRING) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005386 dpp_auth_fail(auth, "No cred::akm string value found");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005387 goto fail;
5388 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07005389 auth->akm = dpp_akm_from_str(token->string);
5390
5391 if (auth->akm == DPP_AKM_PSK || auth->akm == DPP_AKM_SAE ||
5392 auth->akm == DPP_AKM_PSK_SAE) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005393 if (dpp_parse_cred_legacy(auth, cred) < 0)
5394 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005395 } else if (auth->akm == DPP_AKM_DPP) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005396 if (dpp_parse_cred_dpp(auth, cred) < 0)
5397 goto fail;
5398 } else {
5399 wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s",
5400 token->string);
Roshan Pius3a1667e2018-07-03 15:17:14 -07005401 dpp_auth_fail(auth, "Unsupported akm");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005402 goto fail;
5403 }
5404
5405 wpa_printf(MSG_DEBUG, "DPP: JSON parsing completed successfully");
5406 ret = 0;
5407fail:
5408 json_free(root);
5409 return ret;
5410}
5411
5412
5413int dpp_conf_resp_rx(struct dpp_authentication *auth,
5414 const struct wpabuf *resp)
5415{
5416 const u8 *wrapped_data, *e_nonce, *status, *conf_obj;
5417 u16 wrapped_data_len, e_nonce_len, status_len, conf_obj_len;
5418 const u8 *addr[1];
5419 size_t len[1];
5420 u8 *unwrapped = NULL;
5421 size_t unwrapped_len = 0;
5422 int ret = -1;
5423
5424 if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005425 dpp_auth_fail(auth, "Invalid attribute in config response");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005426 return -1;
5427 }
5428
5429 wrapped_data = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp),
5430 DPP_ATTR_WRAPPED_DATA,
5431 &wrapped_data_len);
5432 if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005433 dpp_auth_fail(auth,
5434 "Missing or invalid required Wrapped Data attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005435 return -1;
5436 }
5437
5438 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
5439 wrapped_data, wrapped_data_len);
5440 unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
5441 unwrapped = os_malloc(unwrapped_len);
5442 if (!unwrapped)
5443 return -1;
5444
5445 addr[0] = wpabuf_head(resp);
5446 len[0] = wrapped_data - 4 - (const u8 *) wpabuf_head(resp);
5447 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
5448
5449 if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
5450 wrapped_data, wrapped_data_len,
5451 1, addr, len, unwrapped) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005452 dpp_auth_fail(auth, "AES-SIV decryption failed");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005453 goto fail;
5454 }
5455 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
5456 unwrapped, unwrapped_len);
5457
5458 if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005459 dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005460 goto fail;
5461 }
5462
5463 e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
5464 DPP_ATTR_ENROLLEE_NONCE,
5465 &e_nonce_len);
5466 if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005467 dpp_auth_fail(auth,
5468 "Missing or invalid Enrollee Nonce attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005469 goto fail;
5470 }
5471 wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
5472 if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005473 dpp_auth_fail(auth, "Enrollee Nonce mismatch");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005474 goto fail;
5475 }
5476
5477 status = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp),
5478 DPP_ATTR_STATUS, &status_len);
5479 if (!status || status_len < 1) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005480 dpp_auth_fail(auth,
5481 "Missing or invalid required DPP Status attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005482 goto fail;
5483 }
5484 wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
5485 if (status[0] != DPP_STATUS_OK) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005486 dpp_auth_fail(auth, "Configurator rejected configuration");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005487 goto fail;
5488 }
5489
5490 conf_obj = dpp_get_attr(unwrapped, unwrapped_len,
5491 DPP_ATTR_CONFIG_OBJ, &conf_obj_len);
5492 if (!conf_obj) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005493 dpp_auth_fail(auth,
5494 "Missing required Configuration Object attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005495 goto fail;
5496 }
5497 wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
5498 conf_obj, conf_obj_len);
5499 if (dpp_parse_conf_obj(auth, conf_obj, conf_obj_len) < 0)
5500 goto fail;
5501
5502 ret = 0;
5503
5504fail:
5505 os_free(unwrapped);
5506 return ret;
5507}
5508
5509
5510void dpp_configurator_free(struct dpp_configurator *conf)
5511{
5512 if (!conf)
5513 return;
5514 EVP_PKEY_free(conf->csign);
5515 os_free(conf->kid);
5516 os_free(conf);
5517}
5518
5519
Roshan Pius3a1667e2018-07-03 15:17:14 -07005520int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf,
5521 size_t buflen)
5522{
5523 EC_KEY *eckey;
5524 int key_len, ret = -1;
5525 unsigned char *key = NULL;
5526
5527 if (!conf->csign)
5528 return -1;
5529
5530 eckey = EVP_PKEY_get1_EC_KEY(conf->csign);
5531 if (!eckey)
5532 return -1;
5533
5534 key_len = i2d_ECPrivateKey(eckey, &key);
5535 if (key_len > 0)
5536 ret = wpa_snprintf_hex(buf, buflen, key, key_len);
5537
5538 EC_KEY_free(eckey);
5539 OPENSSL_free(key);
5540 return ret;
5541}
5542
5543
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005544struct dpp_configurator *
5545dpp_keygen_configurator(const char *curve, const u8 *privkey,
5546 size_t privkey_len)
5547{
5548 struct dpp_configurator *conf;
5549 struct wpabuf *csign_pub = NULL;
5550 u8 kid_hash[SHA256_MAC_LEN];
5551 const u8 *addr[1];
5552 size_t len[1];
5553
5554 conf = os_zalloc(sizeof(*conf));
5555 if (!conf)
5556 return NULL;
5557
5558 if (!curve) {
5559 conf->curve = &dpp_curves[0];
5560 } else {
5561 conf->curve = dpp_get_curve_name(curve);
5562 if (!conf->curve) {
5563 wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
5564 curve);
5565 return NULL;
5566 }
5567 }
5568 if (privkey)
5569 conf->csign = dpp_set_keypair(&conf->curve, privkey,
5570 privkey_len);
5571 else
5572 conf->csign = dpp_gen_keypair(conf->curve);
5573 if (!conf->csign)
5574 goto fail;
5575 conf->own = 1;
5576
5577 csign_pub = dpp_get_pubkey_point(conf->csign, 1);
5578 if (!csign_pub) {
5579 wpa_printf(MSG_INFO, "DPP: Failed to extract C-sign-key");
5580 goto fail;
5581 }
5582
5583 /* kid = SHA256(ANSI X9.63 uncompressed C-sign-key) */
5584 addr[0] = wpabuf_head(csign_pub);
5585 len[0] = wpabuf_len(csign_pub);
5586 if (sha256_vector(1, addr, len, kid_hash) < 0) {
5587 wpa_printf(MSG_DEBUG,
5588 "DPP: Failed to derive kid for C-sign-key");
5589 goto fail;
5590 }
5591
5592 conf->kid = (char *) base64_url_encode(kid_hash, sizeof(kid_hash),
5593 NULL, 0);
5594 if (!conf->kid)
5595 goto fail;
5596out:
5597 wpabuf_free(csign_pub);
5598 return conf;
5599fail:
5600 dpp_configurator_free(conf);
5601 conf = NULL;
5602 goto out;
5603}
5604
5605
5606int dpp_configurator_own_config(struct dpp_authentication *auth,
Roshan Pius3a1667e2018-07-03 15:17:14 -07005607 const char *curve, int ap)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005608{
5609 struct wpabuf *conf_obj;
5610 int ret = -1;
5611
5612 if (!auth->conf) {
5613 wpa_printf(MSG_DEBUG, "DPP: No configurator specified");
5614 return -1;
5615 }
5616
5617 if (!curve) {
5618 auth->curve = &dpp_curves[0];
5619 } else {
5620 auth->curve = dpp_get_curve_name(curve);
5621 if (!auth->curve) {
5622 wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
5623 curve);
5624 return -1;
5625 }
5626 }
5627 wpa_printf(MSG_DEBUG,
5628 "DPP: Building own configuration/connector with curve %s",
5629 auth->curve->name);
5630
5631 auth->own_protocol_key = dpp_gen_keypair(auth->curve);
5632 if (!auth->own_protocol_key)
5633 return -1;
5634 dpp_copy_netaccesskey(auth);
5635 auth->peer_protocol_key = auth->own_protocol_key;
5636 dpp_copy_csign(auth, auth->conf->csign);
5637
Roshan Pius3a1667e2018-07-03 15:17:14 -07005638 conf_obj = dpp_build_conf_obj(auth, ap);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005639 if (!conf_obj)
5640 goto fail;
5641 ret = dpp_parse_conf_obj(auth, wpabuf_head(conf_obj),
5642 wpabuf_len(conf_obj));
5643fail:
5644 wpabuf_free(conf_obj);
5645 auth->peer_protocol_key = NULL;
5646 return ret;
5647}
5648
5649
5650static int dpp_compatible_netrole(const char *role1, const char *role2)
5651{
5652 return (os_strcmp(role1, "sta") == 0 && os_strcmp(role2, "ap") == 0) ||
5653 (os_strcmp(role1, "ap") == 0 && os_strcmp(role2, "sta") == 0);
5654}
5655
5656
5657static int dpp_connector_compatible_group(struct json_token *root,
5658 const char *group_id,
5659 const char *net_role)
5660{
5661 struct json_token *groups, *token;
5662
5663 groups = json_get_member(root, "groups");
5664 if (!groups || groups->type != JSON_ARRAY)
5665 return 0;
5666
5667 for (token = groups->child; token; token = token->sibling) {
5668 struct json_token *id, *role;
5669
5670 id = json_get_member(token, "groupId");
5671 if (!id || id->type != JSON_STRING)
5672 continue;
5673
5674 role = json_get_member(token, "netRole");
5675 if (!role || role->type != JSON_STRING)
5676 continue;
5677
5678 if (os_strcmp(id->string, "*") != 0 &&
5679 os_strcmp(group_id, "*") != 0 &&
5680 os_strcmp(id->string, group_id) != 0)
5681 continue;
5682
5683 if (dpp_compatible_netrole(role->string, net_role))
5684 return 1;
5685 }
5686
5687 return 0;
5688}
5689
5690
5691static int dpp_connector_match_groups(struct json_token *own_root,
5692 struct json_token *peer_root)
5693{
5694 struct json_token *groups, *token;
5695
5696 groups = json_get_member(peer_root, "groups");
5697 if (!groups || groups->type != JSON_ARRAY) {
5698 wpa_printf(MSG_DEBUG, "DPP: No peer groups array found");
5699 return 0;
5700 }
5701
5702 for (token = groups->child; token; token = token->sibling) {
5703 struct json_token *id, *role;
5704
5705 id = json_get_member(token, "groupId");
5706 if (!id || id->type != JSON_STRING) {
5707 wpa_printf(MSG_DEBUG,
5708 "DPP: Missing peer groupId string");
5709 continue;
5710 }
5711
5712 role = json_get_member(token, "netRole");
5713 if (!role || role->type != JSON_STRING) {
5714 wpa_printf(MSG_DEBUG,
5715 "DPP: Missing peer groups::netRole string");
5716 continue;
5717 }
5718 wpa_printf(MSG_DEBUG,
5719 "DPP: peer connector group: groupId='%s' netRole='%s'",
5720 id->string, role->string);
5721 if (dpp_connector_compatible_group(own_root, id->string,
5722 role->string)) {
5723 wpa_printf(MSG_DEBUG,
5724 "DPP: Compatible group/netRole in own connector");
5725 return 1;
5726 }
5727 }
5728
5729 return 0;
5730}
5731
5732
5733static int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk,
5734 unsigned int hash_len)
5735{
5736 u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
5737 const char *info = "DPP PMK";
5738 int res;
5739
5740 /* PMK = HKDF(<>, "DPP PMK", N.x) */
5741
5742 /* HKDF-Extract(<>, N.x) */
5743 os_memset(salt, 0, hash_len);
5744 if (dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk) < 0)
5745 return -1;
5746 wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
5747 prk, hash_len);
5748
5749 /* HKDF-Expand(PRK, info, L) */
5750 res = dpp_hkdf_expand(hash_len, prk, hash_len, info, pmk, hash_len);
5751 os_memset(prk, 0, hash_len);
5752 if (res < 0)
5753 return -1;
5754
5755 wpa_hexdump_key(MSG_DEBUG, "DPP: PMK = HKDF-Expand(PRK, info, L)",
5756 pmk, hash_len);
5757 return 0;
5758}
5759
5760
5761static int dpp_derive_pmkid(const struct dpp_curve_params *curve,
5762 EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid)
5763{
5764 struct wpabuf *nkx, *pkx;
5765 int ret = -1, res;
5766 const u8 *addr[2];
5767 size_t len[2];
5768 u8 hash[SHA256_MAC_LEN];
5769
5770 /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
5771 nkx = dpp_get_pubkey_point(own_key, 0);
5772 pkx = dpp_get_pubkey_point(peer_key, 0);
5773 if (!nkx || !pkx)
5774 goto fail;
5775 addr[0] = wpabuf_head(nkx);
5776 len[0] = wpabuf_len(nkx) / 2;
5777 addr[1] = wpabuf_head(pkx);
5778 len[1] = wpabuf_len(pkx) / 2;
5779 if (len[0] != len[1])
5780 goto fail;
5781 if (os_memcmp(addr[0], addr[1], len[0]) > 0) {
5782 addr[0] = wpabuf_head(pkx);
5783 addr[1] = wpabuf_head(nkx);
5784 }
5785 wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 1", addr[0], len[0]);
5786 wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 2", addr[1], len[1]);
5787 res = sha256_vector(2, addr, len, hash);
5788 if (res < 0)
5789 goto fail;
5790 wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash output", hash, SHA256_MAC_LEN);
5791 os_memcpy(pmkid, hash, PMKID_LEN);
5792 wpa_hexdump(MSG_DEBUG, "DPP: PMKID", pmkid, PMKID_LEN);
5793 ret = 0;
5794fail:
5795 wpabuf_free(nkx);
5796 wpabuf_free(pkx);
5797 return ret;
5798}
5799
5800
Roshan Pius3a1667e2018-07-03 15:17:14 -07005801enum dpp_status_error
5802dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
5803 const u8 *net_access_key, size_t net_access_key_len,
5804 const u8 *csign_key, size_t csign_key_len,
5805 const u8 *peer_connector, size_t peer_connector_len,
5806 os_time_t *expiry)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005807{
5808 struct json_token *root = NULL, *netkey, *token;
5809 struct json_token *own_root = NULL;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005810 enum dpp_status_error ret = 255, res;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005811 EVP_PKEY *own_key = NULL, *peer_key = NULL;
5812 struct wpabuf *own_key_pub = NULL;
5813 const struct dpp_curve_params *curve, *own_curve;
5814 struct dpp_signed_connector_info info;
5815 const unsigned char *p;
5816 EVP_PKEY *csign = NULL;
5817 char *signed_connector = NULL;
5818 const char *pos, *end;
5819 unsigned char *own_conn = NULL;
5820 size_t own_conn_len;
5821 EVP_PKEY_CTX *ctx = NULL;
5822 size_t Nx_len;
5823 u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
5824
5825 os_memset(intro, 0, sizeof(*intro));
5826 os_memset(&info, 0, sizeof(info));
5827 if (expiry)
5828 *expiry = 0;
5829
5830 p = csign_key;
5831 csign = d2i_PUBKEY(NULL, &p, csign_key_len);
5832 if (!csign) {
5833 wpa_printf(MSG_ERROR,
5834 "DPP: Failed to parse local C-sign-key information");
5835 goto fail;
5836 }
5837
5838 own_key = dpp_set_keypair(&own_curve, net_access_key,
5839 net_access_key_len);
5840 if (!own_key) {
5841 wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
5842 goto fail;
5843 }
5844
5845 pos = os_strchr(own_connector, '.');
5846 if (!pos) {
5847 wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the first dot (.)");
5848 goto fail;
5849 }
5850 pos++;
5851 end = os_strchr(pos, '.');
5852 if (!end) {
5853 wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)");
5854 goto fail;
5855 }
5856 own_conn = base64_url_decode((const unsigned char *) pos,
5857 end - pos, &own_conn_len);
5858 if (!own_conn) {
5859 wpa_printf(MSG_DEBUG,
5860 "DPP: Failed to base64url decode own signedConnector JWS Payload");
5861 goto fail;
5862 }
5863
5864 own_root = json_parse((const char *) own_conn, own_conn_len);
5865 if (!own_root) {
5866 wpa_printf(MSG_DEBUG, "DPP: Failed to parse local connector");
5867 goto fail;
5868 }
5869
5870 wpa_hexdump_ascii(MSG_DEBUG, "DPP: Peer signedConnector",
5871 peer_connector, peer_connector_len);
5872 signed_connector = os_malloc(peer_connector_len + 1);
5873 if (!signed_connector)
5874 goto fail;
5875 os_memcpy(signed_connector, peer_connector, peer_connector_len);
5876 signed_connector[peer_connector_len] = '\0';
5877
Roshan Pius3a1667e2018-07-03 15:17:14 -07005878 res = dpp_process_signed_connector(&info, csign, signed_connector);
5879 if (res != DPP_STATUS_OK) {
5880 ret = res;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005881 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005882 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005883
5884 root = json_parse((const char *) info.payload, info.payload_len);
5885 if (!root) {
5886 wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed");
Roshan Pius3a1667e2018-07-03 15:17:14 -07005887 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005888 goto fail;
5889 }
5890
5891 if (!dpp_connector_match_groups(own_root, root)) {
5892 wpa_printf(MSG_DEBUG,
5893 "DPP: Peer connector does not include compatible group netrole with own connector");
Roshan Pius3a1667e2018-07-03 15:17:14 -07005894 ret = DPP_STATUS_NO_MATCH;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005895 goto fail;
5896 }
5897
5898 token = json_get_member(root, "expiry");
5899 if (!token || token->type != JSON_STRING) {
5900 wpa_printf(MSG_DEBUG,
5901 "DPP: No expiry string found - connector does not expire");
5902 } else {
5903 wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string);
5904 if (dpp_key_expired(token->string, expiry)) {
5905 wpa_printf(MSG_DEBUG,
5906 "DPP: Connector (netAccessKey) has expired");
Roshan Pius3a1667e2018-07-03 15:17:14 -07005907 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005908 goto fail;
5909 }
5910 }
5911
5912 netkey = json_get_member(root, "netAccessKey");
5913 if (!netkey || netkey->type != JSON_OBJECT) {
5914 wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
Roshan Pius3a1667e2018-07-03 15:17:14 -07005915 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005916 goto fail;
5917 }
5918
5919 peer_key = dpp_parse_jwk(netkey, &curve);
Roshan Pius3a1667e2018-07-03 15:17:14 -07005920 if (!peer_key) {
5921 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005922 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005923 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005924 dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
5925
5926 if (own_curve != curve) {
5927 wpa_printf(MSG_DEBUG,
5928 "DPP: Mismatching netAccessKey curves (%s != %s)",
5929 own_curve->name, curve->name);
Roshan Pius3a1667e2018-07-03 15:17:14 -07005930 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005931 goto fail;
5932 }
5933
5934 /* ECDH: N = nk * PK */
5935 ctx = EVP_PKEY_CTX_new(own_key, NULL);
5936 if (!ctx ||
5937 EVP_PKEY_derive_init(ctx) != 1 ||
5938 EVP_PKEY_derive_set_peer(ctx, peer_key) != 1 ||
5939 EVP_PKEY_derive(ctx, NULL, &Nx_len) != 1 ||
5940 Nx_len > DPP_MAX_SHARED_SECRET_LEN ||
5941 EVP_PKEY_derive(ctx, Nx, &Nx_len) != 1) {
5942 wpa_printf(MSG_ERROR,
5943 "DPP: Failed to derive ECDH shared secret: %s",
5944 ERR_error_string(ERR_get_error(), NULL));
5945 goto fail;
5946 }
5947
5948 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
5949 Nx, Nx_len);
5950
5951 /* PMK = HKDF(<>, "DPP PMK", N.x) */
5952 if (dpp_derive_pmk(Nx, Nx_len, intro->pmk, curve->hash_len) < 0) {
5953 wpa_printf(MSG_ERROR, "DPP: Failed to derive PMK");
5954 goto fail;
5955 }
5956 intro->pmk_len = curve->hash_len;
5957
5958 /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
5959 if (dpp_derive_pmkid(curve, own_key, peer_key, intro->pmkid) < 0) {
5960 wpa_printf(MSG_ERROR, "DPP: Failed to derive PMKID");
5961 goto fail;
5962 }
5963
Roshan Pius3a1667e2018-07-03 15:17:14 -07005964 ret = DPP_STATUS_OK;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005965fail:
Roshan Pius3a1667e2018-07-03 15:17:14 -07005966 if (ret != DPP_STATUS_OK)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005967 os_memset(intro, 0, sizeof(*intro));
5968 os_memset(Nx, 0, sizeof(Nx));
5969 EVP_PKEY_CTX_free(ctx);
5970 os_free(own_conn);
5971 os_free(signed_connector);
5972 os_free(info.payload);
5973 EVP_PKEY_free(own_key);
5974 wpabuf_free(own_key_pub);
5975 EVP_PKEY_free(peer_key);
5976 EVP_PKEY_free(csign);
5977 json_free(root);
5978 json_free(own_root);
5979 return ret;
5980}
5981
5982
5983static EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve,
5984 int init)
5985{
5986 EC_GROUP *group;
5987 size_t len = curve->prime_len;
5988 const u8 *x, *y;
5989
5990 switch (curve->ike_group) {
5991 case 19:
5992 x = init ? pkex_init_x_p256 : pkex_resp_x_p256;
5993 y = init ? pkex_init_y_p256 : pkex_resp_y_p256;
5994 break;
5995 case 20:
5996 x = init ? pkex_init_x_p384 : pkex_resp_x_p384;
5997 y = init ? pkex_init_y_p384 : pkex_resp_y_p384;
5998 break;
5999 case 21:
6000 x = init ? pkex_init_x_p521 : pkex_resp_x_p521;
6001 y = init ? pkex_init_y_p521 : pkex_resp_y_p521;
6002 break;
6003 case 28:
6004 x = init ? pkex_init_x_bp_p256r1 : pkex_resp_x_bp_p256r1;
6005 y = init ? pkex_init_y_bp_p256r1 : pkex_resp_y_bp_p256r1;
6006 break;
6007 case 29:
6008 x = init ? pkex_init_x_bp_p384r1 : pkex_resp_x_bp_p384r1;
6009 y = init ? pkex_init_y_bp_p384r1 : pkex_resp_y_bp_p384r1;
6010 break;
6011 case 30:
6012 x = init ? pkex_init_x_bp_p512r1 : pkex_resp_x_bp_p512r1;
6013 y = init ? pkex_init_y_bp_p512r1 : pkex_resp_y_bp_p512r1;
6014 break;
6015 default:
6016 return NULL;
6017 }
6018
6019 group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
6020 if (!group)
6021 return NULL;
6022 return dpp_set_pubkey_point_group(group, x, y, len);
6023}
6024
6025
6026static EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
6027 const u8 *mac_init, const char *code,
6028 const char *identifier, BN_CTX *bnctx,
6029 const EC_GROUP **ret_group)
6030{
6031 u8 hash[DPP_MAX_HASH_LEN];
6032 const u8 *addr[3];
6033 size_t len[3];
6034 unsigned int num_elem = 0;
6035 EC_POINT *Qi = NULL;
6036 EVP_PKEY *Pi = NULL;
6037 EC_KEY *Pi_ec = NULL;
6038 const EC_POINT *Pi_point;
6039 BIGNUM *hash_bn = NULL;
6040 const EC_GROUP *group = NULL;
6041 EC_GROUP *group2 = NULL;
6042
6043 /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
6044
6045 wpa_printf(MSG_DEBUG, "DPP: MAC-Initiator: " MACSTR, MAC2STR(mac_init));
6046 addr[num_elem] = mac_init;
6047 len[num_elem] = ETH_ALEN;
6048 num_elem++;
6049 if (identifier) {
6050 wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
6051 identifier);
6052 addr[num_elem] = (const u8 *) identifier;
6053 len[num_elem] = os_strlen(identifier);
6054 num_elem++;
6055 }
6056 wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
6057 addr[num_elem] = (const u8 *) code;
6058 len[num_elem] = os_strlen(code);
6059 num_elem++;
6060 if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
6061 goto fail;
6062 wpa_hexdump_key(MSG_DEBUG,
6063 "DPP: H(MAC-Initiator | [identifier |] code)",
6064 hash, curve->hash_len);
6065 Pi = dpp_pkex_get_role_elem(curve, 1);
6066 if (!Pi)
6067 goto fail;
6068 dpp_debug_print_key("DPP: Pi", Pi);
6069 Pi_ec = EVP_PKEY_get1_EC_KEY(Pi);
6070 if (!Pi_ec)
6071 goto fail;
6072 Pi_point = EC_KEY_get0_public_key(Pi_ec);
6073
6074 group = EC_KEY_get0_group(Pi_ec);
6075 if (!group)
6076 goto fail;
6077 group2 = EC_GROUP_dup(group);
6078 if (!group2)
6079 goto fail;
6080 Qi = EC_POINT_new(group2);
6081 if (!Qi) {
6082 EC_GROUP_free(group2);
6083 goto fail;
6084 }
6085 hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
6086 if (!hash_bn ||
6087 EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1)
6088 goto fail;
6089 if (EC_POINT_is_at_infinity(group, Qi)) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07006090 wpa_printf(MSG_INFO, "DPP: Qi is the point-at-infinity");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006091 goto fail;
6092 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07006093 dpp_debug_print_point("DPP: Qi", group, Qi);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006094out:
6095 EC_KEY_free(Pi_ec);
6096 EVP_PKEY_free(Pi);
6097 BN_clear_free(hash_bn);
6098 if (ret_group)
6099 *ret_group = group2;
6100 return Qi;
6101fail:
6102 EC_POINT_free(Qi);
6103 Qi = NULL;
6104 goto out;
6105}
6106
6107
6108static EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
6109 const u8 *mac_resp, const char *code,
6110 const char *identifier, BN_CTX *bnctx,
6111 const EC_GROUP **ret_group)
6112{
6113 u8 hash[DPP_MAX_HASH_LEN];
6114 const u8 *addr[3];
6115 size_t len[3];
6116 unsigned int num_elem = 0;
6117 EC_POINT *Qr = NULL;
6118 EVP_PKEY *Pr = NULL;
6119 EC_KEY *Pr_ec = NULL;
6120 const EC_POINT *Pr_point;
6121 BIGNUM *hash_bn = NULL;
6122 const EC_GROUP *group = NULL;
6123 EC_GROUP *group2 = NULL;
6124
6125 /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
6126
6127 wpa_printf(MSG_DEBUG, "DPP: MAC-Responder: " MACSTR, MAC2STR(mac_resp));
6128 addr[num_elem] = mac_resp;
6129 len[num_elem] = ETH_ALEN;
6130 num_elem++;
6131 if (identifier) {
6132 wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
6133 identifier);
6134 addr[num_elem] = (const u8 *) identifier;
6135 len[num_elem] = os_strlen(identifier);
6136 num_elem++;
6137 }
6138 wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
6139 addr[num_elem] = (const u8 *) code;
6140 len[num_elem] = os_strlen(code);
6141 num_elem++;
6142 if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
6143 goto fail;
6144 wpa_hexdump_key(MSG_DEBUG,
6145 "DPP: H(MAC-Responder | [identifier |] code)",
6146 hash, curve->hash_len);
6147 Pr = dpp_pkex_get_role_elem(curve, 0);
6148 if (!Pr)
6149 goto fail;
6150 dpp_debug_print_key("DPP: Pr", Pr);
6151 Pr_ec = EVP_PKEY_get1_EC_KEY(Pr);
6152 if (!Pr_ec)
6153 goto fail;
6154 Pr_point = EC_KEY_get0_public_key(Pr_ec);
6155
6156 group = EC_KEY_get0_group(Pr_ec);
6157 if (!group)
6158 goto fail;
6159 group2 = EC_GROUP_dup(group);
6160 if (!group2)
6161 goto fail;
6162 Qr = EC_POINT_new(group2);
6163 if (!Qr) {
6164 EC_GROUP_free(group2);
6165 goto fail;
6166 }
6167 hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
6168 if (!hash_bn ||
6169 EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1)
6170 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07006171 if (EC_POINT_is_at_infinity(group, Qr)) {
6172 wpa_printf(MSG_INFO, "DPP: Qr is the point-at-infinity");
6173 goto fail;
6174 }
6175 dpp_debug_print_point("DPP: Qr", group, Qr);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006176out:
6177 EC_KEY_free(Pr_ec);
6178 EVP_PKEY_free(Pr);
6179 BN_clear_free(hash_bn);
6180 if (ret_group)
6181 *ret_group = group2;
6182 return Qr;
6183fail:
6184 EC_POINT_free(Qr);
6185 Qr = NULL;
6186 goto out;
6187}
6188
6189
Roshan Pius3a1667e2018-07-03 15:17:14 -07006190#ifdef CONFIG_TESTING_OPTIONS
6191static int dpp_test_gen_invalid_key(struct wpabuf *msg,
6192 const struct dpp_curve_params *curve)
6193{
6194 BN_CTX *ctx;
6195 BIGNUM *x, *y;
6196 int ret = -1;
6197 EC_GROUP *group;
6198 EC_POINT *point;
6199
6200 group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
6201 if (!group)
6202 return -1;
6203
6204 ctx = BN_CTX_new();
6205 point = EC_POINT_new(group);
6206 x = BN_new();
6207 y = BN_new();
6208 if (!ctx || !point || !x || !y)
6209 goto fail;
6210
6211 if (BN_rand(x, curve->prime_len * 8, 0, 0) != 1)
6212 goto fail;
6213
6214 /* Generate a random y coordinate that results in a point that is not
6215 * on the curve. */
6216 for (;;) {
6217 if (BN_rand(y, curve->prime_len * 8, 0, 0) != 1)
6218 goto fail;
6219
6220 if (EC_POINT_set_affine_coordinates_GFp(group, point, x, y,
6221 ctx) != 1) {
6222#if OPENSSL_VERSION_NUMBER >= 0x10101000L || defined(OPENSSL_IS_BORINGSSL)
6223 /* Unlike older OpenSSL versions, OpenSSL 1.1.1 and BoringSSL
6224 * return an error from EC_POINT_set_affine_coordinates_GFp()
6225 * when the point is not on the curve. */
6226 break;
6227#else /* >=1.1.1 or OPENSSL_IS_BORINGSSL */
6228 goto fail;
6229#endif /* >= 1.1.1 or OPENSSL_IS_BORINGSSL */
6230 }
6231
6232 if (!EC_POINT_is_on_curve(group, point, ctx))
6233 break;
6234 }
6235
6236 if (dpp_bn2bin_pad(x, wpabuf_put(msg, curve->prime_len),
6237 curve->prime_len) < 0 ||
6238 dpp_bn2bin_pad(y, wpabuf_put(msg, curve->prime_len),
6239 curve->prime_len) < 0)
6240 goto fail;
6241
6242 ret = 0;
6243fail:
6244 if (ret < 0)
6245 wpa_printf(MSG_INFO, "DPP: Failed to generate invalid key");
6246 BN_free(x);
6247 BN_free(y);
6248 EC_POINT_free(point);
6249 BN_CTX_free(ctx);
6250
6251 return ret;
6252}
6253#endif /* CONFIG_TESTING_OPTIONS */
6254
6255
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006256static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
6257{
6258 EC_KEY *X_ec = NULL;
6259 const EC_POINT *X_point;
6260 BN_CTX *bnctx = NULL;
6261 const EC_GROUP *group;
6262 EC_POINT *Qi = NULL, *M = NULL;
6263 struct wpabuf *M_buf = NULL;
6264 BIGNUM *Mx = NULL, *My = NULL;
6265 struct wpabuf *msg = NULL;
6266 size_t attr_len;
6267 const struct dpp_curve_params *curve = pkex->own_bi->curve;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006268
6269 wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request");
6270
6271 /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
6272 bnctx = BN_CTX_new();
6273 if (!bnctx)
6274 goto fail;
6275 Qi = dpp_pkex_derive_Qi(curve, pkex->own_mac, pkex->code,
6276 pkex->identifier, bnctx, &group);
6277 if (!Qi)
6278 goto fail;
6279
6280 /* Generate a random ephemeral keypair x/X */
Roshan Pius3a1667e2018-07-03 15:17:14 -07006281#ifdef CONFIG_TESTING_OPTIONS
6282 if (dpp_pkex_ephemeral_key_override_len) {
6283 const struct dpp_curve_params *tmp_curve;
6284
6285 wpa_printf(MSG_INFO,
6286 "DPP: TESTING - override ephemeral key x/X");
6287 pkex->x = dpp_set_keypair(&tmp_curve,
6288 dpp_pkex_ephemeral_key_override,
6289 dpp_pkex_ephemeral_key_override_len);
6290 } else {
6291 pkex->x = dpp_gen_keypair(curve);
6292 }
6293#else /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006294 pkex->x = dpp_gen_keypair(curve);
Roshan Pius3a1667e2018-07-03 15:17:14 -07006295#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006296 if (!pkex->x)
6297 goto fail;
6298
6299 /* M = X + Qi */
6300 X_ec = EVP_PKEY_get1_EC_KEY(pkex->x);
6301 if (!X_ec)
6302 goto fail;
6303 X_point = EC_KEY_get0_public_key(X_ec);
6304 if (!X_point)
6305 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07006306 dpp_debug_print_point("DPP: X", group, X_point);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006307 M = EC_POINT_new(group);
6308 Mx = BN_new();
6309 My = BN_new();
6310 if (!M || !Mx || !My ||
6311 EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 ||
6312 EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1)
6313 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07006314 dpp_debug_print_point("DPP: M", group, M);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006315
6316 /* Initiator -> Responder: group, [identifier,] M */
6317 attr_len = 4 + 2;
6318 if (pkex->identifier)
6319 attr_len += 4 + os_strlen(pkex->identifier);
6320 attr_len += 4 + 2 * curve->prime_len;
6321 msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_REQ, attr_len);
6322 if (!msg)
6323 goto fail;
6324
Roshan Pius3a1667e2018-07-03 15:17:14 -07006325#ifdef CONFIG_TESTING_OPTIONS
6326 if (dpp_test == DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ) {
6327 wpa_printf(MSG_INFO, "DPP: TESTING - no Finite Cyclic Group");
6328 goto skip_finite_cyclic_group;
6329 }
6330#endif /* CONFIG_TESTING_OPTIONS */
6331
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006332 /* Finite Cyclic Group attribute */
6333 wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
6334 wpabuf_put_le16(msg, 2);
6335 wpabuf_put_le16(msg, curve->ike_group);
6336
Roshan Pius3a1667e2018-07-03 15:17:14 -07006337#ifdef CONFIG_TESTING_OPTIONS
6338skip_finite_cyclic_group:
6339#endif /* CONFIG_TESTING_OPTIONS */
6340
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006341 /* Code Identifier attribute */
6342 if (pkex->identifier) {
6343 wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
6344 wpabuf_put_le16(msg, os_strlen(pkex->identifier));
6345 wpabuf_put_str(msg, pkex->identifier);
6346 }
6347
Roshan Pius3a1667e2018-07-03 15:17:14 -07006348#ifdef CONFIG_TESTING_OPTIONS
6349 if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
6350 wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
6351 goto out;
6352 }
6353#endif /* CONFIG_TESTING_OPTIONS */
6354
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006355 /* M in Encrypted Key attribute */
6356 wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
6357 wpabuf_put_le16(msg, 2 * curve->prime_len);
6358
Roshan Pius3a1667e2018-07-03 15:17:14 -07006359#ifdef CONFIG_TESTING_OPTIONS
6360 if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
6361 wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
6362 if (dpp_test_gen_invalid_key(msg, curve) < 0)
6363 goto fail;
6364 goto out;
6365 }
6366#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006367
Roshan Pius3a1667e2018-07-03 15:17:14 -07006368 if (dpp_bn2bin_pad(Mx, wpabuf_put(msg, curve->prime_len),
6369 curve->prime_len) < 0 ||
6370 dpp_bn2bin_pad(Mx, pkex->Mx, curve->prime_len) < 0 ||
6371 dpp_bn2bin_pad(My, wpabuf_put(msg, curve->prime_len),
6372 curve->prime_len) < 0)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006373 goto fail;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006374
6375out:
6376 wpabuf_free(M_buf);
6377 EC_KEY_free(X_ec);
6378 EC_POINT_free(M);
6379 EC_POINT_free(Qi);
6380 BN_clear_free(Mx);
6381 BN_clear_free(My);
6382 BN_CTX_free(bnctx);
6383 return msg;
6384fail:
6385 wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request");
6386 wpabuf_free(msg);
6387 msg = NULL;
6388 goto out;
6389}
6390
6391
Roshan Pius3a1667e2018-07-03 15:17:14 -07006392static void dpp_pkex_fail(struct dpp_pkex *pkex, const char *txt)
6393{
6394 wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
6395}
6396
6397
6398struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006399 const u8 *own_mac,
6400 const char *identifier,
6401 const char *code)
6402{
6403 struct dpp_pkex *pkex;
6404
Roshan Pius3a1667e2018-07-03 15:17:14 -07006405#ifdef CONFIG_TESTING_OPTIONS
6406 if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
6407 wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
6408 MAC2STR(dpp_pkex_own_mac_override));
6409 own_mac = dpp_pkex_own_mac_override;
6410 }
6411#endif /* CONFIG_TESTING_OPTIONS */
6412
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006413 pkex = os_zalloc(sizeof(*pkex));
6414 if (!pkex)
6415 return NULL;
Roshan Pius3a1667e2018-07-03 15:17:14 -07006416 pkex->msg_ctx = msg_ctx;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006417 pkex->initiator = 1;
6418 pkex->own_bi = bi;
6419 os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
6420 if (identifier) {
6421 pkex->identifier = os_strdup(identifier);
6422 if (!pkex->identifier)
6423 goto fail;
6424 }
6425 pkex->code = os_strdup(code);
6426 if (!pkex->code)
6427 goto fail;
6428 pkex->exchange_req = dpp_pkex_build_exchange_req(pkex);
6429 if (!pkex->exchange_req)
6430 goto fail;
6431 return pkex;
6432fail:
6433 dpp_pkex_free(pkex);
6434 return NULL;
6435}
6436
6437
Roshan Pius3a1667e2018-07-03 15:17:14 -07006438static struct wpabuf *
6439dpp_pkex_build_exchange_resp(struct dpp_pkex *pkex,
6440 enum dpp_status_error status,
6441 const BIGNUM *Nx, const BIGNUM *Ny)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006442{
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006443 struct wpabuf *msg = NULL;
6444 size_t attr_len;
Roshan Pius3a1667e2018-07-03 15:17:14 -07006445 const struct dpp_curve_params *curve = pkex->own_bi->curve;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006446
6447 /* Initiator -> Responder: DPP Status, [identifier,] N */
6448 attr_len = 4 + 1;
Roshan Pius3a1667e2018-07-03 15:17:14 -07006449 if (pkex->identifier)
6450 attr_len += 4 + os_strlen(pkex->identifier);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006451 attr_len += 4 + 2 * curve->prime_len;
6452 msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len);
6453 if (!msg)
6454 goto fail;
6455
Roshan Pius3a1667e2018-07-03 15:17:14 -07006456#ifdef CONFIG_TESTING_OPTIONS
6457 if (dpp_test == DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP) {
6458 wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
6459 goto skip_status;
6460 }
6461
6462 if (dpp_test == DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP) {
6463 wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
6464 status = 255;
6465 }
6466#endif /* CONFIG_TESTING_OPTIONS */
6467
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006468 /* DPP Status */
Roshan Pius3a1667e2018-07-03 15:17:14 -07006469 dpp_build_attr_status(msg, status);
6470
6471#ifdef CONFIG_TESTING_OPTIONS
6472skip_status:
6473#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006474
6475 /* Code Identifier attribute */
6476 if (pkex->identifier) {
6477 wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
6478 wpabuf_put_le16(msg, os_strlen(pkex->identifier));
6479 wpabuf_put_str(msg, pkex->identifier);
6480 }
6481
Roshan Pius3a1667e2018-07-03 15:17:14 -07006482 if (status != DPP_STATUS_OK)
6483 goto skip_encrypted_key;
6484
6485#ifdef CONFIG_TESTING_OPTIONS
6486 if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
6487 wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
6488 goto skip_encrypted_key;
6489 }
6490#endif /* CONFIG_TESTING_OPTIONS */
6491
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006492 /* N in Encrypted Key attribute */
6493 wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
6494 wpabuf_put_le16(msg, 2 * curve->prime_len);
6495
Roshan Pius3a1667e2018-07-03 15:17:14 -07006496#ifdef CONFIG_TESTING_OPTIONS
6497 if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
6498 wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
6499 if (dpp_test_gen_invalid_key(msg, curve) < 0)
6500 goto fail;
6501 goto skip_encrypted_key;
6502 }
6503#endif /* CONFIG_TESTING_OPTIONS */
6504
6505 if (dpp_bn2bin_pad(Nx, wpabuf_put(msg, curve->prime_len),
6506 curve->prime_len) < 0 ||
6507 dpp_bn2bin_pad(Nx, pkex->Nx, curve->prime_len) < 0 ||
6508 dpp_bn2bin_pad(Ny, wpabuf_put(msg, curve->prime_len),
6509 curve->prime_len) < 0)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006510 goto fail;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006511
Roshan Pius3a1667e2018-07-03 15:17:14 -07006512skip_encrypted_key:
6513 if (status == DPP_STATUS_BAD_GROUP) {
6514 /* Finite Cyclic Group attribute */
6515 wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
6516 wpabuf_put_le16(msg, 2);
6517 wpabuf_put_le16(msg, curve->ike_group);
6518 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006519
Roshan Pius3a1667e2018-07-03 15:17:14 -07006520 return msg;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006521fail:
Roshan Pius3a1667e2018-07-03 15:17:14 -07006522 wpabuf_free(msg);
6523 return NULL;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006524}
6525
6526
6527static int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
6528 const u8 *Mx, size_t Mx_len,
6529 const u8 *Nx, size_t Nx_len,
6530 const char *code,
6531 const u8 *Kx, size_t Kx_len,
6532 u8 *z, unsigned int hash_len)
6533{
6534 u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
6535 int res;
6536 u8 *info, *pos;
6537 size_t info_len;
6538
6539 /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
6540 */
6541
6542 /* HKDF-Extract(<>, IKM=K.x) */
6543 os_memset(salt, 0, hash_len);
6544 if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0)
6545 return -1;
6546 wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
6547 prk, hash_len);
6548 info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code);
6549 info = os_malloc(info_len);
6550 if (!info)
6551 return -1;
6552 pos = info;
6553 os_memcpy(pos, mac_init, ETH_ALEN);
6554 pos += ETH_ALEN;
6555 os_memcpy(pos, mac_resp, ETH_ALEN);
6556 pos += ETH_ALEN;
6557 os_memcpy(pos, Mx, Mx_len);
6558 pos += Mx_len;
6559 os_memcpy(pos, Nx, Nx_len);
6560 pos += Nx_len;
6561 os_memcpy(pos, code, os_strlen(code));
6562
6563 /* HKDF-Expand(PRK, info, L) */
6564 if (hash_len == 32)
6565 res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len,
6566 z, hash_len);
6567 else if (hash_len == 48)
6568 res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len,
6569 z, hash_len);
6570 else if (hash_len == 64)
6571 res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len,
6572 z, hash_len);
6573 else
6574 res = -1;
6575 os_free(info);
6576 os_memset(prk, 0, hash_len);
6577 if (res < 0)
6578 return -1;
6579
6580 wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)",
6581 z, hash_len);
6582 return 0;
6583}
6584
6585
Roshan Pius3a1667e2018-07-03 15:17:14 -07006586struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
6587 struct dpp_bootstrap_info *bi,
6588 const u8 *own_mac,
6589 const u8 *peer_mac,
6590 const char *identifier,
6591 const char *code,
6592 const u8 *buf, size_t len)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006593{
Roshan Pius3a1667e2018-07-03 15:17:14 -07006594 const u8 *attr_group, *attr_id, *attr_key;
6595 u16 attr_group_len, attr_id_len, attr_key_len;
6596 const struct dpp_curve_params *curve = bi->curve;
6597 u16 ike_group;
6598 struct dpp_pkex *pkex = NULL;
6599 EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006600 BN_CTX *bnctx = NULL;
Roshan Pius3a1667e2018-07-03 15:17:14 -07006601 const EC_GROUP *group;
6602 BIGNUM *Mx = NULL, *My = NULL;
6603 EC_KEY *Y_ec = NULL, *X_ec = NULL;;
6604 const EC_POINT *Y_point;
6605 BIGNUM *Nx = NULL, *Ny = NULL;
6606 u8 Kx[DPP_MAX_SHARED_SECRET_LEN];
6607 size_t Kx_len;
6608 int res;
6609 EVP_PKEY_CTX *ctx = NULL;
6610
6611 if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) {
6612 wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
6613 "PKEX counter t limit reached - ignore message");
6614 return NULL;
6615 }
6616
6617#ifdef CONFIG_TESTING_OPTIONS
6618 if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
6619 wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
6620 MAC2STR(dpp_pkex_peer_mac_override));
6621 peer_mac = dpp_pkex_peer_mac_override;
6622 }
6623 if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
6624 wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
6625 MAC2STR(dpp_pkex_own_mac_override));
6626 own_mac = dpp_pkex_own_mac_override;
6627 }
6628#endif /* CONFIG_TESTING_OPTIONS */
6629
6630 attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER,
6631 &attr_id_len);
6632 if (!attr_id && identifier) {
6633 wpa_printf(MSG_DEBUG,
6634 "DPP: No PKEX code identifier received, but expected one");
6635 return NULL;
6636 }
6637 if (attr_id && identifier &&
6638 (os_strlen(identifier) != attr_id_len ||
6639 os_memcmp(identifier, attr_id, attr_id_len) != 0)) {
6640 wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
6641 return NULL;
6642 }
6643
6644 attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
6645 &attr_group_len);
6646 if (!attr_group || attr_group_len != 2) {
6647 wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
6648 "Missing or invalid Finite Cyclic Group attribute");
6649 return NULL;
6650 }
6651 ike_group = WPA_GET_LE16(attr_group);
6652 if (ike_group != curve->ike_group) {
6653 wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
6654 "Mismatching PKEX curve: peer=%u own=%u",
6655 ike_group, curve->ike_group);
6656 pkex = os_zalloc(sizeof(*pkex));
6657 if (!pkex)
6658 goto fail;
6659 pkex->own_bi = bi;
6660 pkex->failed = 1;
6661 pkex->exchange_resp = dpp_pkex_build_exchange_resp(
6662 pkex, DPP_STATUS_BAD_GROUP, NULL, NULL);
6663 if (!pkex->exchange_resp)
6664 goto fail;
6665 return pkex;
6666 }
6667
6668 /* M in Encrypted Key attribute */
6669 attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY,
6670 &attr_key_len);
6671 if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 ||
6672 attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) {
6673 wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
6674 "Missing Encrypted Key attribute");
6675 return NULL;
6676 }
6677
6678 /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
6679 bnctx = BN_CTX_new();
6680 if (!bnctx)
6681 goto fail;
6682 Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx,
6683 &group);
6684 if (!Qi)
6685 goto fail;
6686
6687 /* X' = M - Qi */
6688 X = EC_POINT_new(group);
6689 M = EC_POINT_new(group);
6690 Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
6691 My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
6692 if (!X || !M || !Mx || !My ||
6693 EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 ||
6694 EC_POINT_is_at_infinity(group, M) ||
6695 !EC_POINT_is_on_curve(group, M, bnctx) ||
6696 EC_POINT_invert(group, Qi, bnctx) != 1 ||
6697 EC_POINT_add(group, X, M, Qi, bnctx) != 1 ||
6698 EC_POINT_is_at_infinity(group, X) ||
6699 !EC_POINT_is_on_curve(group, X, bnctx)) {
6700 wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
6701 "Invalid Encrypted Key value");
6702 bi->pkex_t++;
6703 goto fail;
6704 }
6705 dpp_debug_print_point("DPP: M", group, M);
6706 dpp_debug_print_point("DPP: X'", group, X);
6707
6708 pkex = os_zalloc(sizeof(*pkex));
6709 if (!pkex)
6710 goto fail;
6711 pkex->t = bi->pkex_t;
6712 pkex->msg_ctx = msg_ctx;
6713 pkex->own_bi = bi;
6714 os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
6715 os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
6716 if (identifier) {
6717 pkex->identifier = os_strdup(identifier);
6718 if (!pkex->identifier)
6719 goto fail;
6720 }
6721 pkex->code = os_strdup(code);
6722 if (!pkex->code)
6723 goto fail;
6724
6725 os_memcpy(pkex->Mx, attr_key, attr_key_len / 2);
6726
6727 X_ec = EC_KEY_new();
6728 if (!X_ec ||
6729 EC_KEY_set_group(X_ec, group) != 1 ||
6730 EC_KEY_set_public_key(X_ec, X) != 1)
6731 goto fail;
6732 pkex->x = EVP_PKEY_new();
6733 if (!pkex->x ||
6734 EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1)
6735 goto fail;
6736
6737 /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
6738 Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL);
6739 if (!Qr)
6740 goto fail;
6741
6742 /* Generate a random ephemeral keypair y/Y */
6743#ifdef CONFIG_TESTING_OPTIONS
6744 if (dpp_pkex_ephemeral_key_override_len) {
6745 const struct dpp_curve_params *tmp_curve;
6746
6747 wpa_printf(MSG_INFO,
6748 "DPP: TESTING - override ephemeral key y/Y");
6749 pkex->y = dpp_set_keypair(&tmp_curve,
6750 dpp_pkex_ephemeral_key_override,
6751 dpp_pkex_ephemeral_key_override_len);
6752 } else {
6753 pkex->y = dpp_gen_keypair(curve);
6754 }
6755#else /* CONFIG_TESTING_OPTIONS */
6756 pkex->y = dpp_gen_keypair(curve);
6757#endif /* CONFIG_TESTING_OPTIONS */
6758 if (!pkex->y)
6759 goto fail;
6760
6761 /* N = Y + Qr */
6762 Y_ec = EVP_PKEY_get1_EC_KEY(pkex->y);
6763 if (!Y_ec)
6764 goto fail;
6765 Y_point = EC_KEY_get0_public_key(Y_ec);
6766 if (!Y_point)
6767 goto fail;
6768 dpp_debug_print_point("DPP: Y", group, Y_point);
6769 N = EC_POINT_new(group);
6770 Nx = BN_new();
6771 Ny = BN_new();
6772 if (!N || !Nx || !Ny ||
6773 EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 ||
6774 EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1)
6775 goto fail;
6776 dpp_debug_print_point("DPP: N", group, N);
6777
6778 pkex->exchange_resp = dpp_pkex_build_exchange_resp(pkex, DPP_STATUS_OK,
6779 Nx, Ny);
6780 if (!pkex->exchange_resp)
6781 goto fail;
6782
6783 /* K = y * X' */
6784 ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
6785 if (!ctx ||
6786 EVP_PKEY_derive_init(ctx) != 1 ||
6787 EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 ||
6788 EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 ||
6789 Kx_len > DPP_MAX_SHARED_SECRET_LEN ||
6790 EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) {
6791 wpa_printf(MSG_ERROR,
6792 "DPP: Failed to derive ECDH shared secret: %s",
6793 ERR_error_string(ERR_get_error(), NULL));
6794 goto fail;
6795 }
6796
6797 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
6798 Kx, Kx_len);
6799
6800 /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
6801 */
6802 res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac,
6803 pkex->Mx, curve->prime_len,
6804 pkex->Nx, curve->prime_len, pkex->code,
6805 Kx, Kx_len, pkex->z, curve->hash_len);
6806 os_memset(Kx, 0, Kx_len);
6807 if (res < 0)
6808 goto fail;
6809
6810 pkex->exchange_done = 1;
6811
6812out:
6813 EVP_PKEY_CTX_free(ctx);
6814 BN_CTX_free(bnctx);
6815 EC_POINT_free(Qi);
6816 EC_POINT_free(Qr);
6817 BN_free(Mx);
6818 BN_free(My);
6819 BN_free(Nx);
6820 BN_free(Ny);
6821 EC_POINT_free(M);
6822 EC_POINT_free(N);
6823 EC_POINT_free(X);
6824 EC_KEY_free(X_ec);
6825 EC_KEY_free(Y_ec);
6826 return pkex;
6827fail:
6828 wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed");
6829 dpp_pkex_free(pkex);
6830 pkex = NULL;
6831 goto out;
6832}
6833
6834
6835static struct wpabuf *
6836dpp_pkex_build_commit_reveal_req(struct dpp_pkex *pkex,
6837 const struct wpabuf *A_pub, const u8 *u)
6838{
6839 const struct dpp_curve_params *curve = pkex->own_bi->curve;
6840 struct wpabuf *msg = NULL;
6841 size_t clear_len, attr_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006842 struct wpabuf *clear = NULL;
6843 u8 *wrapped;
Roshan Pius3a1667e2018-07-03 15:17:14 -07006844 u8 octet;
6845 const u8 *addr[2];
6846 size_t len[2];
6847
6848 /* {A, u, [bootstrapping info]}z */
6849 clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
6850 clear = wpabuf_alloc(clear_len);
6851 attr_len = 4 + clear_len + AES_BLOCK_SIZE;
6852#ifdef CONFIG_TESTING_OPTIONS
6853 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ)
6854 attr_len += 5;
6855#endif /* CONFIG_TESTING_OPTIONS */
6856 msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len);
6857 if (!clear || !msg)
6858 goto fail;
6859
6860#ifdef CONFIG_TESTING_OPTIONS
6861 if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ) {
6862 wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
6863 goto skip_bootstrap_key;
6864 }
6865 if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ) {
6866 wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
6867 wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
6868 wpabuf_put_le16(clear, 2 * curve->prime_len);
6869 if (dpp_test_gen_invalid_key(clear, curve) < 0)
6870 goto fail;
6871 goto skip_bootstrap_key;
6872 }
6873#endif /* CONFIG_TESTING_OPTIONS */
6874
6875 /* A in Bootstrap Key attribute */
6876 wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
6877 wpabuf_put_le16(clear, wpabuf_len(A_pub));
6878 wpabuf_put_buf(clear, A_pub);
6879
6880#ifdef CONFIG_TESTING_OPTIONS
6881skip_bootstrap_key:
6882 if (dpp_test == DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ) {
6883 wpa_printf(MSG_INFO, "DPP: TESTING - no I-Auth tag");
6884 goto skip_i_auth_tag;
6885 }
6886 if (dpp_test == DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ) {
6887 wpa_printf(MSG_INFO, "DPP: TESTING - I-Auth tag mismatch");
6888 wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
6889 wpabuf_put_le16(clear, curve->hash_len);
6890 wpabuf_put_data(clear, u, curve->hash_len - 1);
6891 wpabuf_put_u8(clear, u[curve->hash_len - 1] ^ 0x01);
6892 goto skip_i_auth_tag;
6893 }
6894#endif /* CONFIG_TESTING_OPTIONS */
6895
6896 /* u in I-Auth tag attribute */
6897 wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
6898 wpabuf_put_le16(clear, curve->hash_len);
6899 wpabuf_put_data(clear, u, curve->hash_len);
6900
6901#ifdef CONFIG_TESTING_OPTIONS
6902skip_i_auth_tag:
6903 if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ) {
6904 wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
6905 goto skip_wrapped_data;
6906 }
6907#endif /* CONFIG_TESTING_OPTIONS */
6908
6909 addr[0] = wpabuf_head_u8(msg) + 2;
6910 len[0] = DPP_HDR_LEN;
6911 octet = 0;
6912 addr[1] = &octet;
6913 len[1] = sizeof(octet);
6914 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
6915 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
6916
6917 wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
6918 wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
6919 wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
6920
6921 wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
6922 if (aes_siv_encrypt(pkex->z, curve->hash_len,
6923 wpabuf_head(clear), wpabuf_len(clear),
6924 2, addr, len, wrapped) < 0)
6925 goto fail;
6926 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
6927 wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
6928
6929#ifdef CONFIG_TESTING_OPTIONS
6930 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) {
6931 wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
6932 dpp_build_attr_status(msg, DPP_STATUS_OK);
6933 }
6934skip_wrapped_data:
6935#endif /* CONFIG_TESTING_OPTIONS */
6936
6937out:
6938 wpabuf_free(clear);
6939 return msg;
6940
6941fail:
6942 wpabuf_free(msg);
6943 msg = NULL;
6944 goto out;
6945}
6946
6947
6948struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
6949 const u8 *peer_mac,
6950 const u8 *buf, size_t buflen)
6951{
6952 const u8 *attr_status, *attr_id, *attr_key, *attr_group;
6953 u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len;
6954 const EC_GROUP *group;
6955 BN_CTX *bnctx = NULL;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006956 struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
6957 const struct dpp_curve_params *curve = pkex->own_bi->curve;
6958 EC_POINT *Qr = NULL, *Y = NULL, *N = NULL;
6959 BIGNUM *Nx = NULL, *Ny = NULL;
6960 EVP_PKEY_CTX *ctx = NULL;
6961 EC_KEY *Y_ec = NULL;
6962 size_t Jx_len, Kx_len;
6963 u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
6964 const u8 *addr[4];
6965 size_t len[4];
6966 u8 u[DPP_MAX_HASH_LEN];
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006967 int res;
6968
Roshan Pius3a1667e2018-07-03 15:17:14 -07006969 if (pkex->failed || pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
6970 return NULL;
6971
6972#ifdef CONFIG_TESTING_OPTIONS
6973 if (dpp_test == DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP) {
6974 wpa_printf(MSG_INFO,
6975 "DPP: TESTING - stop at PKEX Exchange Response");
6976 pkex->failed = 1;
6977 return NULL;
6978 }
6979
6980 if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
6981 wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
6982 MAC2STR(dpp_pkex_peer_mac_override));
6983 peer_mac = dpp_pkex_peer_mac_override;
6984 }
6985#endif /* CONFIG_TESTING_OPTIONS */
6986
6987 os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
6988
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006989 attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS,
6990 &attr_status_len);
6991 if (!attr_status || attr_status_len != 1) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07006992 dpp_pkex_fail(pkex, "No DPP Status attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006993 return NULL;
6994 }
6995 wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]);
Roshan Pius3a1667e2018-07-03 15:17:14 -07006996
6997 if (attr_status[0] == DPP_STATUS_BAD_GROUP) {
6998 attr_group = dpp_get_attr(buf, buflen,
6999 DPP_ATTR_FINITE_CYCLIC_GROUP,
7000 &attr_group_len);
7001 if (attr_group && attr_group_len == 2) {
7002 wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
7003 "Peer indicated mismatching PKEX group - proposed %u",
7004 WPA_GET_LE16(attr_group));
7005 return NULL;
7006 }
7007 }
7008
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007009 if (attr_status[0] != DPP_STATUS_OK) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007010 dpp_pkex_fail(pkex, "PKEX failed (peer indicated failure)");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007011 return NULL;
7012 }
7013
7014 attr_id = dpp_get_attr(buf, buflen, DPP_ATTR_CODE_IDENTIFIER,
7015 &attr_id_len);
7016 if (!attr_id && pkex->identifier) {
7017 wpa_printf(MSG_DEBUG,
7018 "DPP: No PKEX code identifier received, but expected one");
7019 return NULL;
7020 }
7021 if (attr_id && pkex->identifier &&
7022 (os_strlen(pkex->identifier) != attr_id_len ||
7023 os_memcmp(pkex->identifier, attr_id, attr_id_len) != 0)) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007024 dpp_pkex_fail(pkex, "PKEX code identifier mismatch");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007025 return NULL;
7026 }
7027
7028 /* N in Encrypted Key attribute */
7029 attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY,
7030 &attr_key_len);
7031 if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007032 dpp_pkex_fail(pkex, "Missing Encrypted Key attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007033 return NULL;
7034 }
7035
7036 /* Qr = H(MAC-Responder | [identifier |] code) * Pr */
7037 bnctx = BN_CTX_new();
7038 if (!bnctx)
7039 goto fail;
7040 Qr = dpp_pkex_derive_Qr(curve, pkex->peer_mac, pkex->code,
7041 pkex->identifier, bnctx, &group);
7042 if (!Qr)
7043 goto fail;
7044
7045 /* Y' = N - Qr */
7046 Y = EC_POINT_new(group);
7047 N = EC_POINT_new(group);
7048 Nx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
7049 Ny = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
7050 if (!Y || !N || !Nx || !Ny ||
7051 EC_POINT_set_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1 ||
7052 EC_POINT_is_at_infinity(group, N) ||
7053 !EC_POINT_is_on_curve(group, N, bnctx) ||
7054 EC_POINT_invert(group, Qr, bnctx) != 1 ||
7055 EC_POINT_add(group, Y, N, Qr, bnctx) != 1 ||
7056 EC_POINT_is_at_infinity(group, Y) ||
Roshan Pius3a1667e2018-07-03 15:17:14 -07007057 !EC_POINT_is_on_curve(group, Y, bnctx)) {
7058 dpp_pkex_fail(pkex, "Invalid Encrypted Key value");
7059 pkex->t++;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007060 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07007061 }
7062 dpp_debug_print_point("DPP: N", group, N);
7063 dpp_debug_print_point("DPP: Y'", group, Y);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007064
7065 pkex->exchange_done = 1;
7066
7067 /* ECDH: J = a * Y’ */
7068 Y_ec = EC_KEY_new();
7069 if (!Y_ec ||
7070 EC_KEY_set_group(Y_ec, group) != 1 ||
7071 EC_KEY_set_public_key(Y_ec, Y) != 1)
7072 goto fail;
7073 pkex->y = EVP_PKEY_new();
7074 if (!pkex->y ||
7075 EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1)
7076 goto fail;
7077 ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL);
7078 if (!ctx ||
7079 EVP_PKEY_derive_init(ctx) != 1 ||
7080 EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 ||
7081 EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 ||
7082 Jx_len > DPP_MAX_SHARED_SECRET_LEN ||
7083 EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) {
7084 wpa_printf(MSG_ERROR,
7085 "DPP: Failed to derive ECDH shared secret: %s",
7086 ERR_error_string(ERR_get_error(), NULL));
7087 goto fail;
7088 }
7089
7090 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
7091 Jx, Jx_len);
7092
7093 /* u = HMAC(J.x, MAC-Initiator | A.x | Y’.x | X.x ) */
7094 A_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
7095 Y_pub = dpp_get_pubkey_point(pkex->y, 0);
7096 X_pub = dpp_get_pubkey_point(pkex->x, 0);
7097 if (!A_pub || !Y_pub || !X_pub)
7098 goto fail;
7099 addr[0] = pkex->own_mac;
7100 len[0] = ETH_ALEN;
7101 addr[1] = wpabuf_head(A_pub);
7102 len[1] = wpabuf_len(A_pub) / 2;
7103 addr[2] = wpabuf_head(Y_pub);
7104 len[2] = wpabuf_len(Y_pub) / 2;
7105 addr[3] = wpabuf_head(X_pub);
7106 len[3] = wpabuf_len(X_pub) / 2;
7107 if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
7108 goto fail;
7109 wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len);
7110
7111 /* K = x * Y’ */
7112 EVP_PKEY_CTX_free(ctx);
7113 ctx = EVP_PKEY_CTX_new(pkex->x, NULL);
7114 if (!ctx ||
7115 EVP_PKEY_derive_init(ctx) != 1 ||
7116 EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 ||
7117 EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 ||
7118 Kx_len > DPP_MAX_SHARED_SECRET_LEN ||
7119 EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) {
7120 wpa_printf(MSG_ERROR,
7121 "DPP: Failed to derive ECDH shared secret: %s",
7122 ERR_error_string(ERR_get_error(), NULL));
7123 goto fail;
7124 }
7125
7126 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
7127 Kx, Kx_len);
7128
7129 /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
7130 */
7131 res = dpp_pkex_derive_z(pkex->own_mac, pkex->peer_mac,
7132 pkex->Mx, curve->prime_len,
7133 attr_key /* N.x */, attr_key_len / 2,
7134 pkex->code, Kx, Kx_len,
7135 pkex->z, curve->hash_len);
7136 os_memset(Kx, 0, Kx_len);
7137 if (res < 0)
7138 goto fail;
7139
Roshan Pius3a1667e2018-07-03 15:17:14 -07007140 msg = dpp_pkex_build_commit_reveal_req(pkex, A_pub, u);
7141 if (!msg)
7142 goto fail;
7143
7144out:
7145 wpabuf_free(A_pub);
7146 wpabuf_free(X_pub);
7147 wpabuf_free(Y_pub);
7148 EC_POINT_free(Qr);
7149 EC_POINT_free(Y);
7150 EC_POINT_free(N);
7151 BN_free(Nx);
7152 BN_free(Ny);
7153 EC_KEY_free(Y_ec);
7154 EVP_PKEY_CTX_free(ctx);
7155 BN_CTX_free(bnctx);
7156 return msg;
7157fail:
7158 wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed");
7159 goto out;
7160}
7161
7162
7163static struct wpabuf *
7164dpp_pkex_build_commit_reveal_resp(struct dpp_pkex *pkex,
7165 const struct wpabuf *B_pub, const u8 *v)
7166{
7167 const struct dpp_curve_params *curve = pkex->own_bi->curve;
7168 struct wpabuf *msg = NULL;
7169 const u8 *addr[2];
7170 size_t len[2];
7171 u8 octet;
7172 u8 *wrapped;
7173 struct wpabuf *clear = NULL;
7174 size_t clear_len, attr_len;
7175
7176 /* {B, v [bootstrapping info]}z */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007177 clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
7178 clear = wpabuf_alloc(clear_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07007179 attr_len = 4 + clear_len + AES_BLOCK_SIZE;
7180#ifdef CONFIG_TESTING_OPTIONS
7181 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP)
7182 attr_len += 5;
7183#endif /* CONFIG_TESTING_OPTIONS */
7184 msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007185 if (!clear || !msg)
7186 goto fail;
7187
Roshan Pius3a1667e2018-07-03 15:17:14 -07007188#ifdef CONFIG_TESTING_OPTIONS
7189 if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP) {
7190 wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
7191 goto skip_bootstrap_key;
7192 }
7193 if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP) {
7194 wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
7195 wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
7196 wpabuf_put_le16(clear, 2 * curve->prime_len);
7197 if (dpp_test_gen_invalid_key(clear, curve) < 0)
7198 goto fail;
7199 goto skip_bootstrap_key;
7200 }
7201#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007202
Roshan Pius3a1667e2018-07-03 15:17:14 -07007203 /* B in Bootstrap Key attribute */
7204 wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
7205 wpabuf_put_le16(clear, wpabuf_len(B_pub));
7206 wpabuf_put_buf(clear, B_pub);
7207
7208#ifdef CONFIG_TESTING_OPTIONS
7209skip_bootstrap_key:
7210 if (dpp_test == DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP) {
7211 wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth tag");
7212 goto skip_r_auth_tag;
7213 }
7214 if (dpp_test == DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP) {
7215 wpa_printf(MSG_INFO, "DPP: TESTING - R-Auth tag mismatch");
7216 wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
7217 wpabuf_put_le16(clear, curve->hash_len);
7218 wpabuf_put_data(clear, v, curve->hash_len - 1);
7219 wpabuf_put_u8(clear, v[curve->hash_len - 1] ^ 0x01);
7220 goto skip_r_auth_tag;
7221 }
7222#endif /* CONFIG_TESTING_OPTIONS */
7223
7224 /* v in R-Auth tag attribute */
7225 wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007226 wpabuf_put_le16(clear, curve->hash_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07007227 wpabuf_put_data(clear, v, curve->hash_len);
7228
7229#ifdef CONFIG_TESTING_OPTIONS
7230skip_r_auth_tag:
7231 if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP) {
7232 wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
7233 goto skip_wrapped_data;
7234 }
7235#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007236
7237 addr[0] = wpabuf_head_u8(msg) + 2;
7238 len[0] = DPP_HDR_LEN;
Roshan Pius3a1667e2018-07-03 15:17:14 -07007239 octet = 1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007240 addr[1] = &octet;
7241 len[1] = sizeof(octet);
7242 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
7243 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
7244
7245 wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
7246 wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
7247 wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
7248
7249 wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
7250 if (aes_siv_encrypt(pkex->z, curve->hash_len,
7251 wpabuf_head(clear), wpabuf_len(clear),
7252 2, addr, len, wrapped) < 0)
7253 goto fail;
7254 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
7255 wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
7256
Roshan Pius3a1667e2018-07-03 15:17:14 -07007257#ifdef CONFIG_TESTING_OPTIONS
7258 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP) {
7259 wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
7260 dpp_build_attr_status(msg, DPP_STATUS_OK);
7261 }
7262skip_wrapped_data:
7263#endif /* CONFIG_TESTING_OPTIONS */
7264
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007265out:
7266 wpabuf_free(clear);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007267 return msg;
Roshan Pius3a1667e2018-07-03 15:17:14 -07007268
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007269fail:
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007270 wpabuf_free(msg);
7271 msg = NULL;
7272 goto out;
7273}
7274
7275
7276struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
7277 const u8 *hdr,
7278 const u8 *buf, size_t buflen)
7279{
7280 const struct dpp_curve_params *curve = pkex->own_bi->curve;
Roshan Pius3a1667e2018-07-03 15:17:14 -07007281 EVP_PKEY_CTX *ctx = NULL;
7282 size_t Jx_len, Lx_len;
7283 u8 Jx[DPP_MAX_SHARED_SECRET_LEN];
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007284 u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
7285 const u8 *wrapped_data, *b_key, *peer_u;
7286 u16 wrapped_data_len, b_key_len, peer_u_len = 0;
7287 const u8 *addr[4];
7288 size_t len[4];
7289 u8 octet;
7290 u8 *unwrapped = NULL;
7291 size_t unwrapped_len = 0;
7292 struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
7293 struct wpabuf *B_pub = NULL;
7294 u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN];
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007295
Roshan Pius3a1667e2018-07-03 15:17:14 -07007296#ifdef CONFIG_TESTING_OPTIONS
7297 if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_REQ) {
7298 wpa_printf(MSG_INFO,
7299 "DPP: TESTING - stop at PKEX CR Request");
7300 pkex->failed = 1;
7301 return NULL;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007302 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07007303#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007304
Roshan Pius3a1667e2018-07-03 15:17:14 -07007305 if (!pkex->exchange_done || pkex->failed ||
7306 pkex->t >= PKEX_COUNTER_T_LIMIT || pkex->initiator)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007307 goto fail;
7308
7309 wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
7310 &wrapped_data_len);
7311 if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007312 dpp_pkex_fail(pkex,
7313 "Missing or invalid required Wrapped Data attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007314 goto fail;
7315 }
7316
7317 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
7318 wrapped_data, wrapped_data_len);
7319 unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
7320 unwrapped = os_malloc(unwrapped_len);
7321 if (!unwrapped)
7322 goto fail;
7323
7324 addr[0] = hdr;
7325 len[0] = DPP_HDR_LEN;
7326 octet = 0;
7327 addr[1] = &octet;
7328 len[1] = sizeof(octet);
7329 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
7330 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
7331
7332 if (aes_siv_decrypt(pkex->z, curve->hash_len,
7333 wrapped_data, wrapped_data_len,
7334 2, addr, len, unwrapped) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007335 dpp_pkex_fail(pkex,
7336 "AES-SIV decryption failed - possible PKEX code mismatch");
7337 pkex->failed = 1;
7338 pkex->t++;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007339 goto fail;
7340 }
7341 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
7342 unwrapped, unwrapped_len);
7343
7344 if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007345 dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007346 goto fail;
7347 }
7348
7349 b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
7350 &b_key_len);
7351 if (!b_key || b_key_len != 2 * curve->prime_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007352 dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007353 goto fail;
7354 }
7355 pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
7356 b_key_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07007357 if (!pkex->peer_bootstrap_key) {
7358 dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007359 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07007360 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007361 dpp_debug_print_key("DPP: Peer bootstrap public key",
7362 pkex->peer_bootstrap_key);
7363
7364 /* ECDH: J' = y * A' */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007365 ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
7366 if (!ctx ||
7367 EVP_PKEY_derive_init(ctx) != 1 ||
7368 EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 ||
7369 EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 ||
7370 Jx_len > DPP_MAX_SHARED_SECRET_LEN ||
7371 EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) {
7372 wpa_printf(MSG_ERROR,
7373 "DPP: Failed to derive ECDH shared secret: %s",
7374 ERR_error_string(ERR_get_error(), NULL));
7375 goto fail;
7376 }
7377
7378 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
7379 Jx, Jx_len);
7380
7381 /* u' = HMAC(J'.x, MAC-Initiator | A'.x | Y.x | X'.x) */
7382 A_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
7383 Y_pub = dpp_get_pubkey_point(pkex->y, 0);
7384 X_pub = dpp_get_pubkey_point(pkex->x, 0);
7385 if (!A_pub || !Y_pub || !X_pub)
7386 goto fail;
7387 addr[0] = pkex->peer_mac;
7388 len[0] = ETH_ALEN;
7389 addr[1] = wpabuf_head(A_pub);
7390 len[1] = wpabuf_len(A_pub) / 2;
7391 addr[2] = wpabuf_head(Y_pub);
7392 len[2] = wpabuf_len(Y_pub) / 2;
7393 addr[3] = wpabuf_head(X_pub);
7394 len[3] = wpabuf_len(X_pub) / 2;
7395 if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
7396 goto fail;
7397
7398 peer_u = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
7399 &peer_u_len);
7400 if (!peer_u || peer_u_len != curve->hash_len ||
7401 os_memcmp(peer_u, u, curve->hash_len) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007402 dpp_pkex_fail(pkex, "No valid u (I-Auth tag) found");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007403 wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'",
7404 u, curve->hash_len);
7405 wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07007406 pkex->t++;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007407 goto fail;
7408 }
7409 wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received");
7410
7411 /* ECDH: L = b * X' */
7412 EVP_PKEY_CTX_free(ctx);
7413 ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL);
7414 if (!ctx ||
7415 EVP_PKEY_derive_init(ctx) != 1 ||
7416 EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 ||
7417 EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 ||
7418 Lx_len > DPP_MAX_SHARED_SECRET_LEN ||
7419 EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) {
7420 wpa_printf(MSG_ERROR,
7421 "DPP: Failed to derive ECDH shared secret: %s",
7422 ERR_error_string(ERR_get_error(), NULL));
7423 goto fail;
7424 }
7425
7426 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
7427 Lx, Lx_len);
7428
7429 /* v = HMAC(L.x, MAC-Responder | B.x | X'.x | Y.x) */
7430 B_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
7431 if (!B_pub)
7432 goto fail;
7433 addr[0] = pkex->own_mac;
7434 len[0] = ETH_ALEN;
7435 addr[1] = wpabuf_head(B_pub);
7436 len[1] = wpabuf_len(B_pub) / 2;
7437 addr[2] = wpabuf_head(X_pub);
7438 len[2] = wpabuf_len(X_pub) / 2;
7439 addr[3] = wpabuf_head(Y_pub);
7440 len[3] = wpabuf_len(Y_pub) / 2;
7441 if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
7442 goto fail;
7443 wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len);
7444
Roshan Pius3a1667e2018-07-03 15:17:14 -07007445 msg = dpp_pkex_build_commit_reveal_resp(pkex, B_pub, v);
7446 if (!msg)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007447 goto fail;
7448
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007449out:
7450 EVP_PKEY_CTX_free(ctx);
7451 os_free(unwrapped);
7452 wpabuf_free(A_pub);
7453 wpabuf_free(B_pub);
7454 wpabuf_free(X_pub);
7455 wpabuf_free(Y_pub);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007456 return msg;
7457fail:
Roshan Pius3a1667e2018-07-03 15:17:14 -07007458 wpa_printf(MSG_DEBUG,
7459 "DPP: PKEX Commit-Reveal Request processing failed");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007460 goto out;
7461}
7462
7463
7464int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
7465 const u8 *buf, size_t buflen)
7466{
7467 const struct dpp_curve_params *curve = pkex->own_bi->curve;
7468 const u8 *wrapped_data, *b_key, *peer_v;
7469 u16 wrapped_data_len, b_key_len, peer_v_len = 0;
7470 const u8 *addr[4];
7471 size_t len[4];
7472 u8 octet;
7473 u8 *unwrapped = NULL;
7474 size_t unwrapped_len = 0;
7475 int ret = -1;
7476 u8 v[DPP_MAX_HASH_LEN];
7477 size_t Lx_len;
7478 u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
7479 EVP_PKEY_CTX *ctx = NULL;
7480 struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
7481
Roshan Pius3a1667e2018-07-03 15:17:14 -07007482#ifdef CONFIG_TESTING_OPTIONS
7483 if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_RESP) {
7484 wpa_printf(MSG_INFO,
7485 "DPP: TESTING - stop at PKEX CR Response");
7486 pkex->failed = 1;
7487 goto fail;
7488 }
7489#endif /* CONFIG_TESTING_OPTIONS */
7490
7491 if (!pkex->exchange_done || pkex->failed ||
7492 pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
7493 goto fail;
7494
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007495 wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
7496 &wrapped_data_len);
7497 if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007498 dpp_pkex_fail(pkex,
7499 "Missing or invalid required Wrapped Data attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007500 goto fail;
7501 }
7502
7503 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
7504 wrapped_data, wrapped_data_len);
7505 unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
7506 unwrapped = os_malloc(unwrapped_len);
7507 if (!unwrapped)
7508 goto fail;
7509
7510 addr[0] = hdr;
7511 len[0] = DPP_HDR_LEN;
7512 octet = 1;
7513 addr[1] = &octet;
7514 len[1] = sizeof(octet);
7515 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
7516 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
7517
7518 if (aes_siv_decrypt(pkex->z, curve->hash_len,
7519 wrapped_data, wrapped_data_len,
7520 2, addr, len, unwrapped) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007521 dpp_pkex_fail(pkex,
7522 "AES-SIV decryption failed - possible PKEX code mismatch");
7523 pkex->t++;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007524 goto fail;
7525 }
7526 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
7527 unwrapped, unwrapped_len);
7528
7529 if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007530 dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007531 goto fail;
7532 }
7533
7534 b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
7535 &b_key_len);
7536 if (!b_key || b_key_len != 2 * curve->prime_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007537 dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007538 goto fail;
7539 }
7540 pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
7541 b_key_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07007542 if (!pkex->peer_bootstrap_key) {
7543 dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007544 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07007545 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007546 dpp_debug_print_key("DPP: Peer bootstrap public key",
7547 pkex->peer_bootstrap_key);
7548
7549 /* ECDH: L' = x * B' */
7550 ctx = EVP_PKEY_CTX_new(pkex->x, NULL);
7551 if (!ctx ||
7552 EVP_PKEY_derive_init(ctx) != 1 ||
7553 EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 ||
7554 EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 ||
7555 Lx_len > DPP_MAX_SHARED_SECRET_LEN ||
7556 EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) {
7557 wpa_printf(MSG_ERROR,
7558 "DPP: Failed to derive ECDH shared secret: %s",
7559 ERR_error_string(ERR_get_error(), NULL));
7560 goto fail;
7561 }
7562
7563 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
7564 Lx, Lx_len);
7565
7566 /* v' = HMAC(L.x, MAC-Responder | B'.x | X.x | Y'.x) */
7567 B_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
7568 X_pub = dpp_get_pubkey_point(pkex->x, 0);
7569 Y_pub = dpp_get_pubkey_point(pkex->y, 0);
7570 if (!B_pub || !X_pub || !Y_pub)
7571 goto fail;
7572 addr[0] = pkex->peer_mac;
7573 len[0] = ETH_ALEN;
7574 addr[1] = wpabuf_head(B_pub);
7575 len[1] = wpabuf_len(B_pub) / 2;
7576 addr[2] = wpabuf_head(X_pub);
7577 len[2] = wpabuf_len(X_pub) / 2;
7578 addr[3] = wpabuf_head(Y_pub);
7579 len[3] = wpabuf_len(Y_pub) / 2;
7580 if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
7581 goto fail;
7582
7583 peer_v = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_AUTH_TAG,
7584 &peer_v_len);
7585 if (!peer_v || peer_v_len != curve->hash_len ||
7586 os_memcmp(peer_v, v, curve->hash_len) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007587 dpp_pkex_fail(pkex, "No valid v (R-Auth tag) found");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007588 wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'",
7589 v, curve->hash_len);
7590 wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07007591 pkex->t++;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007592 goto fail;
7593 }
7594 wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received");
7595
7596 ret = 0;
7597out:
7598 wpabuf_free(B_pub);
7599 wpabuf_free(X_pub);
7600 wpabuf_free(Y_pub);
7601 EVP_PKEY_CTX_free(ctx);
7602 os_free(unwrapped);
7603 return ret;
7604fail:
7605 goto out;
7606}
7607
7608
7609void dpp_pkex_free(struct dpp_pkex *pkex)
7610{
7611 if (!pkex)
7612 return;
7613
7614 os_free(pkex->identifier);
7615 os_free(pkex->code);
7616 EVP_PKEY_free(pkex->x);
7617 EVP_PKEY_free(pkex->y);
7618 EVP_PKEY_free(pkex->peer_bootstrap_key);
7619 wpabuf_free(pkex->exchange_req);
7620 wpabuf_free(pkex->exchange_resp);
7621 os_free(pkex);
7622}
Roshan Pius3a1667e2018-07-03 15:17:14 -07007623
7624
7625#ifdef CONFIG_TESTING_OPTIONS
7626char * dpp_corrupt_connector_signature(const char *connector)
7627{
7628 char *tmp, *pos, *signed3 = NULL;
7629 unsigned char *signature = NULL;
7630 size_t signature_len = 0, signed3_len;
7631
7632 tmp = os_zalloc(os_strlen(connector) + 5);
7633 if (!tmp)
7634 goto fail;
7635 os_memcpy(tmp, connector, os_strlen(connector));
7636
7637 pos = os_strchr(tmp, '.');
7638 if (!pos)
7639 goto fail;
7640
7641 pos = os_strchr(pos + 1, '.');
7642 if (!pos)
7643 goto fail;
7644 pos++;
7645
7646 wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s",
7647 pos);
7648 signature = base64_url_decode((const unsigned char *) pos,
7649 os_strlen(pos), &signature_len);
7650 if (!signature || signature_len == 0)
7651 goto fail;
7652 wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature",
7653 signature, signature_len);
7654 signature[signature_len - 1] ^= 0x01;
7655 wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature",
7656 signature, signature_len);
7657 signed3 = (char *) base64_url_encode(signature, signature_len,
7658 &signed3_len, 0);
7659 if (!signed3)
7660 goto fail;
7661 os_memcpy(pos, signed3, signed3_len);
7662 pos[signed3_len] = '\0';
7663 wpa_printf(MSG_DEBUG, "DPP: Corrupted base64url encoded signature: %s",
7664 pos);
7665
7666out:
7667 os_free(signature);
7668 os_free(signed3);
7669 return tmp;
7670fail:
7671 os_free(tmp);
7672 tmp = NULL;
7673 goto out;
7674}
7675#endif /* CONFIG_TESTING_OPTIONS */