blob: 677f586929f329e756bc90b05f3af9d24691798c [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");
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800843 os_free(data);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700844 return -1;
845 }
846 wpa_hexdump(MSG_DEBUG, "DPP: Public key hash",
847 bi->pubkey_hash, SHA256_MAC_LEN);
848
849 /* DER encoded ASN.1 SubjectPublicKeyInfo
850 *
851 * SubjectPublicKeyInfo ::= SEQUENCE {
852 * algorithm AlgorithmIdentifier,
853 * subjectPublicKey BIT STRING }
854 *
855 * AlgorithmIdentifier ::= SEQUENCE {
856 * algorithm OBJECT IDENTIFIER,
857 * parameters ANY DEFINED BY algorithm OPTIONAL }
858 *
859 * subjectPublicKey = compressed format public key per ANSI X9.63
860 * algorithm = ecPublicKey (1.2.840.10045.2.1)
861 * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g.,
862 * prime256v1 (1.2.840.10045.3.1.7)
863 */
864
865 p = data;
866 pkey = d2i_PUBKEY(NULL, &p, data_len);
867 os_free(data);
868
869 if (!pkey) {
870 wpa_printf(MSG_DEBUG,
871 "DPP: Could not parse URI public-key SubjectPublicKeyInfo");
872 return -1;
873 }
874
875 if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
876 wpa_printf(MSG_DEBUG,
877 "DPP: SubjectPublicKeyInfo does not describe an EC key");
878 EVP_PKEY_free(pkey);
879 return -1;
880 }
881
882 res = X509_PUBKEY_set(&pub, pkey);
883 if (res != 1) {
884 wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey");
885 goto fail;
886 }
887
888 res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub);
889 if (res != 1) {
890 wpa_printf(MSG_DEBUG,
891 "DPP: Could not extract SubjectPublicKeyInfo parameters");
892 goto fail;
893 }
894 res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0);
895 if (res < 0 || (size_t) res >= sizeof(buf)) {
896 wpa_printf(MSG_DEBUG,
897 "DPP: Could not extract SubjectPublicKeyInfo algorithm");
898 goto fail;
899 }
900 wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf);
901 if (os_strcmp(buf, "id-ecPublicKey") != 0) {
902 wpa_printf(MSG_DEBUG,
903 "DPP: Unsupported SubjectPublicKeyInfo algorithm");
904 goto fail;
905 }
906
907 X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa);
908 if (ptype != V_ASN1_OBJECT) {
909 wpa_printf(MSG_DEBUG,
910 "DPP: SubjectPublicKeyInfo parameters did not contain an OID");
911 goto fail;
912 }
913 poid = pval;
914 res = OBJ_obj2txt(buf, sizeof(buf), poid, 0);
915 if (res < 0 || (size_t) res >= sizeof(buf)) {
916 wpa_printf(MSG_DEBUG,
917 "DPP: Could not extract SubjectPublicKeyInfo parameters OID");
918 goto fail;
919 }
920 wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf);
921 bi->curve = dpp_get_curve_oid(poid);
922 if (!bi->curve) {
923 wpa_printf(MSG_DEBUG,
924 "DPP: Unsupported SubjectPublicKeyInfo curve: %s",
925 buf);
926 goto fail;
927 }
928
929 wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen);
930
931 X509_PUBKEY_free(pub);
932 bi->pubkey = pkey;
933 return 0;
934fail:
935 X509_PUBKEY_free(pub);
936 EVP_PKEY_free(pkey);
937 return -1;
938}
939
940
941static struct dpp_bootstrap_info * dpp_parse_uri(const char *uri)
942{
943 const char *pos = uri;
944 const char *end;
945 const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL;
946 struct dpp_bootstrap_info *bi;
947
948 wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri));
949
950 if (os_strncmp(pos, "DPP:", 4) != 0) {
951 wpa_printf(MSG_INFO, "DPP: Not a DPP URI");
952 return NULL;
953 }
954 pos += 4;
955
956 for (;;) {
957 end = os_strchr(pos, ';');
958 if (!end)
959 break;
960
961 if (end == pos) {
962 /* Handle terminating ";;" and ignore unexpected ";"
963 * for parsing robustness. */
964 pos++;
965 continue;
966 }
967
968 if (pos[0] == 'C' && pos[1] == ':' && !chan_list)
969 chan_list = pos + 2;
970 else if (pos[0] == 'M' && pos[1] == ':' && !mac)
971 mac = pos + 2;
972 else if (pos[0] == 'I' && pos[1] == ':' && !info)
973 info = pos + 2;
974 else if (pos[0] == 'K' && pos[1] == ':' && !pk)
975 pk = pos + 2;
976 else
977 wpa_hexdump_ascii(MSG_DEBUG,
978 "DPP: Ignore unrecognized URI parameter",
979 pos, end - pos);
980 pos = end + 1;
981 }
982
983 if (!pk) {
984 wpa_printf(MSG_INFO, "DPP: URI missing public-key");
985 return NULL;
986 }
987
988 bi = os_zalloc(sizeof(*bi));
989 if (!bi)
990 return NULL;
991
992 if (dpp_clone_uri(bi, uri) < 0 ||
993 dpp_parse_uri_chan_list(bi, chan_list) < 0 ||
994 dpp_parse_uri_mac(bi, mac) < 0 ||
995 dpp_parse_uri_info(bi, info) < 0 ||
996 dpp_parse_uri_pk(bi, pk) < 0) {
997 dpp_bootstrap_info_free(bi);
998 bi = NULL;
999 }
1000
1001 return bi;
1002}
1003
1004
1005struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri)
1006{
1007 struct dpp_bootstrap_info *bi;
1008
1009 bi = dpp_parse_uri(uri);
1010 if (bi)
1011 bi->type = DPP_BOOTSTRAP_QR_CODE;
1012 return bi;
1013}
1014
1015
1016static void dpp_debug_print_key(const char *title, EVP_PKEY *key)
1017{
1018 EC_KEY *eckey;
1019 BIO *out;
1020 size_t rlen;
1021 char *txt;
1022 int res;
1023 unsigned char *der = NULL;
1024 int der_len;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001025 const EC_GROUP *group;
1026 const EC_POINT *point;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001027
1028 out = BIO_new(BIO_s_mem());
1029 if (!out)
1030 return;
1031
1032 EVP_PKEY_print_private(out, key, 0, NULL);
1033 rlen = BIO_ctrl_pending(out);
1034 txt = os_malloc(rlen + 1);
1035 if (txt) {
1036 res = BIO_read(out, txt, rlen);
1037 if (res > 0) {
1038 txt[res] = '\0';
1039 wpa_printf(MSG_DEBUG, "%s: %s", title, txt);
1040 }
1041 os_free(txt);
1042 }
1043 BIO_free(out);
1044
1045 eckey = EVP_PKEY_get1_EC_KEY(key);
1046 if (!eckey)
1047 return;
1048
Roshan Pius3a1667e2018-07-03 15:17:14 -07001049 group = EC_KEY_get0_group(eckey);
1050 point = EC_KEY_get0_public_key(eckey);
1051 if (group && point)
1052 dpp_debug_print_point(title, group, point);
1053
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001054 der_len = i2d_ECPrivateKey(eckey, &der);
1055 if (der_len > 0)
1056 wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len);
1057 OPENSSL_free(der);
1058 if (der_len <= 0) {
1059 der = NULL;
1060 der_len = i2d_EC_PUBKEY(eckey, &der);
1061 if (der_len > 0)
1062 wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len);
1063 OPENSSL_free(der);
1064 }
1065
1066 EC_KEY_free(eckey);
1067}
1068
1069
1070static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
1071{
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001072 EVP_PKEY_CTX *kctx = NULL;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001073 EC_KEY *ec_params;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001074 EVP_PKEY *params = NULL, *key = NULL;
1075 int nid;
1076
1077 wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
1078
1079 nid = OBJ_txt2nid(curve->name);
1080 if (nid == NID_undef) {
1081 wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name);
1082 return NULL;
1083 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07001084
1085 ec_params = EC_KEY_new_by_curve_name(nid);
1086 if (!ec_params) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001087 wpa_printf(MSG_ERROR,
1088 "DPP: Failed to generate EC_KEY parameters");
1089 goto fail;
1090 }
1091 EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
1092 params = EVP_PKEY_new();
1093 if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
1094 wpa_printf(MSG_ERROR,
1095 "DPP: Failed to generate EVP_PKEY parameters");
1096 goto fail;
1097 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001098
1099 kctx = EVP_PKEY_CTX_new(params, NULL);
1100 if (!kctx ||
1101 EVP_PKEY_keygen_init(kctx) != 1 ||
1102 EVP_PKEY_keygen(kctx, &key) != 1) {
1103 wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key");
1104 goto fail;
1105 }
1106
1107 if (wpa_debug_show_keys)
1108 dpp_debug_print_key("Own generated key", key);
1109
1110 EVP_PKEY_free(params);
1111 EVP_PKEY_CTX_free(kctx);
1112 return key;
1113fail:
1114 EVP_PKEY_CTX_free(kctx);
1115 EVP_PKEY_free(params);
1116 return NULL;
1117}
1118
1119
1120static const struct dpp_curve_params *
1121dpp_get_curve_name(const char *name)
1122{
1123 int i;
1124
1125 for (i = 0; dpp_curves[i].name; i++) {
1126 if (os_strcmp(name, dpp_curves[i].name) == 0 ||
1127 (dpp_curves[i].jwk_crv &&
1128 os_strcmp(name, dpp_curves[i].jwk_crv) == 0))
1129 return &dpp_curves[i];
1130 }
1131 return NULL;
1132}
1133
1134
1135static const struct dpp_curve_params *
1136dpp_get_curve_jwk_crv(const char *name)
1137{
1138 int i;
1139
1140 for (i = 0; dpp_curves[i].name; i++) {
1141 if (dpp_curves[i].jwk_crv &&
1142 os_strcmp(name, dpp_curves[i].jwk_crv) == 0)
1143 return &dpp_curves[i];
1144 }
1145 return NULL;
1146}
1147
1148
1149static EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
1150 const u8 *privkey, size_t privkey_len)
1151{
1152 EVP_PKEY *pkey;
1153 EC_KEY *eckey;
1154 const EC_GROUP *group;
1155 int nid;
1156
1157 pkey = EVP_PKEY_new();
1158 if (!pkey)
1159 return NULL;
1160 eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len);
1161 if (!eckey) {
1162 wpa_printf(MSG_INFO,
1163 "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
1164 ERR_error_string(ERR_get_error(), NULL));
1165 EVP_PKEY_free(pkey);
1166 return NULL;
1167 }
1168 group = EC_KEY_get0_group(eckey);
1169 if (!group) {
1170 EC_KEY_free(eckey);
1171 EVP_PKEY_free(pkey);
1172 return NULL;
1173 }
1174 nid = EC_GROUP_get_curve_name(group);
1175 *curve = dpp_get_curve_nid(nid);
1176 if (!*curve) {
1177 wpa_printf(MSG_INFO,
1178 "DPP: Unsupported curve (nid=%d) in pre-assigned key",
1179 nid);
1180 EC_KEY_free(eckey);
1181 EVP_PKEY_free(pkey);
1182 return NULL;
1183 }
1184
1185 if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
1186 EC_KEY_free(eckey);
1187 EVP_PKEY_free(pkey);
1188 return NULL;
1189 }
1190 return pkey;
1191}
1192
1193
Roshan Pius3a1667e2018-07-03 15:17:14 -07001194typedef struct {
1195 /* AlgorithmIdentifier ecPublicKey with optional parameters present
1196 * as an OID identifying the curve */
1197 X509_ALGOR *alg;
1198 /* Compressed format public key per ANSI X9.63 */
1199 ASN1_BIT_STRING *pub_key;
1200} DPP_BOOTSTRAPPING_KEY;
1201
1202ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = {
1203 ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR),
1204 ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING)
1205} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY);
1206
1207IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY);
1208
1209
1210static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001211{
1212 unsigned char *der = NULL;
1213 int der_len;
1214 EC_KEY *eckey;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001215 struct wpabuf *ret = NULL;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001216 size_t len;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001217 const EC_GROUP *group;
1218 const EC_POINT *point;
1219 BN_CTX *ctx;
1220 DPP_BOOTSTRAPPING_KEY *bootstrap = NULL;
1221 int nid;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001222
Roshan Pius3a1667e2018-07-03 15:17:14 -07001223 ctx = BN_CTX_new();
1224 eckey = EVP_PKEY_get1_EC_KEY(key);
1225 if (!ctx || !eckey)
1226 goto fail;
1227
1228 group = EC_KEY_get0_group(eckey);
1229 point = EC_KEY_get0_public_key(eckey);
1230 if (!group || !point)
1231 goto fail;
1232 dpp_debug_print_point("DPP: bootstrap public key", group, point);
1233 nid = EC_GROUP_get_curve_name(group);
1234
1235 bootstrap = DPP_BOOTSTRAPPING_KEY_new();
1236 if (!bootstrap ||
1237 X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC),
1238 V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1)
1239 goto fail;
1240
1241 len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
1242 NULL, 0, ctx);
1243 if (len == 0)
1244 goto fail;
1245
1246 der = OPENSSL_malloc(len);
1247 if (!der)
1248 goto fail;
1249 len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
1250 der, len, ctx);
1251
1252 OPENSSL_free(bootstrap->pub_key->data);
1253 bootstrap->pub_key->data = der;
1254 der = NULL;
1255 bootstrap->pub_key->length = len;
1256 /* No unused bits */
1257 bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
1258 bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
1259
1260 der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001261 if (der_len <= 0) {
1262 wpa_printf(MSG_ERROR,
1263 "DDP: Failed to build DER encoded public key");
Roshan Pius3a1667e2018-07-03 15:17:14 -07001264 goto fail;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001265 }
1266
Roshan Pius3a1667e2018-07-03 15:17:14 -07001267 ret = wpabuf_alloc_copy(der, der_len);
1268fail:
1269 DPP_BOOTSTRAPPING_KEY_free(bootstrap);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001270 OPENSSL_free(der);
Roshan Pius3a1667e2018-07-03 15:17:14 -07001271 EC_KEY_free(eckey);
1272 BN_CTX_free(ctx);
1273 return ret;
1274}
1275
1276
1277int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
1278{
1279 struct wpabuf *der;
1280 int res;
1281 const u8 *addr[1];
1282 size_t len[1];
1283
1284 der = dpp_bootstrap_key_der(bi->pubkey);
1285 if (!der)
1286 return -1;
1287 wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
1288 der);
1289
1290 addr[0] = wpabuf_head(der);
1291 len[0] = wpabuf_len(der);
1292 res = sha256_vector(1, addr, len, bi->pubkey_hash);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001293 if (res < 0)
1294 wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
Roshan Pius3a1667e2018-07-03 15:17:14 -07001295 else
1296 wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash,
1297 SHA256_MAC_LEN);
1298 wpabuf_free(der);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001299 return res;
1300}
1301
1302
1303char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
1304 const u8 *privkey, size_t privkey_len)
1305{
1306 unsigned char *base64 = NULL;
1307 char *pos, *end;
1308 size_t len;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001309 struct wpabuf *der = NULL;
1310 const u8 *addr[1];
1311 int res;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001312
1313 if (!curve) {
1314 bi->curve = &dpp_curves[0];
1315 } else {
1316 bi->curve = dpp_get_curve_name(curve);
1317 if (!bi->curve) {
1318 wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
1319 curve);
1320 return NULL;
1321 }
1322 }
1323 if (privkey)
1324 bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len);
1325 else
1326 bi->pubkey = dpp_gen_keypair(bi->curve);
1327 if (!bi->pubkey)
1328 goto fail;
1329 bi->own = 1;
1330
Roshan Pius3a1667e2018-07-03 15:17:14 -07001331 der = dpp_bootstrap_key_der(bi->pubkey);
1332 if (!der)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001333 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001334 wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
1335 der);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001336
Roshan Pius3a1667e2018-07-03 15:17:14 -07001337 addr[0] = wpabuf_head(der);
1338 len = wpabuf_len(der);
1339 res = sha256_vector(1, addr, &len, bi->pubkey_hash);
1340 if (res < 0) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001341 wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
1342 goto fail;
1343 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07001344 wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash,
1345 SHA256_MAC_LEN);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001346
Roshan Pius3a1667e2018-07-03 15:17:14 -07001347 base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len);
1348 wpabuf_free(der);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001349 der = NULL;
1350 if (!base64)
1351 goto fail;
1352 pos = (char *) base64;
1353 end = pos + len;
1354 for (;;) {
1355 pos = os_strchr(pos, '\n');
1356 if (!pos)
1357 break;
1358 os_memmove(pos, pos + 1, end - pos);
1359 }
1360 return (char *) base64;
1361fail:
1362 os_free(base64);
Roshan Pius3a1667e2018-07-03 15:17:14 -07001363 wpabuf_free(der);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001364 return NULL;
1365}
1366
1367
1368static int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1,
1369 unsigned int hash_len)
1370{
1371 u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
1372 const char *info = "first intermediate key";
1373 int res;
1374
1375 /* k1 = HKDF(<>, "first intermediate key", M.x) */
1376
1377 /* HKDF-Extract(<>, M.x) */
1378 os_memset(salt, 0, hash_len);
1379 if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0)
1380 return -1;
1381 wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)",
1382 prk, hash_len);
1383
1384 /* HKDF-Expand(PRK, info, L) */
1385 res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len);
1386 os_memset(prk, 0, hash_len);
1387 if (res < 0)
1388 return -1;
1389
1390 wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)",
1391 k1, hash_len);
1392 return 0;
1393}
1394
1395
1396static int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2,
1397 unsigned int hash_len)
1398{
1399 u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
1400 const char *info = "second intermediate key";
1401 int res;
1402
1403 /* k2 = HKDF(<>, "second intermediate key", N.x) */
1404
1405 /* HKDF-Extract(<>, N.x) */
1406 os_memset(salt, 0, hash_len);
1407 res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk);
1408 if (res < 0)
1409 return -1;
1410 wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
1411 prk, hash_len);
1412
1413 /* HKDF-Expand(PRK, info, L) */
1414 res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len);
1415 os_memset(prk, 0, hash_len);
1416 if (res < 0)
1417 return -1;
1418
1419 wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)",
1420 k2, hash_len);
1421 return 0;
1422}
1423
1424
1425static int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke,
1426 unsigned int hash_len)
1427{
1428 size_t nonce_len;
1429 u8 nonces[2 * DPP_MAX_NONCE_LEN];
1430 const char *info_ke = "DPP Key";
1431 u8 prk[DPP_MAX_HASH_LEN];
1432 int res;
1433 const u8 *addr[3];
1434 size_t len[3];
1435 size_t num_elem = 0;
1436
Roshan Pius3a1667e2018-07-03 15:17:14 -07001437 if (!auth->Mx_len || !auth->Nx_len) {
1438 wpa_printf(MSG_DEBUG,
1439 "DPP: Mx/Nx not available - cannot derive ke");
1440 return -1;
1441 }
1442
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001443 /* ke = HKDF(I-nonce | R-nonce, "DPP Key", M.x | N.x [| L.x]) */
1444
1445 /* HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */
1446 nonce_len = auth->curve->nonce_len;
1447 os_memcpy(nonces, auth->i_nonce, nonce_len);
1448 os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len);
1449 addr[num_elem] = auth->Mx;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001450 len[num_elem] = auth->Mx_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001451 num_elem++;
1452 addr[num_elem] = auth->Nx;
Roshan Pius3a1667e2018-07-03 15:17:14 -07001453 len[num_elem] = auth->Nx_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001454 num_elem++;
1455 if (auth->peer_bi && auth->own_bi) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07001456 if (!auth->Lx_len) {
1457 wpa_printf(MSG_DEBUG,
1458 "DPP: Lx not available - cannot derive ke");
1459 return -1;
1460 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001461 addr[num_elem] = auth->Lx;
1462 len[num_elem] = auth->secret_len;
1463 num_elem++;
1464 }
1465 res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len,
1466 num_elem, addr, len, prk);
1467 if (res < 0)
1468 return -1;
1469 wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
1470 prk, hash_len);
1471
1472 /* HKDF-Expand(PRK, info, L) */
1473 res = dpp_hkdf_expand(hash_len, prk, hash_len, info_ke, ke, hash_len);
1474 os_memset(prk, 0, hash_len);
1475 if (res < 0)
1476 return -1;
1477
1478 wpa_hexdump_key(MSG_DEBUG, "DPP: ke = HKDF-Expand(PRK, info, L)",
1479 ke, hash_len);
1480 return 0;
1481}
1482
1483
Roshan Pius3a1667e2018-07-03 15:17:14 -07001484static void dpp_build_attr_status(struct wpabuf *msg,
1485 enum dpp_status_error status)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001486{
Roshan Pius3a1667e2018-07-03 15:17:14 -07001487 wpa_printf(MSG_DEBUG, "DPP: Status %d", status);
1488 wpabuf_put_le16(msg, DPP_ATTR_STATUS);
1489 wpabuf_put_le16(msg, 1);
1490 wpabuf_put_u8(msg, status);
1491}
1492
1493
1494static void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg,
1495 const u8 *hash)
1496{
1497 if (hash) {
1498 wpa_printf(MSG_DEBUG, "DPP: R-Bootstrap Key Hash");
1499 wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
1500 wpabuf_put_le16(msg, SHA256_MAC_LEN);
1501 wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
1502 }
1503}
1504
1505
1506static void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg,
1507 const u8 *hash)
1508{
1509 if (hash) {
1510 wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash");
1511 wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
1512 wpabuf_put_le16(msg, SHA256_MAC_LEN);
1513 wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
1514 }
1515}
1516
1517
1518static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
1519 const struct wpabuf *pi,
1520 size_t nonce_len,
1521 const u8 *r_pubkey_hash,
1522 const u8 *i_pubkey_hash,
1523 unsigned int neg_freq)
1524{
1525 struct wpabuf *msg;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001526 u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1];
1527 u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE];
1528 u8 *pos;
1529 const u8 *addr[2];
1530 size_t len[2], siv_len, attr_len;
1531 u8 *attr_start, *attr_end;
1532
Roshan Pius3a1667e2018-07-03 15:17:14 -07001533 /* Build DPP Authentication Request frame attributes */
1534 attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) +
1535 4 + sizeof(wrapped_data);
1536 if (neg_freq > 0)
1537 attr_len += 4 + 2;
1538#ifdef CONFIG_TESTING_OPTIONS
1539 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ)
1540 attr_len += 5;
1541#endif /* CONFIG_TESTING_OPTIONS */
1542 msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len);
1543 if (!msg)
1544 return NULL;
1545
1546 attr_start = wpabuf_put(msg, 0);
1547
1548 /* Responder Bootstrapping Key Hash */
1549 dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
1550
1551 /* Initiator Bootstrapping Key Hash */
1552 dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
1553
1554 /* Initiator Protocol Key */
1555 if (pi) {
1556 wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
1557 wpabuf_put_le16(msg, wpabuf_len(pi));
1558 wpabuf_put_buf(msg, pi);
1559 }
1560
1561 /* Channel */
1562 if (neg_freq > 0) {
1563 u8 op_class, channel;
1564
1565 if (ieee80211_freq_to_channel_ext(neg_freq, 0, 0, &op_class,
1566 &channel) ==
1567 NUM_HOSTAPD_MODES) {
1568 wpa_printf(MSG_INFO,
1569 "DPP: Unsupported negotiation frequency request: %d",
1570 neg_freq);
1571 wpabuf_free(msg);
1572 return NULL;
1573 }
1574 wpabuf_put_le16(msg, DPP_ATTR_CHANNEL);
1575 wpabuf_put_le16(msg, 2);
1576 wpabuf_put_u8(msg, op_class);
1577 wpabuf_put_u8(msg, channel);
1578 }
1579
1580#ifdef CONFIG_TESTING_OPTIONS
1581 if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) {
1582 wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
1583 goto skip_wrapped_data;
1584 }
1585#endif /* CONFIG_TESTING_OPTIONS */
1586
1587 /* Wrapped data ({I-nonce, I-capabilities}k1) */
1588 pos = clear;
1589
1590#ifdef CONFIG_TESTING_OPTIONS
1591 if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_REQ) {
1592 wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
1593 goto skip_i_nonce;
1594 }
1595 if (dpp_test == DPP_TEST_INVALID_I_NONCE_AUTH_REQ) {
1596 wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-nonce");
1597 WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
1598 pos += 2;
1599 WPA_PUT_LE16(pos, nonce_len - 1);
1600 pos += 2;
1601 os_memcpy(pos, auth->i_nonce, nonce_len - 1);
1602 pos += nonce_len - 1;
1603 goto skip_i_nonce;
1604 }
1605#endif /* CONFIG_TESTING_OPTIONS */
1606
1607 /* I-nonce */
1608 WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
1609 pos += 2;
1610 WPA_PUT_LE16(pos, nonce_len);
1611 pos += 2;
1612 os_memcpy(pos, auth->i_nonce, nonce_len);
1613 pos += nonce_len;
1614
1615#ifdef CONFIG_TESTING_OPTIONS
1616skip_i_nonce:
1617 if (dpp_test == DPP_TEST_NO_I_CAPAB_AUTH_REQ) {
1618 wpa_printf(MSG_INFO, "DPP: TESTING - no I-capab");
1619 goto skip_i_capab;
1620 }
1621#endif /* CONFIG_TESTING_OPTIONS */
1622
1623 /* I-capabilities */
1624 WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES);
1625 pos += 2;
1626 WPA_PUT_LE16(pos, 1);
1627 pos += 2;
1628 auth->i_capab = auth->allowed_roles;
1629 *pos++ = auth->i_capab;
1630#ifdef CONFIG_TESTING_OPTIONS
1631 if (dpp_test == DPP_TEST_ZERO_I_CAPAB) {
1632 wpa_printf(MSG_INFO, "DPP: TESTING - zero I-capabilities");
1633 pos[-1] = 0;
1634 }
1635skip_i_capab:
1636#endif /* CONFIG_TESTING_OPTIONS */
1637
1638 attr_end = wpabuf_put(msg, 0);
1639
1640 /* OUI, OUI type, Crypto Suite, DPP frame type */
1641 addr[0] = wpabuf_head_u8(msg) + 2;
1642 len[0] = 3 + 1 + 1 + 1;
1643 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
1644
1645 /* Attributes before Wrapped Data */
1646 addr[1] = attr_start;
1647 len[1] = attr_end - attr_start;
1648 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
1649
1650 siv_len = pos - clear;
1651 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
1652 if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
1653 2, addr, len, wrapped_data) < 0) {
1654 wpabuf_free(msg);
1655 return NULL;
1656 }
1657 siv_len += AES_BLOCK_SIZE;
1658 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
1659 wrapped_data, siv_len);
1660
1661 wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
1662 wpabuf_put_le16(msg, siv_len);
1663 wpabuf_put_data(msg, wrapped_data, siv_len);
1664
1665#ifdef CONFIG_TESTING_OPTIONS
1666 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) {
1667 wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
1668 dpp_build_attr_status(msg, DPP_STATUS_OK);
1669 }
1670skip_wrapped_data:
1671#endif /* CONFIG_TESTING_OPTIONS */
1672
1673 wpa_hexdump_buf(MSG_DEBUG,
1674 "DPP: Authentication Request frame attributes", msg);
1675
1676 return msg;
1677}
1678
1679
1680static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
1681 enum dpp_status_error status,
1682 const struct wpabuf *pr,
1683 size_t nonce_len,
1684 const u8 *r_pubkey_hash,
1685 const u8 *i_pubkey_hash,
1686 const u8 *r_nonce, const u8 *i_nonce,
1687 const u8 *wrapped_r_auth,
1688 size_t wrapped_r_auth_len,
1689 const u8 *siv_key)
1690{
1691 struct wpabuf *msg;
1692#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
1693 4 + 4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE
1694 u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
1695 u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
1696 const u8 *addr[2];
1697 size_t len[2], siv_len, attr_len;
1698 u8 *attr_start, *attr_end, *pos;
1699
1700 auth->waiting_auth_conf = 1;
1701 auth->auth_resp_tries = 0;
1702
1703 /* Build DPP Authentication Response frame attributes */
1704 attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
1705 4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data);
1706#ifdef CONFIG_TESTING_OPTIONS
1707 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP)
1708 attr_len += 5;
1709#endif /* CONFIG_TESTING_OPTIONS */
1710 msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
1711 if (!msg)
1712 return NULL;
1713
1714 attr_start = wpabuf_put(msg, 0);
1715
1716 /* DPP Status */
1717 if (status != 255)
1718 dpp_build_attr_status(msg, status);
1719
1720 /* Responder Bootstrapping Key Hash */
1721 dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
1722
1723 /* Initiator Bootstrapping Key Hash (mutual authentication) */
1724 dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
1725
1726 /* Responder Protocol Key */
1727 if (pr) {
1728 wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
1729 wpabuf_put_le16(msg, wpabuf_len(pr));
1730 wpabuf_put_buf(msg, pr);
1731 }
1732
1733 attr_end = wpabuf_put(msg, 0);
1734
1735#ifdef CONFIG_TESTING_OPTIONS
1736 if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) {
1737 wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
1738 goto skip_wrapped_data;
1739 }
1740#endif /* CONFIG_TESTING_OPTIONS */
1741
1742 /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
1743 pos = clear;
1744
1745 if (r_nonce) {
1746 /* R-nonce */
1747 WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
1748 pos += 2;
1749 WPA_PUT_LE16(pos, nonce_len);
1750 pos += 2;
1751 os_memcpy(pos, r_nonce, nonce_len);
1752 pos += nonce_len;
1753 }
1754
1755 if (i_nonce) {
1756 /* I-nonce */
1757 WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
1758 pos += 2;
1759 WPA_PUT_LE16(pos, nonce_len);
1760 pos += 2;
1761 os_memcpy(pos, i_nonce, nonce_len);
1762#ifdef CONFIG_TESTING_OPTIONS
1763 if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) {
1764 wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch");
1765 pos[nonce_len / 2] ^= 0x01;
1766 }
1767#endif /* CONFIG_TESTING_OPTIONS */
1768 pos += nonce_len;
1769 }
1770
1771#ifdef CONFIG_TESTING_OPTIONS
1772 if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) {
1773 wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab");
1774 goto skip_r_capab;
1775 }
1776#endif /* CONFIG_TESTING_OPTIONS */
1777
1778 /* R-capabilities */
1779 WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
1780 pos += 2;
1781 WPA_PUT_LE16(pos, 1);
1782 pos += 2;
1783 auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
1784 DPP_CAPAB_ENROLLEE;
1785 *pos++ = auth->r_capab;
1786#ifdef CONFIG_TESTING_OPTIONS
1787 if (dpp_test == DPP_TEST_ZERO_R_CAPAB) {
1788 wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities");
1789 pos[-1] = 0;
1790 } else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) {
1791 wpa_printf(MSG_INFO,
1792 "DPP: TESTING - incompatible R-capabilities");
1793 if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) ==
1794 (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE))
1795 pos[-1] = 0;
1796 else
1797 pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE :
1798 DPP_CAPAB_CONFIGURATOR;
1799 }
1800skip_r_capab:
1801#endif /* CONFIG_TESTING_OPTIONS */
1802
1803 if (wrapped_r_auth) {
1804 /* {R-auth}ke */
1805 WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
1806 pos += 2;
1807 WPA_PUT_LE16(pos, wrapped_r_auth_len);
1808 pos += 2;
1809 os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
1810 pos += wrapped_r_auth_len;
1811 }
1812
1813 /* OUI, OUI type, Crypto Suite, DPP frame type */
1814 addr[0] = wpabuf_head_u8(msg) + 2;
1815 len[0] = 3 + 1 + 1 + 1;
1816 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
1817
1818 /* Attributes before Wrapped Data */
1819 addr[1] = attr_start;
1820 len[1] = attr_end - attr_start;
1821 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
1822
1823 siv_len = pos - clear;
1824 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
1825 if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len,
1826 2, addr, len, wrapped_data) < 0) {
1827 wpabuf_free(msg);
1828 return NULL;
1829 }
1830 siv_len += AES_BLOCK_SIZE;
1831 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
1832 wrapped_data, siv_len);
1833
1834 wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
1835 wpabuf_put_le16(msg, siv_len);
1836 wpabuf_put_data(msg, wrapped_data, siv_len);
1837
1838#ifdef CONFIG_TESTING_OPTIONS
1839 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) {
1840 wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
1841 dpp_build_attr_status(msg, DPP_STATUS_OK);
1842 }
1843skip_wrapped_data:
1844#endif /* CONFIG_TESTING_OPTIONS */
1845
1846 wpa_hexdump_buf(MSG_DEBUG,
1847 "DPP: Authentication Response frame attributes", msg);
1848 return msg;
1849}
1850
1851
1852static int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes,
1853 u16 num_modes, unsigned int freq)
1854{
1855 u16 m;
1856 int c, flag;
1857
1858 if (!own_modes || !num_modes)
1859 return 1;
1860
1861 for (m = 0; m < num_modes; m++) {
1862 for (c = 0; c < own_modes[m].num_channels; c++) {
1863 if ((unsigned int) own_modes[m].channels[c].freq !=
1864 freq)
1865 continue;
1866 flag = own_modes[m].channels[c].flag;
1867 if (!(flag & (HOSTAPD_CHAN_DISABLED |
1868 HOSTAPD_CHAN_NO_IR |
1869 HOSTAPD_CHAN_RADAR)))
1870 return 1;
1871 }
1872 }
1873
1874 wpa_printf(MSG_DEBUG, "DPP: Peer channel %u MHz not supported", freq);
1875 return 0;
1876}
1877
1878
1879static int freq_included(const unsigned int freqs[], unsigned int num,
1880 unsigned int freq)
1881{
1882 while (num > 0) {
1883 if (freqs[--num] == freq)
1884 return 1;
1885 }
1886 return 0;
1887}
1888
1889
1890static void freq_to_start(unsigned int freqs[], unsigned int num,
1891 unsigned int freq)
1892{
1893 unsigned int i;
1894
1895 for (i = 0; i < num; i++) {
1896 if (freqs[i] == freq)
1897 break;
1898 }
1899 if (i == 0 || i >= num)
1900 return;
1901 os_memmove(&freqs[1], &freqs[0], i * sizeof(freqs[0]));
1902 freqs[0] = freq;
1903}
1904
1905
1906static int dpp_channel_intersect(struct dpp_authentication *auth,
1907 struct hostapd_hw_modes *own_modes,
1908 u16 num_modes)
1909{
1910 struct dpp_bootstrap_info *peer_bi = auth->peer_bi;
1911 unsigned int i, freq;
1912
1913 for (i = 0; i < peer_bi->num_freq; i++) {
1914 freq = peer_bi->freq[i];
1915 if (freq_included(auth->freq, auth->num_freq, freq))
1916 continue;
1917 if (dpp_channel_ok_init(own_modes, num_modes, freq))
1918 auth->freq[auth->num_freq++] = freq;
1919 }
1920 if (!auth->num_freq) {
1921 wpa_printf(MSG_INFO,
1922 "DPP: No available channels for initiating DPP Authentication");
1923 return -1;
1924 }
1925 auth->curr_freq = auth->freq[0];
1926 return 0;
1927}
1928
1929
1930static int dpp_channel_local_list(struct dpp_authentication *auth,
1931 struct hostapd_hw_modes *own_modes,
1932 u16 num_modes)
1933{
1934 u16 m;
1935 int c, flag;
1936 unsigned int freq;
1937
1938 auth->num_freq = 0;
1939
1940 if (!own_modes || !num_modes) {
1941 auth->freq[0] = 2412;
1942 auth->freq[1] = 2437;
1943 auth->freq[2] = 2462;
1944 auth->num_freq = 3;
1945 return 0;
1946 }
1947
1948 for (m = 0; m < num_modes; m++) {
1949 for (c = 0; c < own_modes[m].num_channels; c++) {
1950 freq = own_modes[m].channels[c].freq;
1951 flag = own_modes[m].channels[c].flag;
1952 if (flag & (HOSTAPD_CHAN_DISABLED |
1953 HOSTAPD_CHAN_NO_IR |
1954 HOSTAPD_CHAN_RADAR))
1955 continue;
1956 if (freq_included(auth->freq, auth->num_freq, freq))
1957 continue;
1958 auth->freq[auth->num_freq++] = freq;
1959 if (auth->num_freq == DPP_BOOTSTRAP_MAX_FREQ) {
1960 m = num_modes;
1961 break;
1962 }
1963 }
1964 }
1965
1966 return auth->num_freq == 0 ? -1 : 0;
1967}
1968
1969
1970static int dpp_prepare_channel_list(struct dpp_authentication *auth,
1971 struct hostapd_hw_modes *own_modes,
1972 u16 num_modes)
1973{
1974 int res;
1975 char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end;
1976 unsigned int i;
1977
1978 if (auth->peer_bi->num_freq > 0)
1979 res = dpp_channel_intersect(auth, own_modes, num_modes);
1980 else
1981 res = dpp_channel_local_list(auth, own_modes, num_modes);
1982 if (res < 0)
1983 return res;
1984
1985 /* Prioritize 2.4 GHz channels 6, 1, 11 (in this order) to hit the most
1986 * likely channels first. */
1987 freq_to_start(auth->freq, auth->num_freq, 2462);
1988 freq_to_start(auth->freq, auth->num_freq, 2412);
1989 freq_to_start(auth->freq, auth->num_freq, 2437);
1990
1991 auth->freq_idx = 0;
1992 auth->curr_freq = auth->freq[0];
1993
1994 pos = freqs;
1995 end = pos + sizeof(freqs);
1996 for (i = 0; i < auth->num_freq; i++) {
1997 res = os_snprintf(pos, end - pos, " %u", auth->freq[i]);
1998 if (os_snprintf_error(end - pos, res))
1999 break;
2000 pos += res;
2001 }
2002 *pos = '\0';
2003 wpa_printf(MSG_DEBUG, "DPP: Possible frequencies for initiating:%s",
2004 freqs);
2005
2006 return 0;
2007}
2008
2009
2010static int dpp_autogen_bootstrap_key(struct dpp_authentication *auth)
2011{
2012 struct dpp_bootstrap_info *bi;
2013 char *pk = NULL;
2014 size_t len;
2015
2016 if (auth->own_bi)
2017 return 0; /* already generated */
2018
2019 bi = os_zalloc(sizeof(*bi));
2020 if (!bi)
2021 return -1;
2022 bi->type = DPP_BOOTSTRAP_QR_CODE;
2023 pk = dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0);
2024 if (!pk)
2025 goto fail;
2026
2027 len = 4; /* "DPP:" */
2028 len += 4 + os_strlen(pk);
2029 bi->uri = os_malloc(len + 1);
2030 if (!bi->uri)
2031 goto fail;
2032 os_snprintf(bi->uri, len + 1, "DPP:K:%s;;", pk);
2033 wpa_printf(MSG_DEBUG,
2034 "DPP: Auto-generated own bootstrapping key info: URI %s",
2035 bi->uri);
2036
2037 auth->tmp_own_bi = auth->own_bi = bi;
2038
2039 os_free(pk);
2040
2041 return 0;
2042fail:
2043 os_free(pk);
2044 dpp_bootstrap_info_free(bi);
2045 return -1;
2046}
2047
2048
2049struct dpp_authentication * dpp_auth_init(void *msg_ctx,
2050 struct dpp_bootstrap_info *peer_bi,
2051 struct dpp_bootstrap_info *own_bi,
2052 u8 dpp_allowed_roles,
2053 unsigned int neg_freq,
2054 struct hostapd_hw_modes *own_modes,
2055 u16 num_modes)
2056{
2057 struct dpp_authentication *auth;
2058 size_t nonce_len;
2059 EVP_PKEY_CTX *ctx = NULL;
2060 size_t secret_len;
2061 struct wpabuf *pi = NULL;
2062 const u8 *r_pubkey_hash, *i_pubkey_hash;
2063#ifdef CONFIG_TESTING_OPTIONS
2064 u8 test_hash[SHA256_MAC_LEN];
2065#endif /* CONFIG_TESTING_OPTIONS */
2066
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002067 auth = os_zalloc(sizeof(*auth));
2068 if (!auth)
2069 return NULL;
2070 auth->msg_ctx = msg_ctx;
2071 auth->initiator = 1;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002072 auth->waiting_auth_resp = 1;
2073 auth->allowed_roles = dpp_allowed_roles;
2074 auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002075 auth->peer_bi = peer_bi;
2076 auth->own_bi = own_bi;
2077 auth->curve = peer_bi->curve;
2078
Roshan Pius3a1667e2018-07-03 15:17:14 -07002079 if (dpp_autogen_bootstrap_key(auth) < 0 ||
2080 dpp_prepare_channel_list(auth, own_modes, num_modes) < 0)
2081 goto fail;
2082
2083#ifdef CONFIG_TESTING_OPTIONS
2084 if (dpp_nonce_override_len > 0) {
2085 wpa_printf(MSG_INFO, "DPP: TESTING - override I-nonce");
2086 nonce_len = dpp_nonce_override_len;
2087 os_memcpy(auth->i_nonce, dpp_nonce_override, nonce_len);
2088 } else {
2089 nonce_len = auth->curve->nonce_len;
2090 if (random_get_bytes(auth->i_nonce, nonce_len)) {
2091 wpa_printf(MSG_ERROR,
2092 "DPP: Failed to generate I-nonce");
2093 goto fail;
2094 }
2095 }
2096#else /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002097 nonce_len = auth->curve->nonce_len;
2098 if (random_get_bytes(auth->i_nonce, nonce_len)) {
2099 wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
2100 goto fail;
2101 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07002102#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002103 wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len);
2104
Roshan Pius3a1667e2018-07-03 15:17:14 -07002105#ifdef CONFIG_TESTING_OPTIONS
2106 if (dpp_protocol_key_override_len) {
2107 const struct dpp_curve_params *tmp_curve;
2108
2109 wpa_printf(MSG_INFO,
2110 "DPP: TESTING - override protocol key");
2111 auth->own_protocol_key = dpp_set_keypair(
2112 &tmp_curve, dpp_protocol_key_override,
2113 dpp_protocol_key_override_len);
2114 } else {
2115 auth->own_protocol_key = dpp_gen_keypair(auth->curve);
2116 }
2117#else /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002118 auth->own_protocol_key = dpp_gen_keypair(auth->curve);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002119#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002120 if (!auth->own_protocol_key)
2121 goto fail;
2122
2123 pi = dpp_get_pubkey_point(auth->own_protocol_key, 0);
2124 if (!pi)
2125 goto fail;
2126
2127 /* ECDH: M = pI * BR */
2128 ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
2129 if (!ctx ||
2130 EVP_PKEY_derive_init(ctx) != 1 ||
2131 EVP_PKEY_derive_set_peer(ctx, auth->peer_bi->pubkey) != 1 ||
2132 EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
2133 secret_len > DPP_MAX_SHARED_SECRET_LEN ||
2134 EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
2135 wpa_printf(MSG_ERROR,
2136 "DPP: Failed to derive ECDH shared secret: %s",
2137 ERR_error_string(ERR_get_error(), NULL));
2138 goto fail;
2139 }
2140 auth->secret_len = secret_len;
2141 EVP_PKEY_CTX_free(ctx);
2142 ctx = NULL;
2143
2144 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
2145 auth->Mx, auth->secret_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002146 auth->Mx_len = auth->secret_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002147
2148 if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
2149 auth->curve->hash_len) < 0)
2150 goto fail;
2151
Roshan Pius3a1667e2018-07-03 15:17:14 -07002152 r_pubkey_hash = auth->peer_bi->pubkey_hash;
2153 i_pubkey_hash = auth->own_bi->pubkey_hash;
2154
2155#ifdef CONFIG_TESTING_OPTIONS
2156 if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
2157 wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
2158 r_pubkey_hash = NULL;
2159 } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
2160 wpa_printf(MSG_INFO,
2161 "DPP: TESTING - invalid R-Bootstrap Key Hash");
2162 os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
2163 test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
2164 r_pubkey_hash = test_hash;
2165 } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
2166 wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
2167 i_pubkey_hash = NULL;
2168 } else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
2169 wpa_printf(MSG_INFO,
2170 "DPP: TESTING - invalid I-Bootstrap Key Hash");
2171 os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
2172 test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
2173 i_pubkey_hash = test_hash;
2174 } else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) {
2175 wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key");
2176 wpabuf_free(pi);
2177 pi = NULL;
2178 } else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) {
2179 wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key");
2180 wpabuf_free(pi);
2181 pi = wpabuf_alloc(2 * auth->curve->prime_len);
2182 if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0)
2183 goto fail;
2184 }
2185#endif /* CONFIG_TESTING_OPTIONS */
2186
2187 auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash,
2188 i_pubkey_hash, neg_freq);
2189 if (!auth->req_msg)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002190 goto fail;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002191
Roshan Pius3a1667e2018-07-03 15:17:14 -07002192out:
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002193 wpabuf_free(pi);
2194 EVP_PKEY_CTX_free(ctx);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002195 return auth;
2196fail:
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002197 dpp_auth_deinit(auth);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002198 auth = NULL;
2199 goto out;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002200}
2201
2202
2203struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
2204 const char *json)
2205{
2206 size_t nonce_len;
2207 size_t json_len, clear_len;
2208 struct wpabuf *clear = NULL, *msg = NULL;
2209 u8 *wrapped;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002210 size_t attr_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002211
2212 wpa_printf(MSG_DEBUG, "DPP: Build configuration request");
2213
2214 nonce_len = auth->curve->nonce_len;
2215 if (random_get_bytes(auth->e_nonce, nonce_len)) {
2216 wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce");
2217 goto fail;
2218 }
2219 wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", auth->e_nonce, nonce_len);
2220 json_len = os_strlen(json);
2221 wpa_hexdump_ascii(MSG_DEBUG, "DPP: configAttr JSON", json, json_len);
2222
2223 /* { E-nonce, configAttrib }ke */
2224 clear_len = 4 + nonce_len + 4 + json_len;
2225 clear = wpabuf_alloc(clear_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002226 attr_len = 4 + clear_len + AES_BLOCK_SIZE;
2227#ifdef CONFIG_TESTING_OPTIONS
2228 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ)
2229 attr_len += 5;
2230#endif /* CONFIG_TESTING_OPTIONS */
2231 msg = wpabuf_alloc(attr_len);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002232 if (!clear || !msg)
2233 goto fail;
2234
Roshan Pius3a1667e2018-07-03 15:17:14 -07002235#ifdef CONFIG_TESTING_OPTIONS
2236 if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_REQ) {
2237 wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce");
2238 goto skip_e_nonce;
2239 }
2240 if (dpp_test == DPP_TEST_INVALID_E_NONCE_CONF_REQ) {
2241 wpa_printf(MSG_INFO, "DPP: TESTING - invalid E-nonce");
2242 wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
2243 wpabuf_put_le16(clear, nonce_len - 1);
2244 wpabuf_put_data(clear, auth->e_nonce, nonce_len - 1);
2245 goto skip_e_nonce;
2246 }
2247 if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_REQ) {
2248 wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
2249 goto skip_wrapped_data;
2250 }
2251#endif /* CONFIG_TESTING_OPTIONS */
2252
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002253 /* E-nonce */
2254 wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
2255 wpabuf_put_le16(clear, nonce_len);
2256 wpabuf_put_data(clear, auth->e_nonce, nonce_len);
2257
Roshan Pius3a1667e2018-07-03 15:17:14 -07002258#ifdef CONFIG_TESTING_OPTIONS
2259skip_e_nonce:
2260 if (dpp_test == DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ) {
2261 wpa_printf(MSG_INFO, "DPP: TESTING - no configAttrib");
2262 goto skip_conf_attr_obj;
2263 }
2264#endif /* CONFIG_TESTING_OPTIONS */
2265
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002266 /* configAttrib */
2267 wpabuf_put_le16(clear, DPP_ATTR_CONFIG_ATTR_OBJ);
2268 wpabuf_put_le16(clear, json_len);
2269 wpabuf_put_data(clear, json, json_len);
2270
Roshan Pius3a1667e2018-07-03 15:17:14 -07002271#ifdef CONFIG_TESTING_OPTIONS
2272skip_conf_attr_obj:
2273#endif /* CONFIG_TESTING_OPTIONS */
2274
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002275 wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
2276 wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
2277 wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
2278
2279 /* No AES-SIV AD */
2280 wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
2281 if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
2282 wpabuf_head(clear), wpabuf_len(clear),
2283 0, NULL, NULL, wrapped) < 0)
2284 goto fail;
2285 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
2286 wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
2287
Roshan Pius3a1667e2018-07-03 15:17:14 -07002288#ifdef CONFIG_TESTING_OPTIONS
2289 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) {
2290 wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
2291 dpp_build_attr_status(msg, DPP_STATUS_OK);
2292 }
2293skip_wrapped_data:
2294#endif /* CONFIG_TESTING_OPTIONS */
2295
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002296 wpa_hexdump_buf(MSG_DEBUG,
2297 "DPP: Configuration Request frame attributes", msg);
2298 wpabuf_free(clear);
2299 return msg;
2300
2301fail:
2302 wpabuf_free(clear);
2303 wpabuf_free(msg);
2304 return NULL;
2305}
2306
2307
2308static void dpp_auth_success(struct dpp_authentication *auth)
2309{
2310 wpa_printf(MSG_DEBUG,
2311 "DPP: Authentication success - clear temporary keys");
2312 os_memset(auth->Mx, 0, sizeof(auth->Mx));
Roshan Pius3a1667e2018-07-03 15:17:14 -07002313 auth->Mx_len = 0;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002314 os_memset(auth->Nx, 0, sizeof(auth->Nx));
Roshan Pius3a1667e2018-07-03 15:17:14 -07002315 auth->Nx_len = 0;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002316 os_memset(auth->Lx, 0, sizeof(auth->Lx));
Roshan Pius3a1667e2018-07-03 15:17:14 -07002317 auth->Lx_len = 0;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002318 os_memset(auth->k1, 0, sizeof(auth->k1));
2319 os_memset(auth->k2, 0, sizeof(auth->k2));
2320
2321 auth->auth_success = 1;
2322}
2323
2324
2325static int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth)
2326{
2327 struct wpabuf *pix, *prx, *bix, *brx;
2328 const u8 *addr[7];
2329 size_t len[7];
2330 size_t i, num_elem = 0;
2331 size_t nonce_len;
2332 u8 zero = 0;
2333 int res = -1;
2334
2335 /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
2336 nonce_len = auth->curve->nonce_len;
2337
2338 if (auth->initiator) {
2339 pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
2340 prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
2341 if (auth->own_bi)
2342 bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
2343 else
2344 bix = NULL;
2345 brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
2346 } else {
2347 pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
2348 prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
2349 if (auth->peer_bi)
2350 bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
2351 else
2352 bix = NULL;
2353 brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
2354 }
2355 if (!pix || !prx || !brx)
2356 goto fail;
2357
2358 addr[num_elem] = auth->i_nonce;
2359 len[num_elem] = nonce_len;
2360 num_elem++;
2361
2362 addr[num_elem] = auth->r_nonce;
2363 len[num_elem] = nonce_len;
2364 num_elem++;
2365
2366 addr[num_elem] = wpabuf_head(pix);
2367 len[num_elem] = wpabuf_len(pix) / 2;
2368 num_elem++;
2369
2370 addr[num_elem] = wpabuf_head(prx);
2371 len[num_elem] = wpabuf_len(prx) / 2;
2372 num_elem++;
2373
2374 if (bix) {
2375 addr[num_elem] = wpabuf_head(bix);
2376 len[num_elem] = wpabuf_len(bix) / 2;
2377 num_elem++;
2378 }
2379
2380 addr[num_elem] = wpabuf_head(brx);
2381 len[num_elem] = wpabuf_len(brx) / 2;
2382 num_elem++;
2383
2384 addr[num_elem] = &zero;
2385 len[num_elem] = 1;
2386 num_elem++;
2387
2388 wpa_printf(MSG_DEBUG, "DPP: R-auth hash components");
2389 for (i = 0; i < num_elem; i++)
2390 wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
2391 res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth);
2392 if (res == 0)
2393 wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth,
2394 auth->curve->hash_len);
2395fail:
2396 wpabuf_free(pix);
2397 wpabuf_free(prx);
2398 wpabuf_free(bix);
2399 wpabuf_free(brx);
2400 return res;
2401}
2402
2403
2404static int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth)
2405{
2406 struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL;
2407 const u8 *addr[7];
2408 size_t len[7];
2409 size_t i, num_elem = 0;
2410 size_t nonce_len;
2411 u8 one = 1;
2412 int res = -1;
2413
2414 /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
2415 nonce_len = auth->curve->nonce_len;
2416
2417 if (auth->initiator) {
2418 pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
2419 prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
2420 if (auth->own_bi)
2421 bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
2422 else
2423 bix = NULL;
2424 if (!auth->peer_bi)
2425 goto fail;
2426 brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
2427 } else {
2428 pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
2429 prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
2430 if (auth->peer_bi)
2431 bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
2432 else
2433 bix = NULL;
2434 if (!auth->own_bi)
2435 goto fail;
2436 brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
2437 }
2438 if (!pix || !prx || !brx)
2439 goto fail;
2440
2441 addr[num_elem] = auth->r_nonce;
2442 len[num_elem] = nonce_len;
2443 num_elem++;
2444
2445 addr[num_elem] = auth->i_nonce;
2446 len[num_elem] = nonce_len;
2447 num_elem++;
2448
2449 addr[num_elem] = wpabuf_head(prx);
2450 len[num_elem] = wpabuf_len(prx) / 2;
2451 num_elem++;
2452
2453 addr[num_elem] = wpabuf_head(pix);
2454 len[num_elem] = wpabuf_len(pix) / 2;
2455 num_elem++;
2456
2457 addr[num_elem] = wpabuf_head(brx);
2458 len[num_elem] = wpabuf_len(brx) / 2;
2459 num_elem++;
2460
2461 if (bix) {
2462 addr[num_elem] = wpabuf_head(bix);
2463 len[num_elem] = wpabuf_len(bix) / 2;
2464 num_elem++;
2465 }
2466
2467 addr[num_elem] = &one;
2468 len[num_elem] = 1;
2469 num_elem++;
2470
2471 wpa_printf(MSG_DEBUG, "DPP: I-auth hash components");
2472 for (i = 0; i < num_elem; i++)
2473 wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
2474 res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth);
2475 if (res == 0)
2476 wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth,
2477 auth->curve->hash_len);
2478fail:
2479 wpabuf_free(pix);
2480 wpabuf_free(prx);
2481 wpabuf_free(bix);
2482 wpabuf_free(brx);
2483 return res;
2484}
2485
2486
2487static int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
2488{
2489 const EC_GROUP *group;
2490 EC_POINT *l = NULL;
2491 EC_KEY *BI = NULL, *bR = NULL, *pR = NULL;
2492 const EC_POINT *BI_point;
2493 BN_CTX *bnctx;
2494 BIGNUM *lx, *sum, *q;
2495 const BIGNUM *bR_bn, *pR_bn;
2496 int ret = -1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002497
2498 /* L = ((bR + pR) modulo q) * BI */
2499
2500 bnctx = BN_CTX_new();
2501 sum = BN_new();
2502 q = BN_new();
2503 lx = BN_new();
2504 if (!bnctx || !sum || !q || !lx)
2505 goto fail;
2506 BI = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
2507 if (!BI)
2508 goto fail;
2509 BI_point = EC_KEY_get0_public_key(BI);
2510 group = EC_KEY_get0_group(BI);
2511 if (!group)
2512 goto fail;
2513
2514 bR = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
2515 pR = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
2516 if (!bR || !pR)
2517 goto fail;
2518 bR_bn = EC_KEY_get0_private_key(bR);
2519 pR_bn = EC_KEY_get0_private_key(pR);
2520 if (!bR_bn || !pR_bn)
2521 goto fail;
2522 if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
2523 BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1)
2524 goto fail;
2525 l = EC_POINT_new(group);
2526 if (!l ||
2527 EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 ||
2528 EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
2529 bnctx) != 1) {
2530 wpa_printf(MSG_ERROR,
2531 "OpenSSL: failed: %s",
2532 ERR_error_string(ERR_get_error(), NULL));
2533 goto fail;
2534 }
2535
Roshan Pius3a1667e2018-07-03 15:17:14 -07002536 if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002537 goto fail;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002538 wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002539 auth->Lx_len = auth->secret_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002540 ret = 0;
2541fail:
2542 EC_POINT_clear_free(l);
2543 EC_KEY_free(BI);
2544 EC_KEY_free(bR);
2545 EC_KEY_free(pR);
2546 BN_clear_free(lx);
2547 BN_clear_free(sum);
2548 BN_free(q);
2549 BN_CTX_free(bnctx);
2550 return ret;
2551}
2552
2553
2554static int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
2555{
2556 const EC_GROUP *group;
2557 EC_POINT *l = NULL, *sum = NULL;
2558 EC_KEY *bI = NULL, *BR = NULL, *PR = NULL;
2559 const EC_POINT *BR_point, *PR_point;
2560 BN_CTX *bnctx;
2561 BIGNUM *lx;
2562 const BIGNUM *bI_bn;
2563 int ret = -1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002564
2565 /* L = bI * (BR + PR) */
2566
2567 bnctx = BN_CTX_new();
2568 lx = BN_new();
2569 if (!bnctx || !lx)
2570 goto fail;
2571 BR = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
2572 PR = EVP_PKEY_get1_EC_KEY(auth->peer_protocol_key);
2573 if (!BR || !PR)
2574 goto fail;
2575 BR_point = EC_KEY_get0_public_key(BR);
2576 PR_point = EC_KEY_get0_public_key(PR);
2577
2578 bI = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
2579 if (!bI)
2580 goto fail;
2581 group = EC_KEY_get0_group(bI);
2582 bI_bn = EC_KEY_get0_private_key(bI);
2583 if (!group || !bI_bn)
2584 goto fail;
2585 sum = EC_POINT_new(group);
2586 l = EC_POINT_new(group);
2587 if (!sum || !l ||
2588 EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 ||
2589 EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 ||
2590 EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
2591 bnctx) != 1) {
2592 wpa_printf(MSG_ERROR,
2593 "OpenSSL: failed: %s",
2594 ERR_error_string(ERR_get_error(), NULL));
2595 goto fail;
2596 }
2597
Roshan Pius3a1667e2018-07-03 15:17:14 -07002598 if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002599 goto fail;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002600 wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002601 auth->Lx_len = auth->secret_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002602 ret = 0;
2603fail:
2604 EC_POINT_clear_free(l);
Hai Shalom39ba6fc2019-01-22 12:40:38 -08002605 EC_POINT_clear_free(sum);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002606 EC_KEY_free(bI);
2607 EC_KEY_free(BR);
2608 EC_KEY_free(PR);
2609 BN_clear_free(lx);
2610 BN_CTX_free(bnctx);
2611 return ret;
2612}
2613
2614
Roshan Pius3a1667e2018-07-03 15:17:14 -07002615static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002616{
2617 size_t nonce_len;
2618 EVP_PKEY_CTX *ctx = NULL;
2619 size_t secret_len;
2620 struct wpabuf *msg, *pr = NULL;
2621 u8 r_auth[4 + DPP_MAX_HASH_LEN];
Roshan Pius3a1667e2018-07-03 15:17:14 -07002622 u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002623 size_t wrapped_r_auth_len;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002624 int ret = -1;
2625 const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce;
2626 enum dpp_status_error status = DPP_STATUS_OK;
2627#ifdef CONFIG_TESTING_OPTIONS
2628 u8 test_hash[SHA256_MAC_LEN];
2629#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002630
2631 wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
Roshan Pius3a1667e2018-07-03 15:17:14 -07002632 if (!auth->own_bi)
2633 return -1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002634
Roshan Pius3a1667e2018-07-03 15:17:14 -07002635#ifdef CONFIG_TESTING_OPTIONS
2636 if (dpp_nonce_override_len > 0) {
2637 wpa_printf(MSG_INFO, "DPP: TESTING - override R-nonce");
2638 nonce_len = dpp_nonce_override_len;
2639 os_memcpy(auth->r_nonce, dpp_nonce_override, nonce_len);
2640 } else {
2641 nonce_len = auth->curve->nonce_len;
2642 if (random_get_bytes(auth->r_nonce, nonce_len)) {
2643 wpa_printf(MSG_ERROR,
2644 "DPP: Failed to generate R-nonce");
2645 goto fail;
2646 }
2647 }
2648#else /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002649 nonce_len = auth->curve->nonce_len;
2650 if (random_get_bytes(auth->r_nonce, nonce_len)) {
2651 wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
2652 goto fail;
2653 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07002654#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002655 wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len);
2656
Roshan Pius3a1667e2018-07-03 15:17:14 -07002657#ifdef CONFIG_TESTING_OPTIONS
2658 if (dpp_protocol_key_override_len) {
2659 const struct dpp_curve_params *tmp_curve;
2660
2661 wpa_printf(MSG_INFO,
2662 "DPP: TESTING - override protocol key");
2663 auth->own_protocol_key = dpp_set_keypair(
2664 &tmp_curve, dpp_protocol_key_override,
2665 dpp_protocol_key_override_len);
2666 } else {
2667 auth->own_protocol_key = dpp_gen_keypair(auth->curve);
2668 }
2669#else /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002670 auth->own_protocol_key = dpp_gen_keypair(auth->curve);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002671#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002672 if (!auth->own_protocol_key)
2673 goto fail;
2674
2675 pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
2676 if (!pr)
2677 goto fail;
2678
2679 /* ECDH: N = pR * PI */
2680 ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
2681 if (!ctx ||
2682 EVP_PKEY_derive_init(ctx) != 1 ||
2683 EVP_PKEY_derive_set_peer(ctx, auth->peer_protocol_key) != 1 ||
2684 EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
2685 secret_len > DPP_MAX_SHARED_SECRET_LEN ||
2686 EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) {
2687 wpa_printf(MSG_ERROR,
2688 "DPP: Failed to derive ECDH shared secret: %s",
2689 ERR_error_string(ERR_get_error(), NULL));
2690 goto fail;
2691 }
2692 EVP_PKEY_CTX_free(ctx);
2693 ctx = NULL;
2694
2695 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
2696 auth->Nx, auth->secret_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002697 auth->Nx_len = auth->secret_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002698
2699 if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
2700 auth->curve->hash_len) < 0)
2701 goto fail;
2702
2703 if (auth->own_bi && auth->peer_bi) {
2704 /* Mutual authentication */
2705 if (dpp_auth_derive_l_responder(auth) < 0)
2706 goto fail;
2707 }
2708
2709 if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
2710 goto fail;
2711
2712 /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
2713 WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG);
2714 WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002715 if (dpp_gen_r_auth(auth, r_auth + 4) < 0)
2716 goto fail;
2717#ifdef CONFIG_TESTING_OPTIONS
2718 if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) {
2719 wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch");
2720 r_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
2721 }
2722#endif /* CONFIG_TESTING_OPTIONS */
2723 if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002724 r_auth, 4 + auth->curve->hash_len,
2725 0, NULL, NULL, wrapped_r_auth) < 0)
2726 goto fail;
2727 wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE;
2728 wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke",
2729 wrapped_r_auth, wrapped_r_auth_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002730 w_r_auth = wrapped_r_auth;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002731
Roshan Pius3a1667e2018-07-03 15:17:14 -07002732 r_pubkey_hash = auth->own_bi->pubkey_hash;
2733 if (auth->peer_bi)
2734 i_pubkey_hash = auth->peer_bi->pubkey_hash;
2735 else
2736 i_pubkey_hash = NULL;
2737
2738 i_nonce = auth->i_nonce;
2739 r_nonce = auth->r_nonce;
2740
2741#ifdef CONFIG_TESTING_OPTIONS
2742 if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
2743 wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
2744 r_pubkey_hash = NULL;
2745 } else if (dpp_test ==
2746 DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
2747 wpa_printf(MSG_INFO,
2748 "DPP: TESTING - invalid R-Bootstrap Key Hash");
2749 os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
2750 test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
2751 r_pubkey_hash = test_hash;
2752 } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
2753 wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
2754 i_pubkey_hash = NULL;
2755 } else if (dpp_test ==
2756 DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
2757 wpa_printf(MSG_INFO,
2758 "DPP: TESTING - invalid I-Bootstrap Key Hash");
2759 if (i_pubkey_hash)
2760 os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
2761 else
2762 os_memset(test_hash, 0, SHA256_MAC_LEN);
2763 test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
2764 i_pubkey_hash = test_hash;
2765 } else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) {
2766 wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key");
2767 wpabuf_free(pr);
2768 pr = NULL;
2769 } else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) {
2770 wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key");
2771 wpabuf_free(pr);
2772 pr = wpabuf_alloc(2 * auth->curve->prime_len);
2773 if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0)
2774 goto fail;
2775 } else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) {
2776 wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth");
2777 w_r_auth = NULL;
2778 wrapped_r_auth_len = 0;
2779 } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
2780 wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
2781 status = 255;
2782 } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) {
2783 wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
2784 status = 254;
2785 } else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) {
2786 wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce");
2787 r_nonce = NULL;
2788 } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
2789 wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
2790 i_nonce = NULL;
2791 }
2792#endif /* CONFIG_TESTING_OPTIONS */
2793
2794 msg = dpp_auth_build_resp(auth, status, pr, nonce_len,
2795 r_pubkey_hash, i_pubkey_hash,
2796 r_nonce, i_nonce,
2797 w_r_auth, wrapped_r_auth_len,
2798 auth->k2);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002799 if (!msg)
2800 goto fail;
2801 wpabuf_free(auth->resp_msg);
2802 auth->resp_msg = msg;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002803 ret = 0;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002804fail:
2805 wpabuf_free(pr);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002806 return ret;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002807}
2808
2809
2810static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
2811 enum dpp_status_error status)
2812{
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002813 struct wpabuf *msg;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002814 const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce;
2815#ifdef CONFIG_TESTING_OPTIONS
2816 u8 test_hash[SHA256_MAC_LEN];
2817#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002818
Roshan Pius3a1667e2018-07-03 15:17:14 -07002819 if (!auth->own_bi)
2820 return -1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002821 wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
2822
Roshan Pius3a1667e2018-07-03 15:17:14 -07002823 r_pubkey_hash = auth->own_bi->pubkey_hash;
2824 if (auth->peer_bi)
2825 i_pubkey_hash = auth->peer_bi->pubkey_hash;
2826 else
2827 i_pubkey_hash = NULL;
2828
2829 i_nonce = auth->i_nonce;
2830
2831#ifdef CONFIG_TESTING_OPTIONS
2832 if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
2833 wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
2834 r_pubkey_hash = NULL;
2835 } else if (dpp_test ==
2836 DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
2837 wpa_printf(MSG_INFO,
2838 "DPP: TESTING - invalid R-Bootstrap Key Hash");
2839 os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
2840 test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
2841 r_pubkey_hash = test_hash;
2842 } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
2843 wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
2844 i_pubkey_hash = NULL;
2845 } else if (dpp_test ==
2846 DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
2847 wpa_printf(MSG_INFO,
2848 "DPP: TESTING - invalid I-Bootstrap Key Hash");
2849 if (i_pubkey_hash)
2850 os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
2851 else
2852 os_memset(test_hash, 0, SHA256_MAC_LEN);
2853 test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
2854 i_pubkey_hash = test_hash;
2855 } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
2856 wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
2857 status = -1;
2858 } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
2859 wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
2860 i_nonce = NULL;
2861 }
2862#endif /* CONFIG_TESTING_OPTIONS */
2863
2864 msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len,
2865 r_pubkey_hash, i_pubkey_hash,
2866 NULL, i_nonce, NULL, 0, auth->k1);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002867 if (!msg)
Roshan Pius3a1667e2018-07-03 15:17:14 -07002868 return -1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002869 wpabuf_free(auth->resp_msg);
2870 auth->resp_msg = msg;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002871 return 0;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002872}
2873
2874
2875struct dpp_authentication *
2876dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
2877 struct dpp_bootstrap_info *peer_bi,
2878 struct dpp_bootstrap_info *own_bi,
2879 unsigned int freq, const u8 *hdr, const u8 *attr_start,
Roshan Pius3a1667e2018-07-03 15:17:14 -07002880 size_t attr_len)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002881{
2882 EVP_PKEY *pi = NULL;
2883 EVP_PKEY_CTX *ctx = NULL;
2884 size_t secret_len;
2885 const u8 *addr[2];
2886 size_t len[2];
2887 u8 *unwrapped = NULL;
2888 size_t unwrapped_len = 0;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002889 const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap,
2890 *channel;
2891 u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len,
2892 i_bootstrap_len, channel_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002893 struct dpp_authentication *auth = NULL;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002894
Roshan Pius3a1667e2018-07-03 15:17:14 -07002895#ifdef CONFIG_TESTING_OPTIONS
2896 if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) {
2897 wpa_printf(MSG_INFO,
2898 "DPP: TESTING - stop at Authentication Request");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002899 return NULL;
Roshan Pius3a1667e2018-07-03 15:17:14 -07002900 }
2901#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002902
Roshan Pius3a1667e2018-07-03 15:17:14 -07002903 wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
2904 &wrapped_data_len);
2905 if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
2906 wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
2907 "Missing or invalid required Wrapped Data attribute");
2908 return NULL;
2909 }
2910 wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
2911 wrapped_data, wrapped_data_len);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002912 attr_len = wrapped_data - 4 - attr_start;
2913
2914 auth = os_zalloc(sizeof(*auth));
2915 if (!auth)
2916 goto fail;
2917 auth->msg_ctx = msg_ctx;
2918 auth->peer_bi = peer_bi;
2919 auth->own_bi = own_bi;
2920 auth->curve = own_bi->curve;
2921 auth->curr_freq = freq;
2922
Roshan Pius3a1667e2018-07-03 15:17:14 -07002923 channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL,
2924 &channel_len);
2925 if (channel) {
2926 int neg_freq;
2927
2928 if (channel_len < 2) {
2929 dpp_auth_fail(auth, "Too short Channel attribute");
2930 goto fail;
2931 }
2932
2933 neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]);
2934 wpa_printf(MSG_DEBUG,
2935 "DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d",
2936 channel[0], channel[1], neg_freq);
2937 if (neg_freq < 0) {
2938 dpp_auth_fail(auth,
2939 "Unsupported Channel attribute value");
2940 goto fail;
2941 }
2942
2943 if (auth->curr_freq != (unsigned int) neg_freq) {
2944 wpa_printf(MSG_DEBUG,
2945 "DPP: Changing negotiation channel from %u MHz to %u MHz",
2946 freq, neg_freq);
2947 auth->curr_freq = neg_freq;
2948 }
2949 }
2950
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002951 i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY,
2952 &i_proto_len);
2953 if (!i_proto) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07002954 dpp_auth_fail(auth,
2955 "Missing required Initiator Protocol Key attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002956 goto fail;
2957 }
2958 wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key",
2959 i_proto, i_proto_len);
2960
2961 /* M = bR * PI */
2962 pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len);
2963 if (!pi) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07002964 dpp_auth_fail(auth, "Invalid Initiator Protocol Key");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002965 goto fail;
2966 }
2967 dpp_debug_print_key("Peer (Initiator) Protocol Key", pi);
2968
2969 ctx = EVP_PKEY_CTX_new(own_bi->pubkey, NULL);
2970 if (!ctx ||
2971 EVP_PKEY_derive_init(ctx) != 1 ||
2972 EVP_PKEY_derive_set_peer(ctx, pi) != 1 ||
2973 EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
2974 secret_len > DPP_MAX_SHARED_SECRET_LEN ||
2975 EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
2976 wpa_printf(MSG_ERROR,
2977 "DPP: Failed to derive ECDH shared secret: %s",
2978 ERR_error_string(ERR_get_error(), NULL));
Roshan Pius3a1667e2018-07-03 15:17:14 -07002979 dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002980 goto fail;
2981 }
2982 auth->secret_len = secret_len;
2983 EVP_PKEY_CTX_free(ctx);
2984 ctx = NULL;
2985
2986 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
2987 auth->Mx, auth->secret_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07002988 auth->Mx_len = auth->secret_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07002989
2990 if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
2991 auth->curve->hash_len) < 0)
2992 goto fail;
2993
2994 addr[0] = hdr;
2995 len[0] = DPP_HDR_LEN;
2996 addr[1] = attr_start;
2997 len[1] = attr_len;
2998 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
2999 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
3000 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
3001 wrapped_data, wrapped_data_len);
3002 unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
3003 unwrapped = os_malloc(unwrapped_len);
3004 if (!unwrapped)
3005 goto fail;
3006 if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
3007 wrapped_data, wrapped_data_len,
3008 2, addr, len, unwrapped) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003009 dpp_auth_fail(auth, "AES-SIV decryption failed");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003010 goto fail;
3011 }
3012 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
3013 unwrapped, unwrapped_len);
3014
3015 if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003016 dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003017 goto fail;
3018 }
3019
3020 i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
3021 &i_nonce_len);
3022 if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003023 dpp_auth_fail(auth, "Missing or invalid I-nonce");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003024 goto fail;
3025 }
3026 wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
3027 os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
3028
3029 i_capab = dpp_get_attr(unwrapped, unwrapped_len,
3030 DPP_ATTR_I_CAPABILITIES,
3031 &i_capab_len);
3032 if (!i_capab || i_capab_len < 1) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003033 dpp_auth_fail(auth, "Missing or invalid I-capabilities");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003034 goto fail;
3035 }
3036 auth->i_capab = i_capab[0];
3037 wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab);
3038
3039 bin_clear_free(unwrapped, unwrapped_len);
3040 unwrapped = NULL;
3041
3042 switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) {
3043 case DPP_CAPAB_ENROLLEE:
3044 if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) {
3045 wpa_printf(MSG_DEBUG,
3046 "DPP: Local policy does not allow Configurator role");
3047 goto not_compatible;
3048 }
3049 wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
3050 auth->configurator = 1;
3051 break;
3052 case DPP_CAPAB_CONFIGURATOR:
3053 if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) {
3054 wpa_printf(MSG_DEBUG,
3055 "DPP: Local policy does not allow Enrollee role");
3056 goto not_compatible;
3057 }
3058 wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
3059 auth->configurator = 0;
3060 break;
Roshan Pius3a1667e2018-07-03 15:17:14 -07003061 case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE:
3062 if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) {
3063 wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
3064 auth->configurator = 0;
3065 } else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) {
3066 wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
3067 auth->configurator = 1;
3068 } else {
3069 wpa_printf(MSG_DEBUG,
3070 "DPP: Local policy does not allow Configurator/Enrollee role");
3071 goto not_compatible;
3072 }
3073 break;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003074 default:
3075 wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities");
Roshan Pius3a1667e2018-07-03 15:17:14 -07003076 wpa_msg(auth->msg_ctx, MSG_INFO,
3077 DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x",
3078 auth->i_capab & DPP_CAPAB_ROLE_MASK);
3079 goto fail;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003080 }
3081
3082 auth->peer_protocol_key = pi;
3083 pi = NULL;
3084 if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) {
3085 char hex[SHA256_MAC_LEN * 2 + 1];
3086
3087 wpa_printf(MSG_DEBUG,
3088 "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time");
3089 if (dpp_auth_build_resp_status(auth,
3090 DPP_STATUS_RESPONSE_PENDING) < 0)
3091 goto fail;
3092 i_bootstrap = dpp_get_attr(attr_start, attr_len,
3093 DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
3094 &i_bootstrap_len);
3095 if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) {
3096 auth->response_pending = 1;
3097 os_memcpy(auth->waiting_pubkey_hash,
3098 i_bootstrap, i_bootstrap_len);
3099 wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap,
3100 i_bootstrap_len);
3101 } else {
3102 hex[0] = '\0';
3103 }
3104
3105 wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE
3106 "%s", hex);
3107 return auth;
3108 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07003109 if (dpp_auth_build_resp_ok(auth) < 0)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003110 goto fail;
3111
3112 return auth;
3113
3114not_compatible:
3115 wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
3116 "i-capab=0x%02x", auth->i_capab);
3117 if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)
3118 auth->configurator = 1;
3119 else
3120 auth->configurator = 0;
3121 auth->peer_protocol_key = pi;
3122 pi = NULL;
3123 if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0)
3124 goto fail;
3125
3126 auth->remove_on_tx_status = 1;
3127 return auth;
3128fail:
3129 bin_clear_free(unwrapped, unwrapped_len);
3130 EVP_PKEY_free(pi);
3131 EVP_PKEY_CTX_free(ctx);
3132 dpp_auth_deinit(auth);
3133 return NULL;
3134}
3135
3136
3137int dpp_notify_new_qr_code(struct dpp_authentication *auth,
3138 struct dpp_bootstrap_info *peer_bi)
3139{
3140 if (!auth || !auth->response_pending ||
3141 os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash,
3142 SHA256_MAC_LEN) != 0)
3143 return 0;
3144
3145 wpa_printf(MSG_DEBUG,
3146 "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with "
3147 MACSTR, MAC2STR(auth->peer_mac_addr));
3148 auth->peer_bi = peer_bi;
3149
Roshan Pius3a1667e2018-07-03 15:17:14 -07003150 if (dpp_auth_build_resp_ok(auth) < 0)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003151 return -1;
3152
3153 return 1;
3154}
3155
3156
Roshan Pius3a1667e2018-07-03 15:17:14 -07003157static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth,
3158 enum dpp_status_error status)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003159{
3160 struct wpabuf *msg;
3161 u8 i_auth[4 + DPP_MAX_HASH_LEN];
3162 size_t i_auth_len;
Roshan Pius3a1667e2018-07-03 15:17:14 -07003163 u8 r_nonce[4 + DPP_MAX_NONCE_LEN];
3164 size_t r_nonce_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003165 const u8 *addr[2];
3166 size_t len[2], attr_len;
3167 u8 *wrapped_i_auth;
Roshan Pius3a1667e2018-07-03 15:17:14 -07003168 u8 *wrapped_r_nonce;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003169 u8 *attr_start, *attr_end;
Roshan Pius3a1667e2018-07-03 15:17:14 -07003170 const u8 *r_pubkey_hash, *i_pubkey_hash;
3171#ifdef CONFIG_TESTING_OPTIONS
3172 u8 test_hash[SHA256_MAC_LEN];
3173#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003174
3175 wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation");
3176
3177 i_auth_len = 4 + auth->curve->hash_len;
Roshan Pius3a1667e2018-07-03 15:17:14 -07003178 r_nonce_len = 4 + auth->curve->nonce_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003179 /* Build DPP Authentication Confirmation frame attributes */
3180 attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
Roshan Pius3a1667e2018-07-03 15:17:14 -07003181 4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE;
3182#ifdef CONFIG_TESTING_OPTIONS
3183 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF)
3184 attr_len += 5;
3185#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003186 msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len);
3187 if (!msg)
3188 goto fail;
3189
3190 attr_start = wpabuf_put(msg, 0);
3191
Roshan Pius3a1667e2018-07-03 15:17:14 -07003192 r_pubkey_hash = auth->peer_bi->pubkey_hash;
3193 if (auth->own_bi)
3194 i_pubkey_hash = auth->own_bi->pubkey_hash;
3195 else
3196 i_pubkey_hash = NULL;
3197
3198#ifdef CONFIG_TESTING_OPTIONS
3199 if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) {
3200 wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
3201 goto skip_status;
3202 } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) {
3203 wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
3204 status = 254;
3205 }
3206#endif /* CONFIG_TESTING_OPTIONS */
3207
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003208 /* DPP Status */
Roshan Pius3a1667e2018-07-03 15:17:14 -07003209 dpp_build_attr_status(msg, status);
3210
3211#ifdef CONFIG_TESTING_OPTIONS
3212skip_status:
3213 if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
3214 wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
3215 r_pubkey_hash = NULL;
3216 } else if (dpp_test ==
3217 DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
3218 wpa_printf(MSG_INFO,
3219 "DPP: TESTING - invalid R-Bootstrap Key Hash");
3220 os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
3221 test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
3222 r_pubkey_hash = test_hash;
3223 } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
3224 wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
3225 i_pubkey_hash = NULL;
3226 } else if (dpp_test ==
3227 DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
3228 wpa_printf(MSG_INFO,
3229 "DPP: TESTING - invalid I-Bootstrap Key Hash");
3230 if (i_pubkey_hash)
3231 os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
3232 else
3233 os_memset(test_hash, 0, SHA256_MAC_LEN);
3234 test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
3235 i_pubkey_hash = test_hash;
3236 }
3237#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003238
3239 /* Responder Bootstrapping Key Hash */
Roshan Pius3a1667e2018-07-03 15:17:14 -07003240 dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003241
Roshan Pius3a1667e2018-07-03 15:17:14 -07003242 /* Initiator Bootstrapping Key Hash (mutual authentication) */
3243 dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
3244
3245#ifdef CONFIG_TESTING_OPTIONS
3246 if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF)
3247 goto skip_wrapped_data;
3248 if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
3249 i_auth_len = 0;
3250#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003251
3252 attr_end = wpabuf_put(msg, 0);
3253
3254 /* OUI, OUI type, Crypto Suite, DPP frame type */
3255 addr[0] = wpabuf_head_u8(msg) + 2;
3256 len[0] = 3 + 1 + 1 + 1;
3257 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
3258
3259 /* Attributes before Wrapped Data */
3260 addr[1] = attr_start;
3261 len[1] = attr_end - attr_start;
3262 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
3263
Roshan Pius3a1667e2018-07-03 15:17:14 -07003264 if (status == DPP_STATUS_OK) {
3265 /* I-auth wrapped with ke */
3266 wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
3267 wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE);
3268 wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
3269
3270#ifdef CONFIG_TESTING_OPTIONS
3271 if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
3272 goto skip_i_auth;
3273#endif /* CONFIG_TESTING_OPTIONS */
3274
3275 /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |]
3276 * 1) */
3277 WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
3278 WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
3279 if (dpp_gen_i_auth(auth, i_auth + 4) < 0)
3280 goto fail;
3281
3282#ifdef CONFIG_TESTING_OPTIONS
3283 if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) {
3284 wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch");
3285 i_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
3286 }
3287skip_i_auth:
3288#endif /* CONFIG_TESTING_OPTIONS */
3289 if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
3290 i_auth, i_auth_len,
3291 2, addr, len, wrapped_i_auth) < 0)
3292 goto fail;
3293 wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
3294 wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
3295 } else {
3296 /* R-nonce wrapped with k2 */
3297 wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
3298 wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE);
3299 wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE);
3300
3301 WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE);
3302 WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len);
3303 os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len);
3304
3305 if (aes_siv_encrypt(auth->k2, auth->curve->hash_len,
3306 r_nonce, r_nonce_len,
3307 2, addr, len, wrapped_r_nonce) < 0)
3308 goto fail;
3309 wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2",
3310 wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE);
3311 }
3312
3313#ifdef CONFIG_TESTING_OPTIONS
3314 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) {
3315 wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
3316 dpp_build_attr_status(msg, DPP_STATUS_OK);
3317 }
3318skip_wrapped_data:
3319#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003320
3321 wpa_hexdump_buf(MSG_DEBUG,
3322 "DPP: Authentication Confirmation frame attributes",
3323 msg);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003324 if (status == DPP_STATUS_OK)
3325 dpp_auth_success(auth);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003326
3327 return msg;
3328
3329fail:
Roshan Pius3a1667e2018-07-03 15:17:14 -07003330 wpabuf_free(msg);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003331 return NULL;
3332}
3333
3334
3335static void
3336dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
3337 const u8 *attr_start, size_t attr_len,
3338 const u8 *wrapped_data, u16 wrapped_data_len,
3339 enum dpp_status_error status)
3340{
3341 const u8 *addr[2];
3342 size_t len[2];
3343 u8 *unwrapped = NULL;
3344 size_t unwrapped_len = 0;
3345 const u8 *i_nonce, *r_capab;
3346 u16 i_nonce_len, r_capab_len;
3347
3348 if (status == DPP_STATUS_NOT_COMPATIBLE) {
3349 wpa_printf(MSG_DEBUG,
3350 "DPP: Responder reported incompatible roles");
3351 } else if (status == DPP_STATUS_RESPONSE_PENDING) {
3352 wpa_printf(MSG_DEBUG,
3353 "DPP: Responder reported more time needed");
3354 } else {
3355 wpa_printf(MSG_DEBUG,
3356 "DPP: Responder reported failure (status %d)",
3357 status);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003358 dpp_auth_fail(auth, "Responder reported failure");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003359 return;
3360 }
3361
3362 addr[0] = hdr;
3363 len[0] = DPP_HDR_LEN;
3364 addr[1] = attr_start;
3365 len[1] = attr_len;
3366 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
3367 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
3368 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
3369 wrapped_data, wrapped_data_len);
3370 unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
3371 unwrapped = os_malloc(unwrapped_len);
3372 if (!unwrapped)
3373 goto fail;
3374 if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
3375 wrapped_data, wrapped_data_len,
3376 2, addr, len, unwrapped) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003377 dpp_auth_fail(auth, "AES-SIV decryption failed");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003378 goto fail;
3379 }
3380 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
3381 unwrapped, unwrapped_len);
3382
3383 if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003384 dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003385 goto fail;
3386 }
3387
3388 i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
3389 &i_nonce_len);
3390 if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003391 dpp_auth_fail(auth, "Missing or invalid I-nonce");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003392 goto fail;
3393 }
3394 wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
3395 if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003396 dpp_auth_fail(auth, "I-nonce mismatch");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003397 goto fail;
3398 }
3399
3400 r_capab = dpp_get_attr(unwrapped, unwrapped_len,
3401 DPP_ATTR_R_CAPABILITIES,
3402 &r_capab_len);
3403 if (!r_capab || r_capab_len < 1) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003404 dpp_auth_fail(auth, "Missing or invalid R-capabilities");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003405 goto fail;
3406 }
3407 auth->r_capab = r_capab[0];
3408 wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
3409 if (status == DPP_STATUS_NOT_COMPATIBLE) {
3410 wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
3411 "r-capab=0x%02x", auth->r_capab);
3412 } else if (status == DPP_STATUS_RESPONSE_PENDING) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003413 u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
3414
3415 if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
3416 (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
3417 wpa_msg(auth->msg_ctx, MSG_INFO,
3418 DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x",
3419 role);
3420 } else {
3421 wpa_printf(MSG_DEBUG,
3422 "DPP: Continue waiting for full DPP Authentication Response");
3423 wpa_msg(auth->msg_ctx, MSG_INFO,
3424 DPP_EVENT_RESPONSE_PENDING "%s",
3425 auth->tmp_own_bi ? auth->tmp_own_bi->uri : "");
3426 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003427 }
3428fail:
3429 bin_clear_free(unwrapped, unwrapped_len);
3430}
3431
3432
3433struct wpabuf *
3434dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
3435 const u8 *attr_start, size_t attr_len)
3436{
3437 EVP_PKEY *pr;
3438 EVP_PKEY_CTX *ctx = NULL;
3439 size_t secret_len;
3440 const u8 *addr[2];
3441 size_t len[2];
3442 u8 *unwrapped = NULL, *unwrapped2 = NULL;
3443 size_t unwrapped_len = 0, unwrapped2_len = 0;
3444 const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto,
3445 *r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth;
3446 u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
3447 r_proto_len, r_nonce_len, i_nonce_len, r_capab_len,
3448 wrapped2_len, r_auth_len;
3449 u8 r_auth2[DPP_MAX_HASH_LEN];
Roshan Pius3a1667e2018-07-03 15:17:14 -07003450 u8 role;
3451
3452#ifdef CONFIG_TESTING_OPTIONS
3453 if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) {
3454 wpa_printf(MSG_INFO,
3455 "DPP: TESTING - stop at Authentication Response");
3456 return NULL;
3457 }
3458#endif /* CONFIG_TESTING_OPTIONS */
3459
3460 if (!auth->initiator) {
3461 dpp_auth_fail(auth, "Unexpected Authentication Response");
3462 return NULL;
3463 }
3464
3465 auth->waiting_auth_resp = 0;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003466
3467 wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
3468 &wrapped_data_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003469 if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
3470 dpp_auth_fail(auth,
3471 "Missing or invalid required Wrapped Data attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003472 return NULL;
3473 }
3474 wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
3475 wrapped_data, wrapped_data_len);
3476
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003477 attr_len = wrapped_data - 4 - attr_start;
3478
3479 r_bootstrap = dpp_get_attr(attr_start, attr_len,
3480 DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
3481 &r_bootstrap_len);
3482 if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003483 dpp_auth_fail(auth,
3484 "Missing or invalid required Responder Bootstrapping Key Hash attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003485 return NULL;
3486 }
3487 wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
3488 r_bootstrap, r_bootstrap_len);
3489 if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash,
3490 SHA256_MAC_LEN) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003491 dpp_auth_fail(auth,
3492 "Unexpected Responder Bootstrapping Key Hash value");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003493 wpa_hexdump(MSG_DEBUG,
3494 "DPP: Expected Responder Bootstrapping Key Hash",
3495 auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
3496 return NULL;
3497 }
3498
3499 i_bootstrap = dpp_get_attr(attr_start, attr_len,
3500 DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
3501 &i_bootstrap_len);
3502 if (i_bootstrap) {
3503 if (i_bootstrap_len != SHA256_MAC_LEN) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003504 dpp_auth_fail(auth,
3505 "Invalid Initiator Bootstrapping Key Hash attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003506 return NULL;
3507 }
3508 wpa_hexdump(MSG_MSGDUMP,
3509 "DPP: Initiator Bootstrapping Key Hash",
3510 i_bootstrap, i_bootstrap_len);
3511 if (!auth->own_bi ||
3512 os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash,
3513 SHA256_MAC_LEN) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003514 dpp_auth_fail(auth,
3515 "Initiator Bootstrapping Key Hash attribute did not match");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003516 return NULL;
3517 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07003518 } else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) {
3519 /* PKEX bootstrapping mandates use of mutual authentication */
3520 dpp_auth_fail(auth,
3521 "Missing Initiator Bootstrapping Key Hash attribute");
3522 return NULL;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003523 }
3524
3525 status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
3526 &status_len);
3527 if (!status || status_len < 1) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003528 dpp_auth_fail(auth,
3529 "Missing or invalid required DPP Status attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003530 return NULL;
3531 }
3532 wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
3533 auth->auth_resp_status = status[0];
3534 if (status[0] != DPP_STATUS_OK) {
3535 dpp_auth_resp_rx_status(auth, hdr, attr_start,
3536 attr_len, wrapped_data,
3537 wrapped_data_len, status[0]);
3538 return NULL;
3539 }
3540
Roshan Pius3a1667e2018-07-03 15:17:14 -07003541 if (!i_bootstrap && auth->own_bi) {
3542 wpa_printf(MSG_DEBUG,
3543 "DPP: Responder decided not to use mutual authentication");
3544 auth->own_bi = NULL;
3545 }
3546
3547 wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_DIRECTION "mutual=%d",
3548 auth->own_bi != NULL);
3549
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003550 r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
3551 &r_proto_len);
3552 if (!r_proto) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003553 dpp_auth_fail(auth,
3554 "Missing required Responder Protocol Key attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003555 return NULL;
3556 }
3557 wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
3558 r_proto, r_proto_len);
3559
3560 /* N = pI * PR */
3561 pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len);
3562 if (!pr) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003563 dpp_auth_fail(auth, "Invalid Responder Protocol Key");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003564 return NULL;
3565 }
3566 dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
3567
3568 ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
3569 if (!ctx ||
3570 EVP_PKEY_derive_init(ctx) != 1 ||
3571 EVP_PKEY_derive_set_peer(ctx, pr) != 1 ||
3572 EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
3573 secret_len > DPP_MAX_SHARED_SECRET_LEN ||
3574 EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) {
3575 wpa_printf(MSG_ERROR,
3576 "DPP: Failed to derive ECDH shared secret: %s",
3577 ERR_error_string(ERR_get_error(), NULL));
Roshan Pius3a1667e2018-07-03 15:17:14 -07003578 dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003579 goto fail;
3580 }
3581 EVP_PKEY_CTX_free(ctx);
3582 ctx = NULL;
3583 auth->peer_protocol_key = pr;
3584 pr = NULL;
3585
3586 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
3587 auth->Nx, auth->secret_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003588 auth->Nx_len = auth->secret_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003589
3590 if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
3591 auth->curve->hash_len) < 0)
3592 goto fail;
3593
3594 addr[0] = hdr;
3595 len[0] = DPP_HDR_LEN;
3596 addr[1] = attr_start;
3597 len[1] = attr_len;
3598 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
3599 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
3600 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
3601 wrapped_data, wrapped_data_len);
3602 unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
3603 unwrapped = os_malloc(unwrapped_len);
3604 if (!unwrapped)
3605 goto fail;
3606 if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
3607 wrapped_data, wrapped_data_len,
3608 2, addr, len, unwrapped) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003609 dpp_auth_fail(auth, "AES-SIV decryption failed");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003610 goto fail;
3611 }
3612 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
3613 unwrapped, unwrapped_len);
3614
3615 if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003616 dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003617 goto fail;
3618 }
3619
3620 r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
3621 &r_nonce_len);
3622 if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003623 dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003624 goto fail;
3625 }
3626 wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
3627 os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
3628
3629 i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
3630 &i_nonce_len);
3631 if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003632 dpp_auth_fail(auth, "Missing or invalid I-nonce");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003633 goto fail;
3634 }
3635 wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
3636 if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003637 dpp_auth_fail(auth, "I-nonce mismatch");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003638 goto fail;
3639 }
3640
3641 if (auth->own_bi && auth->peer_bi) {
3642 /* Mutual authentication */
3643 if (dpp_auth_derive_l_initiator(auth) < 0)
3644 goto fail;
3645 }
3646
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003647 r_capab = dpp_get_attr(unwrapped, unwrapped_len,
3648 DPP_ATTR_R_CAPABILITIES,
3649 &r_capab_len);
3650 if (!r_capab || r_capab_len < 1) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003651 dpp_auth_fail(auth, "Missing or invalid R-capabilities");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003652 goto fail;
3653 }
3654 auth->r_capab = r_capab[0];
3655 wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003656 role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
3657 if ((auth->allowed_roles ==
3658 (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) &&
3659 (role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) {
3660 /* Peer selected its role, so move from "either role" to the
3661 * role that is compatible with peer's selection. */
3662 auth->configurator = role == DPP_CAPAB_ENROLLEE;
3663 wpa_printf(MSG_DEBUG, "DPP: Acting as %s",
3664 auth->configurator ? "Configurator" : "Enrollee");
3665 } else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
3666 (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003667 wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection");
Roshan Pius3a1667e2018-07-03 15:17:14 -07003668 wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
3669 "Unexpected role in R-capabilities 0x%02x",
3670 role);
3671 if (role != DPP_CAPAB_ENROLLEE &&
3672 role != DPP_CAPAB_CONFIGURATOR)
3673 goto fail;
3674 bin_clear_free(unwrapped, unwrapped_len);
3675 auth->remove_on_tx_status = 1;
3676 return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003677 }
3678
3679 wrapped2 = dpp_get_attr(unwrapped, unwrapped_len,
3680 DPP_ATTR_WRAPPED_DATA, &wrapped2_len);
3681 if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003682 dpp_auth_fail(auth,
3683 "Missing or invalid Secondary Wrapped Data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003684 goto fail;
3685 }
3686
3687 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
3688 wrapped2, wrapped2_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003689
3690 if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
3691 goto fail;
3692
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003693 unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE;
3694 unwrapped2 = os_malloc(unwrapped2_len);
3695 if (!unwrapped2)
3696 goto fail;
3697 if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
3698 wrapped2, wrapped2_len,
3699 0, NULL, NULL, unwrapped2) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003700 dpp_auth_fail(auth, "AES-SIV decryption failed");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003701 goto fail;
3702 }
3703 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
3704 unwrapped2, unwrapped2_len);
3705
3706 if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003707 dpp_auth_fail(auth,
3708 "Invalid attribute in secondary unwrapped data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003709 goto fail;
3710 }
3711
3712 r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG,
3713 &r_auth_len);
3714 if (!r_auth || r_auth_len != auth->curve->hash_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003715 dpp_auth_fail(auth,
3716 "Missing or invalid Responder Authenticating Tag");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003717 goto fail;
3718 }
3719 wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag",
3720 r_auth, r_auth_len);
3721 /* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
3722 if (dpp_gen_r_auth(auth, r_auth2) < 0)
3723 goto fail;
3724 wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag",
3725 r_auth2, r_auth_len);
3726 if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003727 dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag");
3728 bin_clear_free(unwrapped, unwrapped_len);
3729 bin_clear_free(unwrapped2, unwrapped2_len);
3730 auth->remove_on_tx_status = 1;
3731 return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003732 }
3733
3734 bin_clear_free(unwrapped, unwrapped_len);
3735 bin_clear_free(unwrapped2, unwrapped2_len);
3736
Roshan Pius3a1667e2018-07-03 15:17:14 -07003737#ifdef CONFIG_TESTING_OPTIONS
3738 if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) {
3739 wpa_printf(MSG_INFO,
3740 "DPP: TESTING - Authentication Response in place of Confirm");
3741 if (dpp_auth_build_resp_ok(auth) < 0)
3742 return NULL;
3743 return wpabuf_dup(auth->resp_msg);
3744 }
3745#endif /* CONFIG_TESTING_OPTIONS */
3746
3747 return dpp_auth_build_conf(auth, DPP_STATUS_OK);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003748
3749fail:
3750 bin_clear_free(unwrapped, unwrapped_len);
3751 bin_clear_free(unwrapped2, unwrapped2_len);
3752 EVP_PKEY_free(pr);
3753 EVP_PKEY_CTX_free(ctx);
3754 return NULL;
3755}
3756
3757
Roshan Pius3a1667e2018-07-03 15:17:14 -07003758static int dpp_auth_conf_rx_failure(struct dpp_authentication *auth,
3759 const u8 *hdr,
3760 const u8 *attr_start, size_t attr_len,
3761 const u8 *wrapped_data,
3762 u16 wrapped_data_len,
3763 enum dpp_status_error status)
3764{
3765 const u8 *addr[2];
3766 size_t len[2];
3767 u8 *unwrapped = NULL;
3768 size_t unwrapped_len = 0;
3769 const u8 *r_nonce;
3770 u16 r_nonce_len;
3771
3772 /* Authentication Confirm failure cases are expected to include
3773 * {R-nonce}k2 in the Wrapped Data attribute. */
3774
3775 addr[0] = hdr;
3776 len[0] = DPP_HDR_LEN;
3777 addr[1] = attr_start;
3778 len[1] = attr_len;
3779 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
3780 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
3781 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
3782 wrapped_data, wrapped_data_len);
3783 unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
3784 unwrapped = os_malloc(unwrapped_len);
3785 if (!unwrapped) {
3786 dpp_auth_fail(auth, "Authentication failed");
3787 goto fail;
3788 }
3789 if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
3790 wrapped_data, wrapped_data_len,
3791 2, addr, len, unwrapped) < 0) {
3792 dpp_auth_fail(auth, "AES-SIV decryption failed");
3793 goto fail;
3794 }
3795 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
3796 unwrapped, unwrapped_len);
3797
3798 if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
3799 dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
3800 goto fail;
3801 }
3802
3803 r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
3804 &r_nonce_len);
3805 if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
3806 dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
3807 goto fail;
3808 }
3809 if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) {
3810 wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce",
3811 r_nonce, r_nonce_len);
3812 wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce",
3813 auth->r_nonce, r_nonce_len);
3814 dpp_auth_fail(auth, "R-nonce mismatch");
3815 goto fail;
3816 }
3817
3818 if (status == DPP_STATUS_NOT_COMPATIBLE)
3819 dpp_auth_fail(auth, "Peer reported incompatible R-capab role");
3820 else if (status == DPP_STATUS_AUTH_FAILURE)
3821 dpp_auth_fail(auth, "Peer reported authentication failure)");
3822
3823fail:
3824 bin_clear_free(unwrapped, unwrapped_len);
3825 return -1;
3826}
3827
3828
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003829int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
3830 const u8 *attr_start, size_t attr_len)
3831{
3832 const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth;
3833 u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
3834 i_auth_len;
3835 const u8 *addr[2];
3836 size_t len[2];
3837 u8 *unwrapped = NULL;
3838 size_t unwrapped_len = 0;
3839 u8 i_auth2[DPP_MAX_HASH_LEN];
3840
Roshan Pius3a1667e2018-07-03 15:17:14 -07003841#ifdef CONFIG_TESTING_OPTIONS
3842 if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
3843 wpa_printf(MSG_INFO,
3844 "DPP: TESTING - stop at Authentication Confirm");
3845 return -1;
3846 }
3847#endif /* CONFIG_TESTING_OPTIONS */
3848
3849 if (auth->initiator) {
3850 dpp_auth_fail(auth, "Unexpected Authentication Confirm");
3851 return -1;
3852 }
3853
3854 auth->waiting_auth_conf = 0;
3855
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003856 wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
3857 &wrapped_data_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003858 if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
3859 dpp_auth_fail(auth,
3860 "Missing or invalid required Wrapped Data attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003861 return -1;
3862 }
3863 wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
3864 wrapped_data, wrapped_data_len);
3865
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003866 attr_len = wrapped_data - 4 - attr_start;
3867
3868 r_bootstrap = dpp_get_attr(attr_start, attr_len,
3869 DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
3870 &r_bootstrap_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003871 if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
3872 dpp_auth_fail(auth,
3873 "Missing or invalid required Responder Bootstrapping Key Hash attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003874 return -1;
3875 }
3876 wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
3877 r_bootstrap, r_bootstrap_len);
3878 if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash,
3879 SHA256_MAC_LEN) != 0) {
3880 wpa_hexdump(MSG_DEBUG,
3881 "DPP: Expected Responder Bootstrapping Key Hash",
3882 auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003883 dpp_auth_fail(auth,
3884 "Responder Bootstrapping Key Hash mismatch");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003885 return -1;
3886 }
3887
3888 i_bootstrap = dpp_get_attr(attr_start, attr_len,
3889 DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
3890 &i_bootstrap_len);
3891 if (i_bootstrap) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003892 if (i_bootstrap_len != SHA256_MAC_LEN) {
3893 dpp_auth_fail(auth,
3894 "Invalid Initiator Bootstrapping Key Hash attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003895 return -1;
3896 }
3897 wpa_hexdump(MSG_MSGDUMP,
3898 "DPP: Initiator Bootstrapping Key Hash",
3899 i_bootstrap, i_bootstrap_len);
3900 if (!auth->peer_bi ||
3901 os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash,
3902 SHA256_MAC_LEN) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003903 dpp_auth_fail(auth,
3904 "Initiator Bootstrapping Key Hash mismatch");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003905 return -1;
3906 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07003907 } else if (auth->own_bi && auth->peer_bi) {
3908 /* Mutual authentication and peer did not include its
3909 * Bootstrapping Key Hash attribute. */
3910 dpp_auth_fail(auth,
3911 "Missing Initiator Bootstrapping Key Hash attribute");
3912 return -1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003913 }
3914
3915 status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
3916 &status_len);
3917 if (!status || status_len < 1) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003918 dpp_auth_fail(auth,
3919 "Missing or invalid required DPP Status attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003920 return -1;
3921 }
3922 wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
Roshan Pius3a1667e2018-07-03 15:17:14 -07003923 if (status[0] == DPP_STATUS_NOT_COMPATIBLE ||
3924 status[0] == DPP_STATUS_AUTH_FAILURE)
3925 return dpp_auth_conf_rx_failure(auth, hdr, attr_start,
3926 attr_len, wrapped_data,
3927 wrapped_data_len, status[0]);
3928
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003929 if (status[0] != DPP_STATUS_OK) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003930 dpp_auth_fail(auth, "Authentication failed");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003931 return -1;
3932 }
3933
3934 addr[0] = hdr;
3935 len[0] = DPP_HDR_LEN;
3936 addr[1] = attr_start;
3937 len[1] = attr_len;
3938 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
3939 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
3940 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
3941 wrapped_data, wrapped_data_len);
3942 unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
3943 unwrapped = os_malloc(unwrapped_len);
3944 if (!unwrapped)
3945 return -1;
3946 if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
3947 wrapped_data, wrapped_data_len,
3948 2, addr, len, unwrapped) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003949 dpp_auth_fail(auth, "AES-SIV decryption failed");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003950 goto fail;
3951 }
3952 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
3953 unwrapped, unwrapped_len);
3954
3955 if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003956 dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003957 goto fail;
3958 }
3959
3960 i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
3961 &i_auth_len);
3962 if (!i_auth || i_auth_len != auth->curve->hash_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003963 dpp_auth_fail(auth,
3964 "Missing or invalid Initiator Authenticating Tag");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003965 goto fail;
3966 }
3967 wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag",
3968 i_auth, i_auth_len);
3969 /* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
3970 if (dpp_gen_i_auth(auth, i_auth2) < 0)
3971 goto fail;
3972 wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag",
3973 i_auth2, i_auth_len);
3974 if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07003975 dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003976 goto fail;
3977 }
3978
3979 bin_clear_free(unwrapped, unwrapped_len);
3980 dpp_auth_success(auth);
3981 return 0;
3982fail:
3983 bin_clear_free(unwrapped, unwrapped_len);
3984 return -1;
3985}
3986
3987
3988void dpp_configuration_free(struct dpp_configuration *conf)
3989{
3990 if (!conf)
3991 return;
3992 str_clear_free(conf->passphrase);
Hai Shalomce48b4a2018-09-05 11:41:35 -07003993 os_free(conf->group_id);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07003994 bin_clear_free(conf, sizeof(*conf));
3995}
3996
3997
3998void dpp_auth_deinit(struct dpp_authentication *auth)
3999{
4000 if (!auth)
4001 return;
4002 dpp_configuration_free(auth->conf_ap);
4003 dpp_configuration_free(auth->conf_sta);
4004 EVP_PKEY_free(auth->own_protocol_key);
4005 EVP_PKEY_free(auth->peer_protocol_key);
4006 wpabuf_free(auth->req_msg);
4007 wpabuf_free(auth->resp_msg);
4008 wpabuf_free(auth->conf_req);
4009 os_free(auth->connector);
4010 wpabuf_free(auth->net_access_key);
4011 wpabuf_free(auth->c_sign_key);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004012 dpp_bootstrap_info_free(auth->tmp_own_bi);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004013#ifdef CONFIG_TESTING_OPTIONS
4014 os_free(auth->config_obj_override);
4015 os_free(auth->discovery_override);
4016 os_free(auth->groups_override);
4017#endif /* CONFIG_TESTING_OPTIONS */
4018 bin_clear_free(auth, sizeof(*auth));
4019}
4020
4021
4022static struct wpabuf *
4023dpp_build_conf_start(struct dpp_authentication *auth,
4024 struct dpp_configuration *conf, size_t tailroom)
4025{
4026 struct wpabuf *buf;
4027 char ssid[6 * sizeof(conf->ssid) + 1];
4028
4029#ifdef CONFIG_TESTING_OPTIONS
4030 if (auth->discovery_override)
4031 tailroom += os_strlen(auth->discovery_override);
4032#endif /* CONFIG_TESTING_OPTIONS */
4033
4034 buf = wpabuf_alloc(200 + tailroom);
4035 if (!buf)
4036 return NULL;
4037 wpabuf_put_str(buf, "{\"wi-fi_tech\":\"infra\",\"discovery\":");
4038#ifdef CONFIG_TESTING_OPTIONS
4039 if (auth->discovery_override) {
4040 wpa_printf(MSG_DEBUG, "DPP: TESTING - discovery override: '%s'",
4041 auth->discovery_override);
4042 wpabuf_put_str(buf, auth->discovery_override);
4043 wpabuf_put_u8(buf, ',');
4044 return buf;
4045 }
4046#endif /* CONFIG_TESTING_OPTIONS */
4047 wpabuf_put_str(buf, "{\"ssid\":\"");
4048 json_escape_string(ssid, sizeof(ssid),
4049 (const char *) conf->ssid, conf->ssid_len);
4050 wpabuf_put_str(buf, ssid);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004051 wpabuf_put_str(buf, "\"},");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004052
4053 return buf;
4054}
4055
4056
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004057static int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
4058 const char *kid, const struct dpp_curve_params *curve)
4059{
4060 struct wpabuf *pub;
4061 const u8 *pos;
4062 char *x = NULL, *y = NULL;
4063 int ret = -1;
4064
4065 pub = dpp_get_pubkey_point(key, 0);
4066 if (!pub)
4067 goto fail;
4068 pos = wpabuf_head(pub);
4069 x = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0);
4070 pos += curve->prime_len;
4071 y = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0);
4072 if (!x || !y)
4073 goto fail;
4074
4075 wpabuf_put_str(buf, "\"");
4076 wpabuf_put_str(buf, name);
4077 wpabuf_put_str(buf, "\":{\"kty\":\"EC\",\"crv\":\"");
4078 wpabuf_put_str(buf, curve->jwk_crv);
4079 wpabuf_put_str(buf, "\",\"x\":\"");
4080 wpabuf_put_str(buf, x);
4081 wpabuf_put_str(buf, "\",\"y\":\"");
4082 wpabuf_put_str(buf, y);
4083 if (kid) {
4084 wpabuf_put_str(buf, "\",\"kid\":\"");
4085 wpabuf_put_str(buf, kid);
4086 }
4087 wpabuf_put_str(buf, "\"}");
4088 ret = 0;
4089fail:
4090 wpabuf_free(pub);
4091 os_free(x);
4092 os_free(y);
4093 return ret;
4094}
4095
4096
4097static struct wpabuf *
4098dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap,
4099 struct dpp_configuration *conf)
4100{
4101 struct wpabuf *buf = NULL;
4102 char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL;
4103 size_t tailroom;
4104 const struct dpp_curve_params *curve;
4105 char jws_prot_hdr[100];
4106 size_t signed1_len, signed2_len, signed3_len;
4107 struct wpabuf *dppcon = NULL;
4108 unsigned char *signature = NULL;
4109 const unsigned char *p;
4110 size_t signature_len;
4111 EVP_MD_CTX *md_ctx = NULL;
4112 ECDSA_SIG *sig = NULL;
4113 char *dot = ".";
4114 const EVP_MD *sign_md;
4115 const BIGNUM *r, *s;
4116 size_t extra_len = 1000;
4117
4118 if (!auth->conf) {
4119 wpa_printf(MSG_INFO,
4120 "DPP: No configurator specified - cannot generate DPP config object");
4121 goto fail;
4122 }
4123 curve = auth->conf->curve;
4124 if (curve->hash_len == SHA256_MAC_LEN) {
4125 sign_md = EVP_sha256();
4126 } else if (curve->hash_len == SHA384_MAC_LEN) {
4127 sign_md = EVP_sha384();
4128 } else if (curve->hash_len == SHA512_MAC_LEN) {
4129 sign_md = EVP_sha512();
4130 } else {
4131 wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
4132 goto fail;
4133 }
4134
4135#ifdef CONFIG_TESTING_OPTIONS
4136 if (auth->groups_override)
4137 extra_len += os_strlen(auth->groups_override);
4138#endif /* CONFIG_TESTING_OPTIONS */
4139
Hai Shalomce48b4a2018-09-05 11:41:35 -07004140 if (conf->group_id)
4141 extra_len += os_strlen(conf->group_id);
4142
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004143 /* Connector (JSON dppCon object) */
4144 dppcon = wpabuf_alloc(extra_len + 2 * auth->curve->prime_len * 4 / 3);
4145 if (!dppcon)
4146 goto fail;
4147#ifdef CONFIG_TESTING_OPTIONS
4148 if (auth->groups_override) {
4149 wpabuf_put_u8(dppcon, '{');
4150 if (auth->groups_override) {
4151 wpa_printf(MSG_DEBUG,
4152 "DPP: TESTING - groups override: '%s'",
4153 auth->groups_override);
4154 wpabuf_put_str(dppcon, "\"groups\":");
4155 wpabuf_put_str(dppcon, auth->groups_override);
4156 wpabuf_put_u8(dppcon, ',');
4157 }
4158 goto skip_groups;
4159 }
4160#endif /* CONFIG_TESTING_OPTIONS */
Hai Shalomce48b4a2018-09-05 11:41:35 -07004161 wpabuf_printf(dppcon, "{\"groups\":[{\"groupId\":\"%s\",",
4162 conf->group_id ? conf->group_id : "*");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004163 wpabuf_printf(dppcon, "\"netRole\":\"%s\"}],", ap ? "ap" : "sta");
4164#ifdef CONFIG_TESTING_OPTIONS
4165skip_groups:
4166#endif /* CONFIG_TESTING_OPTIONS */
4167 if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL,
4168 auth->curve) < 0) {
4169 wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
4170 goto fail;
4171 }
4172 if (conf->netaccesskey_expiry) {
4173 struct os_tm tm;
4174
4175 if (os_gmtime(conf->netaccesskey_expiry, &tm) < 0) {
4176 wpa_printf(MSG_DEBUG,
4177 "DPP: Failed to generate expiry string");
4178 goto fail;
4179 }
4180 wpabuf_printf(dppcon,
4181 ",\"expiry\":\"%04u-%02u-%02uT%02u:%02u:%02uZ\"",
4182 tm.year, tm.month, tm.day,
4183 tm.hour, tm.min, tm.sec);
4184 }
4185 wpabuf_put_u8(dppcon, '}');
4186 wpa_printf(MSG_DEBUG, "DPP: dppCon: %s",
4187 (const char *) wpabuf_head(dppcon));
4188
4189 os_snprintf(jws_prot_hdr, sizeof(jws_prot_hdr),
4190 "{\"typ\":\"dppCon\",\"kid\":\"%s\",\"alg\":\"%s\"}",
4191 auth->conf->kid, curve->jws_alg);
4192 signed1 = (char *) base64_url_encode((unsigned char *) jws_prot_hdr,
4193 os_strlen(jws_prot_hdr),
4194 &signed1_len, 0);
4195 signed2 = (char *) base64_url_encode(wpabuf_head(dppcon),
4196 wpabuf_len(dppcon),
4197 &signed2_len, 0);
4198 if (!signed1 || !signed2)
4199 goto fail;
4200
4201 md_ctx = EVP_MD_CTX_create();
4202 if (!md_ctx)
4203 goto fail;
4204
4205 ERR_clear_error();
4206 if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL,
4207 auth->conf->csign) != 1) {
4208 wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s",
4209 ERR_error_string(ERR_get_error(), NULL));
4210 goto fail;
4211 }
4212 if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 ||
4213 EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 ||
4214 EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) {
4215 wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s",
4216 ERR_error_string(ERR_get_error(), NULL));
4217 goto fail;
4218 }
4219 if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) {
4220 wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
4221 ERR_error_string(ERR_get_error(), NULL));
4222 goto fail;
4223 }
4224 signature = os_malloc(signature_len);
4225 if (!signature)
4226 goto fail;
4227 if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) {
4228 wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
4229 ERR_error_string(ERR_get_error(), NULL));
4230 goto fail;
4231 }
4232 wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)",
4233 signature, signature_len);
4234 /* Convert to raw coordinates r,s */
4235 p = signature;
4236 sig = d2i_ECDSA_SIG(NULL, &p, signature_len);
4237 if (!sig)
4238 goto fail;
4239 ECDSA_SIG_get0(sig, &r, &s);
4240 if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 ||
4241 dpp_bn2bin_pad(s, signature + curve->prime_len,
4242 curve->prime_len) < 0)
4243 goto fail;
4244 signature_len = 2 * curve->prime_len;
4245 wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)",
4246 signature, signature_len);
4247 signed3 = (char *) base64_url_encode(signature, signature_len,
4248 &signed3_len, 0);
4249 if (!signed3)
4250 goto fail;
4251
4252 tailroom = 1000;
4253 tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid);
4254 tailroom += signed1_len + signed2_len + signed3_len;
4255 buf = dpp_build_conf_start(auth, conf, tailroom);
4256 if (!buf)
Roshan Pius3a1667e2018-07-03 15:17:14 -07004257 goto fail;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004258
4259 wpabuf_put_str(buf, "\"cred\":{\"akm\":\"dpp\",\"signedConnector\":\"");
4260 wpabuf_put_str(buf, signed1);
4261 wpabuf_put_u8(buf, '.');
4262 wpabuf_put_str(buf, signed2);
4263 wpabuf_put_u8(buf, '.');
4264 wpabuf_put_str(buf, signed3);
4265 wpabuf_put_str(buf, "\",");
4266 if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid,
4267 curve) < 0) {
4268 wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK");
4269 goto fail;
4270 }
4271
4272 wpabuf_put_str(buf, "}}");
4273
4274 wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object",
4275 wpabuf_head(buf), wpabuf_len(buf));
4276
4277out:
4278 EVP_MD_CTX_destroy(md_ctx);
4279 ECDSA_SIG_free(sig);
4280 os_free(signed1);
4281 os_free(signed2);
4282 os_free(signed3);
4283 os_free(signature);
4284 wpabuf_free(dppcon);
4285 return buf;
4286fail:
4287 wpa_printf(MSG_DEBUG, "DPP: Failed to build configuration object");
4288 wpabuf_free(buf);
4289 buf = NULL;
4290 goto out;
4291}
4292
4293
4294static struct wpabuf *
4295dpp_build_conf_obj_legacy(struct dpp_authentication *auth, int ap,
4296 struct dpp_configuration *conf)
4297{
4298 struct wpabuf *buf;
4299
4300 buf = dpp_build_conf_start(auth, conf, 1000);
4301 if (!buf)
4302 return NULL;
4303
Roshan Pius3a1667e2018-07-03 15:17:14 -07004304 wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(conf->akm));
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004305 if (conf->passphrase) {
4306 char pass[63 * 6 + 1];
4307
4308 if (os_strlen(conf->passphrase) > 63) {
4309 wpabuf_free(buf);
4310 return NULL;
4311 }
4312
4313 json_escape_string(pass, sizeof(pass), conf->passphrase,
4314 os_strlen(conf->passphrase));
4315 wpabuf_put_str(buf, "\"pass\":\"");
4316 wpabuf_put_str(buf, pass);
4317 wpabuf_put_str(buf, "\"");
4318 } else {
4319 char psk[2 * sizeof(conf->psk) + 1];
4320
4321 wpa_snprintf_hex(psk, sizeof(psk),
4322 conf->psk, sizeof(conf->psk));
4323 wpabuf_put_str(buf, "\"psk_hex\":\"");
4324 wpabuf_put_str(buf, psk);
4325 wpabuf_put_str(buf, "\"");
4326 }
4327 wpabuf_put_str(buf, "}}");
4328
4329 wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)",
4330 wpabuf_head(buf), wpabuf_len(buf));
4331
4332 return buf;
4333}
4334
4335
4336static struct wpabuf *
4337dpp_build_conf_obj(struct dpp_authentication *auth, int ap)
4338{
4339 struct dpp_configuration *conf;
4340
4341#ifdef CONFIG_TESTING_OPTIONS
4342 if (auth->config_obj_override) {
4343 wpa_printf(MSG_DEBUG, "DPP: Testing - Config Object override");
4344 return wpabuf_alloc_copy(auth->config_obj_override,
4345 os_strlen(auth->config_obj_override));
4346 }
4347#endif /* CONFIG_TESTING_OPTIONS */
4348
4349 conf = ap ? auth->conf_ap : auth->conf_sta;
4350 if (!conf) {
4351 wpa_printf(MSG_DEBUG,
4352 "DPP: No configuration available for Enrollee(%s) - reject configuration request",
4353 ap ? "ap" : "sta");
4354 return NULL;
4355 }
4356
Roshan Pius3a1667e2018-07-03 15:17:14 -07004357 if (conf->akm == DPP_AKM_DPP)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004358 return dpp_build_conf_obj_dpp(auth, ap, conf);
4359 return dpp_build_conf_obj_legacy(auth, ap, conf);
4360}
4361
4362
4363static struct wpabuf *
4364dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
4365 u16 e_nonce_len, int ap)
4366{
4367 struct wpabuf *conf;
Roshan Pius3a1667e2018-07-03 15:17:14 -07004368 size_t clear_len, attr_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004369 struct wpabuf *clear = NULL, *msg = NULL;
4370 u8 *wrapped;
4371 const u8 *addr[1];
4372 size_t len[1];
4373 enum dpp_status_error status;
4374
4375 conf = dpp_build_conf_obj(auth, ap);
4376 if (conf) {
4377 wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
4378 wpabuf_head(conf), wpabuf_len(conf));
4379 }
4380 status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE;
4381
4382 /* { E-nonce, configurationObject}ke */
4383 clear_len = 4 + e_nonce_len;
4384 if (conf)
4385 clear_len += 4 + wpabuf_len(conf);
4386 clear = wpabuf_alloc(clear_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004387 attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE;
4388#ifdef CONFIG_TESTING_OPTIONS
4389 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP)
4390 attr_len += 5;
4391#endif /* CONFIG_TESTING_OPTIONS */
4392 msg = wpabuf_alloc(attr_len);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004393 if (!clear || !msg)
4394 goto fail;
4395
Roshan Pius3a1667e2018-07-03 15:17:14 -07004396#ifdef CONFIG_TESTING_OPTIONS
4397 if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_RESP) {
4398 wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce");
4399 goto skip_e_nonce;
4400 }
4401 if (dpp_test == DPP_TEST_E_NONCE_MISMATCH_CONF_RESP) {
4402 wpa_printf(MSG_INFO, "DPP: TESTING - E-nonce mismatch");
4403 wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
4404 wpabuf_put_le16(clear, e_nonce_len);
4405 wpabuf_put_data(clear, e_nonce, e_nonce_len - 1);
4406 wpabuf_put_u8(clear, e_nonce[e_nonce_len - 1] ^ 0x01);
4407 goto skip_e_nonce;
4408 }
4409 if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_RESP) {
4410 wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
4411 goto skip_wrapped_data;
4412 }
4413#endif /* CONFIG_TESTING_OPTIONS */
4414
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004415 /* E-nonce */
4416 wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
4417 wpabuf_put_le16(clear, e_nonce_len);
4418 wpabuf_put_data(clear, e_nonce, e_nonce_len);
4419
Roshan Pius3a1667e2018-07-03 15:17:14 -07004420#ifdef CONFIG_TESTING_OPTIONS
4421skip_e_nonce:
4422 if (dpp_test == DPP_TEST_NO_CONFIG_OBJ_CONF_RESP) {
4423 wpa_printf(MSG_INFO, "DPP: TESTING - Config Object");
4424 goto skip_config_obj;
4425 }
4426#endif /* CONFIG_TESTING_OPTIONS */
4427
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004428 if (conf) {
4429 wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ);
4430 wpabuf_put_le16(clear, wpabuf_len(conf));
4431 wpabuf_put_buf(clear, conf);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004432 }
4433
Roshan Pius3a1667e2018-07-03 15:17:14 -07004434#ifdef CONFIG_TESTING_OPTIONS
4435skip_config_obj:
4436 if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) {
4437 wpa_printf(MSG_INFO, "DPP: TESTING - Status");
4438 goto skip_status;
4439 }
4440 if (dpp_test == DPP_TEST_INVALID_STATUS_CONF_RESP) {
4441 wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
4442 status = 255;
4443 }
4444#endif /* CONFIG_TESTING_OPTIONS */
4445
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004446 /* DPP Status */
Roshan Pius3a1667e2018-07-03 15:17:14 -07004447 dpp_build_attr_status(msg, status);
4448
4449#ifdef CONFIG_TESTING_OPTIONS
4450skip_status:
4451#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004452
4453 addr[0] = wpabuf_head(msg);
4454 len[0] = wpabuf_len(msg);
4455 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
4456
4457 wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
4458 wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
4459 wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
4460
4461 wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
4462 if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
4463 wpabuf_head(clear), wpabuf_len(clear),
4464 1, addr, len, wrapped) < 0)
4465 goto fail;
4466 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
4467 wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004468
4469#ifdef CONFIG_TESTING_OPTIONS
4470 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) {
4471 wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
4472 dpp_build_attr_status(msg, DPP_STATUS_OK);
4473 }
4474skip_wrapped_data:
4475#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004476
4477 wpa_hexdump_buf(MSG_DEBUG,
4478 "DPP: Configuration Response attributes", msg);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004479out:
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004480 wpabuf_free(conf);
4481 wpabuf_free(clear);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004482
4483 return msg;
4484fail:
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004485 wpabuf_free(msg);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004486 msg = NULL;
4487 goto out;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004488}
4489
4490
4491struct wpabuf *
4492dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
4493 size_t attr_len)
4494{
4495 const u8 *wrapped_data, *e_nonce, *config_attr;
4496 u16 wrapped_data_len, e_nonce_len, config_attr_len;
4497 u8 *unwrapped = NULL;
4498 size_t unwrapped_len = 0;
4499 struct wpabuf *resp = NULL;
4500 struct json_token *root = NULL, *token;
4501 int ap;
4502
Roshan Pius3a1667e2018-07-03 15:17:14 -07004503#ifdef CONFIG_TESTING_OPTIONS
4504 if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) {
4505 wpa_printf(MSG_INFO,
4506 "DPP: TESTING - stop at Config Request");
4507 return NULL;
4508 }
4509#endif /* CONFIG_TESTING_OPTIONS */
4510
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004511 if (dpp_check_attrs(attr_start, attr_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004512 dpp_auth_fail(auth, "Invalid attribute in config request");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004513 return NULL;
4514 }
4515
4516 wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
4517 &wrapped_data_len);
4518 if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004519 dpp_auth_fail(auth,
4520 "Missing or invalid required Wrapped Data attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004521 return NULL;
4522 }
4523
4524 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
4525 wrapped_data, wrapped_data_len);
4526 unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
4527 unwrapped = os_malloc(unwrapped_len);
4528 if (!unwrapped)
4529 return NULL;
4530 if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
4531 wrapped_data, wrapped_data_len,
4532 0, NULL, NULL, unwrapped) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004533 dpp_auth_fail(auth, "AES-SIV decryption failed");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004534 goto fail;
4535 }
4536 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
4537 unwrapped, unwrapped_len);
4538
4539 if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004540 dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004541 goto fail;
4542 }
4543
4544 e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
4545 DPP_ATTR_ENROLLEE_NONCE,
4546 &e_nonce_len);
4547 if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004548 dpp_auth_fail(auth,
4549 "Missing or invalid Enrollee Nonce attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004550 goto fail;
4551 }
4552 wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
4553
4554 config_attr = dpp_get_attr(unwrapped, unwrapped_len,
4555 DPP_ATTR_CONFIG_ATTR_OBJ,
4556 &config_attr_len);
4557 if (!config_attr) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004558 dpp_auth_fail(auth,
4559 "Missing or invalid Config Attributes attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004560 goto fail;
4561 }
4562 wpa_hexdump_ascii(MSG_DEBUG, "DPP: Config Attributes",
4563 config_attr, config_attr_len);
4564
4565 root = json_parse((const char *) config_attr, config_attr_len);
4566 if (!root) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004567 dpp_auth_fail(auth, "Could not parse Config Attributes");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004568 goto fail;
4569 }
4570
4571 token = json_get_member(root, "name");
4572 if (!token || token->type != JSON_STRING) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004573 dpp_auth_fail(auth, "No Config Attributes - name");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004574 goto fail;
4575 }
4576 wpa_printf(MSG_DEBUG, "DPP: Enrollee name = '%s'", token->string);
4577
4578 token = json_get_member(root, "wi-fi_tech");
4579 if (!token || token->type != JSON_STRING) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004580 dpp_auth_fail(auth, "No Config Attributes - wi-fi_tech");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004581 goto fail;
4582 }
4583 wpa_printf(MSG_DEBUG, "DPP: wi-fi_tech = '%s'", token->string);
4584 if (os_strcmp(token->string, "infra") != 0) {
4585 wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech '%s'",
4586 token->string);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004587 dpp_auth_fail(auth, "Unsupported wi-fi_tech");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004588 goto fail;
4589 }
4590
4591 token = json_get_member(root, "netRole");
4592 if (!token || token->type != JSON_STRING) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004593 dpp_auth_fail(auth, "No Config Attributes - netRole");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004594 goto fail;
4595 }
4596 wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string);
4597 if (os_strcmp(token->string, "sta") == 0) {
4598 ap = 0;
4599 } else if (os_strcmp(token->string, "ap") == 0) {
4600 ap = 1;
4601 } else {
4602 wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'",
4603 token->string);
Roshan Pius3a1667e2018-07-03 15:17:14 -07004604 dpp_auth_fail(auth, "Unsupported netRole");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004605 goto fail;
4606 }
4607
4608 resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, ap);
4609
4610fail:
4611 json_free(root);
4612 os_free(unwrapped);
4613 return resp;
4614}
4615
4616
4617static struct wpabuf *
4618dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve,
4619 const u8 *prot_hdr, u16 prot_hdr_len,
4620 const EVP_MD **ret_md)
4621{
4622 struct json_token *root, *token;
4623 struct wpabuf *kid = NULL;
4624
4625 root = json_parse((const char *) prot_hdr, prot_hdr_len);
4626 if (!root) {
4627 wpa_printf(MSG_DEBUG,
4628 "DPP: JSON parsing failed for JWS Protected Header");
4629 goto fail;
4630 }
4631
4632 if (root->type != JSON_OBJECT) {
4633 wpa_printf(MSG_DEBUG,
4634 "DPP: JWS Protected Header root is not an object");
4635 goto fail;
4636 }
4637
4638 token = json_get_member(root, "typ");
4639 if (!token || token->type != JSON_STRING) {
4640 wpa_printf(MSG_DEBUG, "DPP: No typ string value found");
4641 goto fail;
4642 }
4643 wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s",
4644 token->string);
4645 if (os_strcmp(token->string, "dppCon") != 0) {
4646 wpa_printf(MSG_DEBUG,
4647 "DPP: Unsupported JWS Protected Header typ=%s",
4648 token->string);
4649 goto fail;
4650 }
4651
4652 token = json_get_member(root, "alg");
4653 if (!token || token->type != JSON_STRING) {
4654 wpa_printf(MSG_DEBUG, "DPP: No alg string value found");
4655 goto fail;
4656 }
4657 wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s",
4658 token->string);
4659 if (os_strcmp(token->string, curve->jws_alg) != 0) {
4660 wpa_printf(MSG_DEBUG,
4661 "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)",
4662 token->string, curve->jws_alg);
4663 goto fail;
4664 }
4665 if (os_strcmp(token->string, "ES256") == 0 ||
4666 os_strcmp(token->string, "BS256") == 0)
4667 *ret_md = EVP_sha256();
4668 else if (os_strcmp(token->string, "ES384") == 0 ||
4669 os_strcmp(token->string, "BS384") == 0)
4670 *ret_md = EVP_sha384();
4671 else if (os_strcmp(token->string, "ES512") == 0 ||
4672 os_strcmp(token->string, "BS512") == 0)
4673 *ret_md = EVP_sha512();
4674 else
4675 *ret_md = NULL;
4676 if (!*ret_md) {
4677 wpa_printf(MSG_DEBUG,
4678 "DPP: Unsupported JWS Protected Header alg=%s",
4679 token->string);
4680 goto fail;
4681 }
4682
4683 kid = json_get_member_base64url(root, "kid");
4684 if (!kid) {
4685 wpa_printf(MSG_DEBUG, "DPP: No kid string value found");
4686 goto fail;
4687 }
4688 wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)",
4689 kid);
4690
4691fail:
4692 json_free(root);
4693 return kid;
4694}
4695
4696
4697static int dpp_parse_cred_legacy(struct dpp_authentication *auth,
4698 struct json_token *cred)
4699{
4700 struct json_token *pass, *psk_hex;
4701
4702 wpa_printf(MSG_DEBUG, "DPP: Legacy akm=psk credential");
4703
4704 pass = json_get_member(cred, "pass");
4705 psk_hex = json_get_member(cred, "psk_hex");
4706
4707 if (pass && pass->type == JSON_STRING) {
4708 size_t len = os_strlen(pass->string);
4709
4710 wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Legacy passphrase",
4711 pass->string, len);
4712 if (len < 8 || len > 63)
4713 return -1;
4714 os_strlcpy(auth->passphrase, pass->string,
4715 sizeof(auth->passphrase));
4716 } else if (psk_hex && psk_hex->type == JSON_STRING) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07004717 if (auth->akm == DPP_AKM_SAE) {
4718 wpa_printf(MSG_DEBUG,
4719 "DPP: Unexpected psk_hex with akm=sae");
4720 return -1;
4721 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004722 if (os_strlen(psk_hex->string) != PMK_LEN * 2 ||
4723 hexstr2bin(psk_hex->string, auth->psk, PMK_LEN) < 0) {
4724 wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding");
4725 return -1;
4726 }
4727 wpa_hexdump_key(MSG_DEBUG, "DPP: Legacy PSK",
4728 auth->psk, PMK_LEN);
4729 auth->psk_set = 1;
4730 } else {
4731 wpa_printf(MSG_DEBUG, "DPP: No pass or psk_hex strings found");
4732 return -1;
4733 }
4734
Roshan Pius3a1667e2018-07-03 15:17:14 -07004735 if ((auth->akm == DPP_AKM_SAE || auth->akm == DPP_AKM_PSK_SAE) &&
4736 !auth->passphrase[0]) {
4737 wpa_printf(MSG_DEBUG, "DPP: No pass for sae found");
4738 return -1;
4739 }
4740
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07004741 return 0;
4742}
4743
4744
4745static EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
4746 const struct dpp_curve_params **key_curve)
4747{
4748 struct json_token *token;
4749 const struct dpp_curve_params *curve;
4750 struct wpabuf *x = NULL, *y = NULL;
4751 EC_GROUP *group;
4752 EVP_PKEY *pkey = NULL;
4753
4754 token = json_get_member(jwk, "kty");
4755 if (!token || token->type != JSON_STRING) {
4756 wpa_printf(MSG_DEBUG, "DPP: No kty in JWK");
4757 goto fail;
4758 }
4759 if (os_strcmp(token->string, "EC") != 0) {
4760 wpa_printf(MSG_DEBUG, "DPP: Unexpected JWK kty '%s",
4761 token->string);
4762 goto fail;
4763 }
4764
4765 token = json_get_member(jwk, "crv");
4766 if (!token || token->type != JSON_STRING) {
4767 wpa_printf(MSG_DEBUG, "DPP: No crv in JWK");
4768 goto fail;
4769 }
4770 curve = dpp_get_curve_jwk_crv(token->string);
4771 if (!curve) {
4772 wpa_printf(MSG_DEBUG, "DPP: Unsupported JWK crv '%s'",
4773 token->string);
4774 goto fail;
4775 }
4776
4777 x = json_get_member_base64url(jwk, "x");
4778 if (!x) {
4779 wpa_printf(MSG_DEBUG, "DPP: No x in JWK");
4780 goto fail;
4781 }
4782 wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK x", x);
4783 if (wpabuf_len(x) != curve->prime_len) {
4784 wpa_printf(MSG_DEBUG,
4785 "DPP: Unexpected JWK x length %u (expected %u for curve %s)",
4786 (unsigned int) wpabuf_len(x),
4787 (unsigned int) curve->prime_len, curve->name);
4788 goto fail;
4789 }
4790
4791 y = json_get_member_base64url(jwk, "y");
4792 if (!y) {
4793 wpa_printf(MSG_DEBUG, "DPP: No y in JWK");
4794 goto fail;
4795 }
4796 wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK y", y);
4797 if (wpabuf_len(y) != curve->prime_len) {
4798 wpa_printf(MSG_DEBUG,
4799 "DPP: Unexpected JWK y length %u (expected %u for curve %s)",
4800 (unsigned int) wpabuf_len(y),
4801 (unsigned int) curve->prime_len, curve->name);
4802 goto fail;
4803 }
4804
4805 group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
4806 if (!group) {
4807 wpa_printf(MSG_DEBUG, "DPP: Could not prepare group for JWK");
4808 goto fail;
4809 }
4810
4811 pkey = dpp_set_pubkey_point_group(group, wpabuf_head(x), wpabuf_head(y),
4812 wpabuf_len(x));
4813 *key_curve = curve;
4814
4815fail:
4816 wpabuf_free(x);
4817 wpabuf_free(y);
4818
4819 return pkey;
4820}
4821
4822
4823int dpp_key_expired(const char *timestamp, os_time_t *expiry)
4824{
4825 struct os_time now;
4826 unsigned int year, month, day, hour, min, sec;
4827 os_time_t utime;
4828 const char *pos;
4829
4830 /* ISO 8601 date and time:
4831 * <date>T<time>
4832 * YYYY-MM-DDTHH:MM:SSZ
4833 * YYYY-MM-DDTHH:MM:SS+03:00
4834 */
4835 if (os_strlen(timestamp) < 19) {
4836 wpa_printf(MSG_DEBUG,
4837 "DPP: Too short timestamp - assume expired key");
4838 return 1;
4839 }
4840 if (sscanf(timestamp, "%04u-%02u-%02uT%02u:%02u:%02u",
4841 &year, &month, &day, &hour, &min, &sec) != 6) {
4842 wpa_printf(MSG_DEBUG,
4843 "DPP: Failed to parse expiration day - assume expired key");
4844 return 1;
4845 }
4846
4847 if (os_mktime(year, month, day, hour, min, sec, &utime) < 0) {
4848 wpa_printf(MSG_DEBUG,
4849 "DPP: Invalid date/time information - assume expired key");
4850 return 1;
4851 }
4852
4853 pos = timestamp + 19;
4854 if (*pos == 'Z' || *pos == '\0') {
4855 /* In UTC - no need to adjust */
4856 } else if (*pos == '-' || *pos == '+') {
4857 int items;
4858
4859 /* Adjust local time to UTC */
4860 items = sscanf(pos + 1, "%02u:%02u", &hour, &min);
4861 if (items < 1) {
4862 wpa_printf(MSG_DEBUG,
4863 "DPP: Invalid time zone designator (%s) - assume expired key",
4864 pos);
4865 return 1;
4866 }
4867 if (*pos == '-')
4868 utime += 3600 * hour;
4869 if (*pos == '+')
4870 utime -= 3600 * hour;
4871 if (items > 1) {
4872 if (*pos == '-')
4873 utime += 60 * min;
4874 if (*pos == '+')
4875 utime -= 60 * min;
4876 }
4877 } else {
4878 wpa_printf(MSG_DEBUG,
4879 "DPP: Invalid time zone designator (%s) - assume expired key",
4880 pos);
4881 return 1;
4882 }
4883 if (expiry)
4884 *expiry = utime;
4885
4886 if (os_get_time(&now) < 0) {
4887 wpa_printf(MSG_DEBUG,
4888 "DPP: Cannot get current time - assume expired key");
4889 return 1;
4890 }
4891
4892 if (now.sec > utime) {
4893 wpa_printf(MSG_DEBUG, "DPP: Key has expired (%lu < %lu)",
4894 utime, now.sec);
4895 return 1;
4896 }
4897
4898 return 0;
4899}
4900
4901
4902static int dpp_parse_connector(struct dpp_authentication *auth,
4903 const unsigned char *payload,
4904 u16 payload_len)
4905{
4906 struct json_token *root, *groups, *netkey, *token;
4907 int ret = -1;
4908 EVP_PKEY *key = NULL;
4909 const struct dpp_curve_params *curve;
4910 unsigned int rules = 0;
4911
4912 root = json_parse((const char *) payload, payload_len);
4913 if (!root) {
4914 wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed");
4915 goto fail;
4916 }
4917
4918 groups = json_get_member(root, "groups");
4919 if (!groups || groups->type != JSON_ARRAY) {
4920 wpa_printf(MSG_DEBUG, "DPP: No groups array found");
4921 goto skip_groups;
4922 }
4923 for (token = groups->child; token; token = token->sibling) {
4924 struct json_token *id, *role;
4925
4926 id = json_get_member(token, "groupId");
4927 if (!id || id->type != JSON_STRING) {
4928 wpa_printf(MSG_DEBUG, "DPP: Missing groupId string");
4929 goto fail;
4930 }
4931
4932 role = json_get_member(token, "netRole");
4933 if (!role || role->type != JSON_STRING) {
4934 wpa_printf(MSG_DEBUG, "DPP: Missing netRole string");
4935 goto fail;
4936 }
4937 wpa_printf(MSG_DEBUG,
4938 "DPP: connector group: groupId='%s' netRole='%s'",
4939 id->string, role->string);
4940 rules++;
4941 }
4942skip_groups:
4943
4944 if (!rules) {
4945 wpa_printf(MSG_DEBUG,
4946 "DPP: Connector includes no groups");
4947 goto fail;
4948 }
4949
4950 token = json_get_member(root, "expiry");
4951 if (!token || token->type != JSON_STRING) {
4952 wpa_printf(MSG_DEBUG,
4953 "DPP: No expiry string found - connector does not expire");
4954 } else {
4955 wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string);
4956 if (dpp_key_expired(token->string,
4957 &auth->net_access_key_expiry)) {
4958 wpa_printf(MSG_DEBUG,
4959 "DPP: Connector (netAccessKey) has expired");
4960 goto fail;
4961 }
4962 }
4963
4964 netkey = json_get_member(root, "netAccessKey");
4965 if (!netkey || netkey->type != JSON_OBJECT) {
4966 wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
4967 goto fail;
4968 }
4969
4970 key = dpp_parse_jwk(netkey, &curve);
4971 if (!key)
4972 goto fail;
4973 dpp_debug_print_key("DPP: Received netAccessKey", key);
4974
4975 if (EVP_PKEY_cmp(key, auth->own_protocol_key) != 1) {
4976 wpa_printf(MSG_DEBUG,
4977 "DPP: netAccessKey in connector does not match own protocol key");
4978#ifdef CONFIG_TESTING_OPTIONS
4979 if (auth->ignore_netaccesskey_mismatch) {
4980 wpa_printf(MSG_DEBUG,
4981 "DPP: TESTING - skip netAccessKey mismatch");
4982 } else {
4983 goto fail;
4984 }
4985#else /* CONFIG_TESTING_OPTIONS */
4986 goto fail;
4987#endif /* CONFIG_TESTING_OPTIONS */
4988 }
4989
4990 ret = 0;
4991fail:
4992 EVP_PKEY_free(key);
4993 json_free(root);
4994 return ret;
4995}
4996
4997
4998static int dpp_check_pubkey_match(EVP_PKEY *pub, struct wpabuf *r_hash)
4999{
5000 struct wpabuf *uncomp;
5001 int res;
5002 u8 hash[SHA256_MAC_LEN];
5003 const u8 *addr[1];
5004 size_t len[1];
5005
5006 if (wpabuf_len(r_hash) != SHA256_MAC_LEN)
5007 return -1;
5008 uncomp = dpp_get_pubkey_point(pub, 1);
5009 if (!uncomp)
5010 return -1;
5011 addr[0] = wpabuf_head(uncomp);
5012 len[0] = wpabuf_len(uncomp);
5013 wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed public key",
5014 addr[0], len[0]);
5015 res = sha256_vector(1, addr, len, hash);
5016 wpabuf_free(uncomp);
5017 if (res < 0)
5018 return -1;
5019 if (os_memcmp(hash, wpabuf_head(r_hash), SHA256_MAC_LEN) != 0) {
5020 wpa_printf(MSG_DEBUG,
5021 "DPP: Received hash value does not match calculated public key hash value");
5022 wpa_hexdump(MSG_DEBUG, "DPP: Calculated hash",
5023 hash, SHA256_MAC_LEN);
5024 return -1;
5025 }
5026 return 0;
5027}
5028
5029
5030static void dpp_copy_csign(struct dpp_authentication *auth, EVP_PKEY *csign)
5031{
5032 unsigned char *der = NULL;
5033 int der_len;
5034
5035 der_len = i2d_PUBKEY(csign, &der);
5036 if (der_len <= 0)
5037 return;
5038 wpabuf_free(auth->c_sign_key);
5039 auth->c_sign_key = wpabuf_alloc_copy(der, der_len);
5040 OPENSSL_free(der);
5041}
5042
5043
5044static void dpp_copy_netaccesskey(struct dpp_authentication *auth)
5045{
5046 unsigned char *der = NULL;
5047 int der_len;
5048 EC_KEY *eckey;
5049
5050 eckey = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
5051 if (!eckey)
5052 return;
5053
5054 der_len = i2d_ECPrivateKey(eckey, &der);
5055 if (der_len <= 0) {
5056 EC_KEY_free(eckey);
5057 return;
5058 }
5059 wpabuf_free(auth->net_access_key);
5060 auth->net_access_key = wpabuf_alloc_copy(der, der_len);
5061 OPENSSL_free(der);
5062 EC_KEY_free(eckey);
5063}
5064
5065
5066struct dpp_signed_connector_info {
5067 unsigned char *payload;
5068 size_t payload_len;
5069};
5070
Roshan Pius3a1667e2018-07-03 15:17:14 -07005071static enum dpp_status_error
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005072dpp_process_signed_connector(struct dpp_signed_connector_info *info,
5073 EVP_PKEY *csign_pub, const char *connector)
5074{
Roshan Pius3a1667e2018-07-03 15:17:14 -07005075 enum dpp_status_error ret = 255;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005076 const char *pos, *end, *signed_start, *signed_end;
5077 struct wpabuf *kid = NULL;
5078 unsigned char *prot_hdr = NULL, *signature = NULL;
5079 size_t prot_hdr_len = 0, signature_len = 0;
5080 const EVP_MD *sign_md = NULL;
5081 unsigned char *der = NULL;
5082 int der_len;
5083 int res;
5084 EVP_MD_CTX *md_ctx = NULL;
5085 ECDSA_SIG *sig = NULL;
5086 BIGNUM *r = NULL, *s = NULL;
5087 const struct dpp_curve_params *curve;
5088 EC_KEY *eckey;
5089 const EC_GROUP *group;
5090 int nid;
5091
5092 eckey = EVP_PKEY_get1_EC_KEY(csign_pub);
5093 if (!eckey)
5094 goto fail;
5095 group = EC_KEY_get0_group(eckey);
5096 if (!group)
5097 goto fail;
5098 nid = EC_GROUP_get_curve_name(group);
5099 curve = dpp_get_curve_nid(nid);
5100 if (!curve)
5101 goto fail;
5102 wpa_printf(MSG_DEBUG, "DPP: C-sign-key group: %s", curve->jwk_crv);
5103 os_memset(info, 0, sizeof(*info));
5104
5105 signed_start = pos = connector;
5106 end = os_strchr(pos, '.');
5107 if (!end) {
5108 wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector");
Roshan Pius3a1667e2018-07-03 15:17:14 -07005109 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005110 goto fail;
5111 }
5112 prot_hdr = base64_url_decode((const unsigned char *) pos,
5113 end - pos, &prot_hdr_len);
5114 if (!prot_hdr) {
5115 wpa_printf(MSG_DEBUG,
5116 "DPP: Failed to base64url decode signedConnector JWS Protected Header");
Roshan Pius3a1667e2018-07-03 15:17:14 -07005117 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005118 goto fail;
5119 }
5120 wpa_hexdump_ascii(MSG_DEBUG,
5121 "DPP: signedConnector - JWS Protected Header",
5122 prot_hdr, prot_hdr_len);
5123 kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md);
Roshan Pius3a1667e2018-07-03 15:17:14 -07005124 if (!kid) {
5125 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005126 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005127 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005128 if (wpabuf_len(kid) != SHA256_MAC_LEN) {
5129 wpa_printf(MSG_DEBUG,
5130 "DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)",
5131 (unsigned int) wpabuf_len(kid), SHA256_MAC_LEN);
Roshan Pius3a1667e2018-07-03 15:17:14 -07005132 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005133 goto fail;
5134 }
5135
5136 pos = end + 1;
5137 end = os_strchr(pos, '.');
5138 if (!end) {
5139 wpa_printf(MSG_DEBUG,
5140 "DPP: Missing dot(2) in signedConnector");
Roshan Pius3a1667e2018-07-03 15:17:14 -07005141 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005142 goto fail;
5143 }
5144 signed_end = end - 1;
5145 info->payload = base64_url_decode((const unsigned char *) pos,
5146 end - pos, &info->payload_len);
5147 if (!info->payload) {
5148 wpa_printf(MSG_DEBUG,
5149 "DPP: Failed to base64url decode signedConnector JWS Payload");
Roshan Pius3a1667e2018-07-03 15:17:14 -07005150 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005151 goto fail;
5152 }
5153 wpa_hexdump_ascii(MSG_DEBUG,
5154 "DPP: signedConnector - JWS Payload",
5155 info->payload, info->payload_len);
5156 pos = end + 1;
5157 signature = base64_url_decode((const unsigned char *) pos,
5158 os_strlen(pos), &signature_len);
5159 if (!signature) {
5160 wpa_printf(MSG_DEBUG,
5161 "DPP: Failed to base64url decode signedConnector signature");
Roshan Pius3a1667e2018-07-03 15:17:14 -07005162 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005163 goto fail;
5164 }
5165 wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature",
5166 signature, signature_len);
5167
Roshan Pius3a1667e2018-07-03 15:17:14 -07005168 if (dpp_check_pubkey_match(csign_pub, kid) < 0) {
5169 ret = DPP_STATUS_NO_MATCH;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005170 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005171 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005172
5173 if (signature_len & 0x01) {
5174 wpa_printf(MSG_DEBUG,
5175 "DPP: Unexpected signedConnector signature length (%d)",
5176 (int) signature_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07005177 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005178 goto fail;
5179 }
5180
5181 /* JWS Signature encodes the signature (r,s) as two octet strings. Need
5182 * to convert that to DER encoded ECDSA_SIG for OpenSSL EVP routines. */
5183 r = BN_bin2bn(signature, signature_len / 2, NULL);
5184 s = BN_bin2bn(signature + signature_len / 2, signature_len / 2, NULL);
5185 sig = ECDSA_SIG_new();
5186 if (!r || !s || !sig || ECDSA_SIG_set0(sig, r, s) != 1)
5187 goto fail;
5188 r = NULL;
5189 s = NULL;
5190
5191 der_len = i2d_ECDSA_SIG(sig, &der);
5192 if (der_len <= 0) {
5193 wpa_printf(MSG_DEBUG, "DPP: Could not DER encode signature");
5194 goto fail;
5195 }
5196 wpa_hexdump(MSG_DEBUG, "DPP: DER encoded signature", der, der_len);
5197 md_ctx = EVP_MD_CTX_create();
5198 if (!md_ctx)
5199 goto fail;
5200
5201 ERR_clear_error();
5202 if (EVP_DigestVerifyInit(md_ctx, NULL, sign_md, NULL, csign_pub) != 1) {
5203 wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyInit failed: %s",
5204 ERR_error_string(ERR_get_error(), NULL));
5205 goto fail;
5206 }
5207 if (EVP_DigestVerifyUpdate(md_ctx, signed_start,
5208 signed_end - signed_start + 1) != 1) {
5209 wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyUpdate failed: %s",
5210 ERR_error_string(ERR_get_error(), NULL));
5211 goto fail;
5212 }
5213 res = EVP_DigestVerifyFinal(md_ctx, der, der_len);
5214 if (res != 1) {
5215 wpa_printf(MSG_DEBUG,
5216 "DPP: EVP_DigestVerifyFinal failed (res=%d): %s",
5217 res, ERR_error_string(ERR_get_error(), NULL));
Roshan Pius3a1667e2018-07-03 15:17:14 -07005218 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005219 goto fail;
5220 }
5221
Roshan Pius3a1667e2018-07-03 15:17:14 -07005222 ret = DPP_STATUS_OK;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005223fail:
5224 EC_KEY_free(eckey);
5225 EVP_MD_CTX_destroy(md_ctx);
5226 os_free(prot_hdr);
5227 wpabuf_free(kid);
5228 os_free(signature);
5229 ECDSA_SIG_free(sig);
5230 BN_free(r);
5231 BN_free(s);
5232 OPENSSL_free(der);
5233 return ret;
5234}
5235
5236
5237static int dpp_parse_cred_dpp(struct dpp_authentication *auth,
5238 struct json_token *cred)
5239{
5240 struct dpp_signed_connector_info info;
5241 struct json_token *token, *csign;
5242 int ret = -1;
5243 EVP_PKEY *csign_pub = NULL;
5244 const struct dpp_curve_params *key_curve = NULL;
5245 const char *signed_connector;
5246
5247 os_memset(&info, 0, sizeof(info));
5248
5249 wpa_printf(MSG_DEBUG, "DPP: Connector credential");
5250
5251 csign = json_get_member(cred, "csign");
5252 if (!csign || csign->type != JSON_OBJECT) {
5253 wpa_printf(MSG_DEBUG, "DPP: No csign JWK in JSON");
5254 goto fail;
5255 }
5256
5257 csign_pub = dpp_parse_jwk(csign, &key_curve);
5258 if (!csign_pub) {
5259 wpa_printf(MSG_DEBUG, "DPP: Failed to parse csign JWK");
5260 goto fail;
5261 }
5262 dpp_debug_print_key("DPP: Received C-sign-key", csign_pub);
5263
5264 token = json_get_member(cred, "signedConnector");
5265 if (!token || token->type != JSON_STRING) {
5266 wpa_printf(MSG_DEBUG, "DPP: No signedConnector string found");
5267 goto fail;
5268 }
5269 wpa_hexdump_ascii(MSG_DEBUG, "DPP: signedConnector",
5270 token->string, os_strlen(token->string));
5271 signed_connector = token->string;
5272
5273 if (os_strchr(signed_connector, '"') ||
5274 os_strchr(signed_connector, '\n')) {
5275 wpa_printf(MSG_DEBUG,
5276 "DPP: Unexpected character in signedConnector");
5277 goto fail;
5278 }
5279
5280 if (dpp_process_signed_connector(&info, csign_pub,
Roshan Pius3a1667e2018-07-03 15:17:14 -07005281 signed_connector) != DPP_STATUS_OK)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005282 goto fail;
5283
5284 if (dpp_parse_connector(auth, info.payload, info.payload_len) < 0) {
5285 wpa_printf(MSG_DEBUG, "DPP: Failed to parse connector");
5286 goto fail;
5287 }
5288
5289 os_free(auth->connector);
5290 auth->connector = os_strdup(signed_connector);
5291
5292 dpp_copy_csign(auth, csign_pub);
5293 dpp_copy_netaccesskey(auth);
5294
5295 ret = 0;
5296fail:
5297 EVP_PKEY_free(csign_pub);
5298 os_free(info.payload);
5299 return ret;
5300}
5301
5302
Roshan Pius3a1667e2018-07-03 15:17:14 -07005303const char * dpp_akm_str(enum dpp_akm akm)
5304{
5305 switch (akm) {
5306 case DPP_AKM_DPP:
5307 return "dpp";
5308 case DPP_AKM_PSK:
5309 return "psk";
5310 case DPP_AKM_SAE:
5311 return "sae";
5312 case DPP_AKM_PSK_SAE:
5313 return "psk+sae";
5314 default:
5315 return "??";
5316 }
5317}
5318
5319
5320static enum dpp_akm dpp_akm_from_str(const char *akm)
5321{
5322 if (os_strcmp(akm, "psk") == 0)
5323 return DPP_AKM_PSK;
5324 if (os_strcmp(akm, "sae") == 0)
5325 return DPP_AKM_SAE;
5326 if (os_strcmp(akm, "psk+sae") == 0)
5327 return DPP_AKM_PSK_SAE;
5328 if (os_strcmp(akm, "dpp") == 0)
5329 return DPP_AKM_DPP;
5330 return DPP_AKM_UNKNOWN;
5331}
5332
5333
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005334static int dpp_parse_conf_obj(struct dpp_authentication *auth,
5335 const u8 *conf_obj, u16 conf_obj_len)
5336{
5337 int ret = -1;
5338 struct json_token *root, *token, *discovery, *cred;
5339
5340 root = json_parse((const char *) conf_obj, conf_obj_len);
5341 if (!root)
5342 return -1;
5343 if (root->type != JSON_OBJECT) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005344 dpp_auth_fail(auth, "JSON root is not an object");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005345 goto fail;
5346 }
5347
5348 token = json_get_member(root, "wi-fi_tech");
5349 if (!token || token->type != JSON_STRING) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005350 dpp_auth_fail(auth, "No wi-fi_tech string value found");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005351 goto fail;
5352 }
5353 if (os_strcmp(token->string, "infra") != 0) {
5354 wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech value: '%s'",
5355 token->string);
Roshan Pius3a1667e2018-07-03 15:17:14 -07005356 dpp_auth_fail(auth, "Unsupported wi-fi_tech value");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005357 goto fail;
5358 }
5359
5360 discovery = json_get_member(root, "discovery");
5361 if (!discovery || discovery->type != JSON_OBJECT) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005362 dpp_auth_fail(auth, "No discovery object in JSON");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005363 goto fail;
5364 }
5365
5366 token = json_get_member(discovery, "ssid");
5367 if (!token || token->type != JSON_STRING) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005368 dpp_auth_fail(auth, "No discovery::ssid string value found");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005369 goto fail;
5370 }
5371 wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid",
5372 token->string, os_strlen(token->string));
5373 if (os_strlen(token->string) > SSID_MAX_LEN) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005374 dpp_auth_fail(auth, "Too long discovery::ssid string value");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005375 goto fail;
5376 }
5377 auth->ssid_len = os_strlen(token->string);
5378 os_memcpy(auth->ssid, token->string, auth->ssid_len);
5379
5380 cred = json_get_member(root, "cred");
5381 if (!cred || cred->type != JSON_OBJECT) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005382 dpp_auth_fail(auth, "No cred object in JSON");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005383 goto fail;
5384 }
5385
5386 token = json_get_member(cred, "akm");
5387 if (!token || token->type != JSON_STRING) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005388 dpp_auth_fail(auth, "No cred::akm string value found");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005389 goto fail;
5390 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07005391 auth->akm = dpp_akm_from_str(token->string);
5392
5393 if (auth->akm == DPP_AKM_PSK || auth->akm == DPP_AKM_SAE ||
5394 auth->akm == DPP_AKM_PSK_SAE) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005395 if (dpp_parse_cred_legacy(auth, cred) < 0)
5396 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005397 } else if (auth->akm == DPP_AKM_DPP) {
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005398 if (dpp_parse_cred_dpp(auth, cred) < 0)
5399 goto fail;
5400 } else {
5401 wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s",
5402 token->string);
Roshan Pius3a1667e2018-07-03 15:17:14 -07005403 dpp_auth_fail(auth, "Unsupported akm");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005404 goto fail;
5405 }
5406
5407 wpa_printf(MSG_DEBUG, "DPP: JSON parsing completed successfully");
5408 ret = 0;
5409fail:
5410 json_free(root);
5411 return ret;
5412}
5413
5414
5415int dpp_conf_resp_rx(struct dpp_authentication *auth,
5416 const struct wpabuf *resp)
5417{
5418 const u8 *wrapped_data, *e_nonce, *status, *conf_obj;
5419 u16 wrapped_data_len, e_nonce_len, status_len, conf_obj_len;
5420 const u8 *addr[1];
5421 size_t len[1];
5422 u8 *unwrapped = NULL;
5423 size_t unwrapped_len = 0;
5424 int ret = -1;
5425
5426 if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005427 dpp_auth_fail(auth, "Invalid attribute in config response");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005428 return -1;
5429 }
5430
5431 wrapped_data = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp),
5432 DPP_ATTR_WRAPPED_DATA,
5433 &wrapped_data_len);
5434 if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005435 dpp_auth_fail(auth,
5436 "Missing or invalid required Wrapped Data attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005437 return -1;
5438 }
5439
5440 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
5441 wrapped_data, wrapped_data_len);
5442 unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
5443 unwrapped = os_malloc(unwrapped_len);
5444 if (!unwrapped)
5445 return -1;
5446
5447 addr[0] = wpabuf_head(resp);
5448 len[0] = wrapped_data - 4 - (const u8 *) wpabuf_head(resp);
5449 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
5450
5451 if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
5452 wrapped_data, wrapped_data_len,
5453 1, addr, len, unwrapped) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005454 dpp_auth_fail(auth, "AES-SIV decryption failed");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005455 goto fail;
5456 }
5457 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
5458 unwrapped, unwrapped_len);
5459
5460 if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005461 dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005462 goto fail;
5463 }
5464
5465 e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
5466 DPP_ATTR_ENROLLEE_NONCE,
5467 &e_nonce_len);
5468 if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005469 dpp_auth_fail(auth,
5470 "Missing or invalid Enrollee Nonce attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005471 goto fail;
5472 }
5473 wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
5474 if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005475 dpp_auth_fail(auth, "Enrollee Nonce mismatch");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005476 goto fail;
5477 }
5478
5479 status = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp),
5480 DPP_ATTR_STATUS, &status_len);
5481 if (!status || status_len < 1) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005482 dpp_auth_fail(auth,
5483 "Missing or invalid required DPP Status attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005484 goto fail;
5485 }
5486 wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
5487 if (status[0] != DPP_STATUS_OK) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005488 dpp_auth_fail(auth, "Configurator rejected configuration");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005489 goto fail;
5490 }
5491
5492 conf_obj = dpp_get_attr(unwrapped, unwrapped_len,
5493 DPP_ATTR_CONFIG_OBJ, &conf_obj_len);
5494 if (!conf_obj) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07005495 dpp_auth_fail(auth,
5496 "Missing required Configuration Object attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005497 goto fail;
5498 }
5499 wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
5500 conf_obj, conf_obj_len);
5501 if (dpp_parse_conf_obj(auth, conf_obj, conf_obj_len) < 0)
5502 goto fail;
5503
5504 ret = 0;
5505
5506fail:
5507 os_free(unwrapped);
5508 return ret;
5509}
5510
5511
5512void dpp_configurator_free(struct dpp_configurator *conf)
5513{
5514 if (!conf)
5515 return;
5516 EVP_PKEY_free(conf->csign);
5517 os_free(conf->kid);
5518 os_free(conf);
5519}
5520
5521
Roshan Pius3a1667e2018-07-03 15:17:14 -07005522int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf,
5523 size_t buflen)
5524{
5525 EC_KEY *eckey;
5526 int key_len, ret = -1;
5527 unsigned char *key = NULL;
5528
5529 if (!conf->csign)
5530 return -1;
5531
5532 eckey = EVP_PKEY_get1_EC_KEY(conf->csign);
5533 if (!eckey)
5534 return -1;
5535
5536 key_len = i2d_ECPrivateKey(eckey, &key);
5537 if (key_len > 0)
5538 ret = wpa_snprintf_hex(buf, buflen, key, key_len);
5539
5540 EC_KEY_free(eckey);
5541 OPENSSL_free(key);
5542 return ret;
5543}
5544
5545
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005546struct dpp_configurator *
5547dpp_keygen_configurator(const char *curve, const u8 *privkey,
5548 size_t privkey_len)
5549{
5550 struct dpp_configurator *conf;
5551 struct wpabuf *csign_pub = NULL;
5552 u8 kid_hash[SHA256_MAC_LEN];
5553 const u8 *addr[1];
5554 size_t len[1];
5555
5556 conf = os_zalloc(sizeof(*conf));
5557 if (!conf)
5558 return NULL;
5559
5560 if (!curve) {
5561 conf->curve = &dpp_curves[0];
5562 } else {
5563 conf->curve = dpp_get_curve_name(curve);
5564 if (!conf->curve) {
5565 wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
5566 curve);
Hai Shalom39ba6fc2019-01-22 12:40:38 -08005567 os_free(conf);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005568 return NULL;
5569 }
5570 }
5571 if (privkey)
5572 conf->csign = dpp_set_keypair(&conf->curve, privkey,
5573 privkey_len);
5574 else
5575 conf->csign = dpp_gen_keypair(conf->curve);
5576 if (!conf->csign)
5577 goto fail;
5578 conf->own = 1;
5579
5580 csign_pub = dpp_get_pubkey_point(conf->csign, 1);
5581 if (!csign_pub) {
5582 wpa_printf(MSG_INFO, "DPP: Failed to extract C-sign-key");
5583 goto fail;
5584 }
5585
5586 /* kid = SHA256(ANSI X9.63 uncompressed C-sign-key) */
5587 addr[0] = wpabuf_head(csign_pub);
5588 len[0] = wpabuf_len(csign_pub);
5589 if (sha256_vector(1, addr, len, kid_hash) < 0) {
5590 wpa_printf(MSG_DEBUG,
5591 "DPP: Failed to derive kid for C-sign-key");
5592 goto fail;
5593 }
5594
5595 conf->kid = (char *) base64_url_encode(kid_hash, sizeof(kid_hash),
5596 NULL, 0);
5597 if (!conf->kid)
5598 goto fail;
5599out:
5600 wpabuf_free(csign_pub);
5601 return conf;
5602fail:
5603 dpp_configurator_free(conf);
5604 conf = NULL;
5605 goto out;
5606}
5607
5608
5609int dpp_configurator_own_config(struct dpp_authentication *auth,
Roshan Pius3a1667e2018-07-03 15:17:14 -07005610 const char *curve, int ap)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005611{
5612 struct wpabuf *conf_obj;
5613 int ret = -1;
5614
5615 if (!auth->conf) {
5616 wpa_printf(MSG_DEBUG, "DPP: No configurator specified");
5617 return -1;
5618 }
5619
5620 if (!curve) {
5621 auth->curve = &dpp_curves[0];
5622 } else {
5623 auth->curve = dpp_get_curve_name(curve);
5624 if (!auth->curve) {
5625 wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
5626 curve);
5627 return -1;
5628 }
5629 }
5630 wpa_printf(MSG_DEBUG,
5631 "DPP: Building own configuration/connector with curve %s",
5632 auth->curve->name);
5633
5634 auth->own_protocol_key = dpp_gen_keypair(auth->curve);
5635 if (!auth->own_protocol_key)
5636 return -1;
5637 dpp_copy_netaccesskey(auth);
5638 auth->peer_protocol_key = auth->own_protocol_key;
5639 dpp_copy_csign(auth, auth->conf->csign);
5640
Roshan Pius3a1667e2018-07-03 15:17:14 -07005641 conf_obj = dpp_build_conf_obj(auth, ap);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005642 if (!conf_obj)
5643 goto fail;
5644 ret = dpp_parse_conf_obj(auth, wpabuf_head(conf_obj),
5645 wpabuf_len(conf_obj));
5646fail:
5647 wpabuf_free(conf_obj);
5648 auth->peer_protocol_key = NULL;
5649 return ret;
5650}
5651
5652
5653static int dpp_compatible_netrole(const char *role1, const char *role2)
5654{
5655 return (os_strcmp(role1, "sta") == 0 && os_strcmp(role2, "ap") == 0) ||
5656 (os_strcmp(role1, "ap") == 0 && os_strcmp(role2, "sta") == 0);
5657}
5658
5659
5660static int dpp_connector_compatible_group(struct json_token *root,
5661 const char *group_id,
5662 const char *net_role)
5663{
5664 struct json_token *groups, *token;
5665
5666 groups = json_get_member(root, "groups");
5667 if (!groups || groups->type != JSON_ARRAY)
5668 return 0;
5669
5670 for (token = groups->child; token; token = token->sibling) {
5671 struct json_token *id, *role;
5672
5673 id = json_get_member(token, "groupId");
5674 if (!id || id->type != JSON_STRING)
5675 continue;
5676
5677 role = json_get_member(token, "netRole");
5678 if (!role || role->type != JSON_STRING)
5679 continue;
5680
5681 if (os_strcmp(id->string, "*") != 0 &&
5682 os_strcmp(group_id, "*") != 0 &&
5683 os_strcmp(id->string, group_id) != 0)
5684 continue;
5685
5686 if (dpp_compatible_netrole(role->string, net_role))
5687 return 1;
5688 }
5689
5690 return 0;
5691}
5692
5693
5694static int dpp_connector_match_groups(struct json_token *own_root,
5695 struct json_token *peer_root)
5696{
5697 struct json_token *groups, *token;
5698
5699 groups = json_get_member(peer_root, "groups");
5700 if (!groups || groups->type != JSON_ARRAY) {
5701 wpa_printf(MSG_DEBUG, "DPP: No peer groups array found");
5702 return 0;
5703 }
5704
5705 for (token = groups->child; token; token = token->sibling) {
5706 struct json_token *id, *role;
5707
5708 id = json_get_member(token, "groupId");
5709 if (!id || id->type != JSON_STRING) {
5710 wpa_printf(MSG_DEBUG,
5711 "DPP: Missing peer groupId string");
5712 continue;
5713 }
5714
5715 role = json_get_member(token, "netRole");
5716 if (!role || role->type != JSON_STRING) {
5717 wpa_printf(MSG_DEBUG,
5718 "DPP: Missing peer groups::netRole string");
5719 continue;
5720 }
5721 wpa_printf(MSG_DEBUG,
5722 "DPP: peer connector group: groupId='%s' netRole='%s'",
5723 id->string, role->string);
5724 if (dpp_connector_compatible_group(own_root, id->string,
5725 role->string)) {
5726 wpa_printf(MSG_DEBUG,
5727 "DPP: Compatible group/netRole in own connector");
5728 return 1;
5729 }
5730 }
5731
5732 return 0;
5733}
5734
5735
5736static int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk,
5737 unsigned int hash_len)
5738{
5739 u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
5740 const char *info = "DPP PMK";
5741 int res;
5742
5743 /* PMK = HKDF(<>, "DPP PMK", N.x) */
5744
5745 /* HKDF-Extract(<>, N.x) */
5746 os_memset(salt, 0, hash_len);
5747 if (dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk) < 0)
5748 return -1;
5749 wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
5750 prk, hash_len);
5751
5752 /* HKDF-Expand(PRK, info, L) */
5753 res = dpp_hkdf_expand(hash_len, prk, hash_len, info, pmk, hash_len);
5754 os_memset(prk, 0, hash_len);
5755 if (res < 0)
5756 return -1;
5757
5758 wpa_hexdump_key(MSG_DEBUG, "DPP: PMK = HKDF-Expand(PRK, info, L)",
5759 pmk, hash_len);
5760 return 0;
5761}
5762
5763
5764static int dpp_derive_pmkid(const struct dpp_curve_params *curve,
5765 EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid)
5766{
5767 struct wpabuf *nkx, *pkx;
5768 int ret = -1, res;
5769 const u8 *addr[2];
5770 size_t len[2];
5771 u8 hash[SHA256_MAC_LEN];
5772
5773 /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
5774 nkx = dpp_get_pubkey_point(own_key, 0);
5775 pkx = dpp_get_pubkey_point(peer_key, 0);
5776 if (!nkx || !pkx)
5777 goto fail;
5778 addr[0] = wpabuf_head(nkx);
5779 len[0] = wpabuf_len(nkx) / 2;
5780 addr[1] = wpabuf_head(pkx);
5781 len[1] = wpabuf_len(pkx) / 2;
5782 if (len[0] != len[1])
5783 goto fail;
5784 if (os_memcmp(addr[0], addr[1], len[0]) > 0) {
5785 addr[0] = wpabuf_head(pkx);
5786 addr[1] = wpabuf_head(nkx);
5787 }
5788 wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 1", addr[0], len[0]);
5789 wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 2", addr[1], len[1]);
5790 res = sha256_vector(2, addr, len, hash);
5791 if (res < 0)
5792 goto fail;
5793 wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash output", hash, SHA256_MAC_LEN);
5794 os_memcpy(pmkid, hash, PMKID_LEN);
5795 wpa_hexdump(MSG_DEBUG, "DPP: PMKID", pmkid, PMKID_LEN);
5796 ret = 0;
5797fail:
5798 wpabuf_free(nkx);
5799 wpabuf_free(pkx);
5800 return ret;
5801}
5802
5803
Roshan Pius3a1667e2018-07-03 15:17:14 -07005804enum dpp_status_error
5805dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
5806 const u8 *net_access_key, size_t net_access_key_len,
5807 const u8 *csign_key, size_t csign_key_len,
5808 const u8 *peer_connector, size_t peer_connector_len,
5809 os_time_t *expiry)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005810{
5811 struct json_token *root = NULL, *netkey, *token;
5812 struct json_token *own_root = NULL;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005813 enum dpp_status_error ret = 255, res;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005814 EVP_PKEY *own_key = NULL, *peer_key = NULL;
5815 struct wpabuf *own_key_pub = NULL;
5816 const struct dpp_curve_params *curve, *own_curve;
5817 struct dpp_signed_connector_info info;
5818 const unsigned char *p;
5819 EVP_PKEY *csign = NULL;
5820 char *signed_connector = NULL;
5821 const char *pos, *end;
5822 unsigned char *own_conn = NULL;
5823 size_t own_conn_len;
5824 EVP_PKEY_CTX *ctx = NULL;
5825 size_t Nx_len;
5826 u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
5827
5828 os_memset(intro, 0, sizeof(*intro));
5829 os_memset(&info, 0, sizeof(info));
5830 if (expiry)
5831 *expiry = 0;
5832
5833 p = csign_key;
5834 csign = d2i_PUBKEY(NULL, &p, csign_key_len);
5835 if (!csign) {
5836 wpa_printf(MSG_ERROR,
5837 "DPP: Failed to parse local C-sign-key information");
5838 goto fail;
5839 }
5840
5841 own_key = dpp_set_keypair(&own_curve, net_access_key,
5842 net_access_key_len);
5843 if (!own_key) {
5844 wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
5845 goto fail;
5846 }
5847
5848 pos = os_strchr(own_connector, '.');
5849 if (!pos) {
5850 wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the first dot (.)");
5851 goto fail;
5852 }
5853 pos++;
5854 end = os_strchr(pos, '.');
5855 if (!end) {
5856 wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)");
5857 goto fail;
5858 }
5859 own_conn = base64_url_decode((const unsigned char *) pos,
5860 end - pos, &own_conn_len);
5861 if (!own_conn) {
5862 wpa_printf(MSG_DEBUG,
5863 "DPP: Failed to base64url decode own signedConnector JWS Payload");
5864 goto fail;
5865 }
5866
5867 own_root = json_parse((const char *) own_conn, own_conn_len);
5868 if (!own_root) {
5869 wpa_printf(MSG_DEBUG, "DPP: Failed to parse local connector");
5870 goto fail;
5871 }
5872
5873 wpa_hexdump_ascii(MSG_DEBUG, "DPP: Peer signedConnector",
5874 peer_connector, peer_connector_len);
5875 signed_connector = os_malloc(peer_connector_len + 1);
5876 if (!signed_connector)
5877 goto fail;
5878 os_memcpy(signed_connector, peer_connector, peer_connector_len);
5879 signed_connector[peer_connector_len] = '\0';
5880
Roshan Pius3a1667e2018-07-03 15:17:14 -07005881 res = dpp_process_signed_connector(&info, csign, signed_connector);
5882 if (res != DPP_STATUS_OK) {
5883 ret = res;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005884 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005885 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005886
5887 root = json_parse((const char *) info.payload, info.payload_len);
5888 if (!root) {
5889 wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed");
Roshan Pius3a1667e2018-07-03 15:17:14 -07005890 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005891 goto fail;
5892 }
5893
5894 if (!dpp_connector_match_groups(own_root, root)) {
5895 wpa_printf(MSG_DEBUG,
5896 "DPP: Peer connector does not include compatible group netrole with own connector");
Roshan Pius3a1667e2018-07-03 15:17:14 -07005897 ret = DPP_STATUS_NO_MATCH;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005898 goto fail;
5899 }
5900
5901 token = json_get_member(root, "expiry");
5902 if (!token || token->type != JSON_STRING) {
5903 wpa_printf(MSG_DEBUG,
5904 "DPP: No expiry string found - connector does not expire");
5905 } else {
5906 wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string);
5907 if (dpp_key_expired(token->string, expiry)) {
5908 wpa_printf(MSG_DEBUG,
5909 "DPP: Connector (netAccessKey) has expired");
Roshan Pius3a1667e2018-07-03 15:17:14 -07005910 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005911 goto fail;
5912 }
5913 }
5914
5915 netkey = json_get_member(root, "netAccessKey");
5916 if (!netkey || netkey->type != JSON_OBJECT) {
5917 wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
Roshan Pius3a1667e2018-07-03 15:17:14 -07005918 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005919 goto fail;
5920 }
5921
5922 peer_key = dpp_parse_jwk(netkey, &curve);
Roshan Pius3a1667e2018-07-03 15:17:14 -07005923 if (!peer_key) {
5924 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005925 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07005926 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005927 dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
5928
5929 if (own_curve != curve) {
5930 wpa_printf(MSG_DEBUG,
5931 "DPP: Mismatching netAccessKey curves (%s != %s)",
5932 own_curve->name, curve->name);
Roshan Pius3a1667e2018-07-03 15:17:14 -07005933 ret = DPP_STATUS_INVALID_CONNECTOR;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005934 goto fail;
5935 }
5936
5937 /* ECDH: N = nk * PK */
5938 ctx = EVP_PKEY_CTX_new(own_key, NULL);
5939 if (!ctx ||
5940 EVP_PKEY_derive_init(ctx) != 1 ||
5941 EVP_PKEY_derive_set_peer(ctx, peer_key) != 1 ||
5942 EVP_PKEY_derive(ctx, NULL, &Nx_len) != 1 ||
5943 Nx_len > DPP_MAX_SHARED_SECRET_LEN ||
5944 EVP_PKEY_derive(ctx, Nx, &Nx_len) != 1) {
5945 wpa_printf(MSG_ERROR,
5946 "DPP: Failed to derive ECDH shared secret: %s",
5947 ERR_error_string(ERR_get_error(), NULL));
5948 goto fail;
5949 }
5950
5951 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
5952 Nx, Nx_len);
5953
5954 /* PMK = HKDF(<>, "DPP PMK", N.x) */
5955 if (dpp_derive_pmk(Nx, Nx_len, intro->pmk, curve->hash_len) < 0) {
5956 wpa_printf(MSG_ERROR, "DPP: Failed to derive PMK");
5957 goto fail;
5958 }
5959 intro->pmk_len = curve->hash_len;
5960
5961 /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
5962 if (dpp_derive_pmkid(curve, own_key, peer_key, intro->pmkid) < 0) {
5963 wpa_printf(MSG_ERROR, "DPP: Failed to derive PMKID");
5964 goto fail;
5965 }
5966
Roshan Pius3a1667e2018-07-03 15:17:14 -07005967 ret = DPP_STATUS_OK;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005968fail:
Roshan Pius3a1667e2018-07-03 15:17:14 -07005969 if (ret != DPP_STATUS_OK)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07005970 os_memset(intro, 0, sizeof(*intro));
5971 os_memset(Nx, 0, sizeof(Nx));
5972 EVP_PKEY_CTX_free(ctx);
5973 os_free(own_conn);
5974 os_free(signed_connector);
5975 os_free(info.payload);
5976 EVP_PKEY_free(own_key);
5977 wpabuf_free(own_key_pub);
5978 EVP_PKEY_free(peer_key);
5979 EVP_PKEY_free(csign);
5980 json_free(root);
5981 json_free(own_root);
5982 return ret;
5983}
5984
5985
5986static EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve,
5987 int init)
5988{
5989 EC_GROUP *group;
5990 size_t len = curve->prime_len;
5991 const u8 *x, *y;
5992
5993 switch (curve->ike_group) {
5994 case 19:
5995 x = init ? pkex_init_x_p256 : pkex_resp_x_p256;
5996 y = init ? pkex_init_y_p256 : pkex_resp_y_p256;
5997 break;
5998 case 20:
5999 x = init ? pkex_init_x_p384 : pkex_resp_x_p384;
6000 y = init ? pkex_init_y_p384 : pkex_resp_y_p384;
6001 break;
6002 case 21:
6003 x = init ? pkex_init_x_p521 : pkex_resp_x_p521;
6004 y = init ? pkex_init_y_p521 : pkex_resp_y_p521;
6005 break;
6006 case 28:
6007 x = init ? pkex_init_x_bp_p256r1 : pkex_resp_x_bp_p256r1;
6008 y = init ? pkex_init_y_bp_p256r1 : pkex_resp_y_bp_p256r1;
6009 break;
6010 case 29:
6011 x = init ? pkex_init_x_bp_p384r1 : pkex_resp_x_bp_p384r1;
6012 y = init ? pkex_init_y_bp_p384r1 : pkex_resp_y_bp_p384r1;
6013 break;
6014 case 30:
6015 x = init ? pkex_init_x_bp_p512r1 : pkex_resp_x_bp_p512r1;
6016 y = init ? pkex_init_y_bp_p512r1 : pkex_resp_y_bp_p512r1;
6017 break;
6018 default:
6019 return NULL;
6020 }
6021
6022 group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
6023 if (!group)
6024 return NULL;
6025 return dpp_set_pubkey_point_group(group, x, y, len);
6026}
6027
6028
6029static EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
6030 const u8 *mac_init, const char *code,
6031 const char *identifier, BN_CTX *bnctx,
6032 const EC_GROUP **ret_group)
6033{
6034 u8 hash[DPP_MAX_HASH_LEN];
6035 const u8 *addr[3];
6036 size_t len[3];
6037 unsigned int num_elem = 0;
6038 EC_POINT *Qi = NULL;
6039 EVP_PKEY *Pi = NULL;
6040 EC_KEY *Pi_ec = NULL;
6041 const EC_POINT *Pi_point;
6042 BIGNUM *hash_bn = NULL;
6043 const EC_GROUP *group = NULL;
6044 EC_GROUP *group2 = NULL;
6045
6046 /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
6047
6048 wpa_printf(MSG_DEBUG, "DPP: MAC-Initiator: " MACSTR, MAC2STR(mac_init));
6049 addr[num_elem] = mac_init;
6050 len[num_elem] = ETH_ALEN;
6051 num_elem++;
6052 if (identifier) {
6053 wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
6054 identifier);
6055 addr[num_elem] = (const u8 *) identifier;
6056 len[num_elem] = os_strlen(identifier);
6057 num_elem++;
6058 }
6059 wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
6060 addr[num_elem] = (const u8 *) code;
6061 len[num_elem] = os_strlen(code);
6062 num_elem++;
6063 if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
6064 goto fail;
6065 wpa_hexdump_key(MSG_DEBUG,
6066 "DPP: H(MAC-Initiator | [identifier |] code)",
6067 hash, curve->hash_len);
6068 Pi = dpp_pkex_get_role_elem(curve, 1);
6069 if (!Pi)
6070 goto fail;
6071 dpp_debug_print_key("DPP: Pi", Pi);
6072 Pi_ec = EVP_PKEY_get1_EC_KEY(Pi);
6073 if (!Pi_ec)
6074 goto fail;
6075 Pi_point = EC_KEY_get0_public_key(Pi_ec);
6076
6077 group = EC_KEY_get0_group(Pi_ec);
6078 if (!group)
6079 goto fail;
6080 group2 = EC_GROUP_dup(group);
6081 if (!group2)
6082 goto fail;
6083 Qi = EC_POINT_new(group2);
6084 if (!Qi) {
6085 EC_GROUP_free(group2);
6086 goto fail;
6087 }
6088 hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
6089 if (!hash_bn ||
6090 EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1)
6091 goto fail;
6092 if (EC_POINT_is_at_infinity(group, Qi)) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07006093 wpa_printf(MSG_INFO, "DPP: Qi is the point-at-infinity");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006094 goto fail;
6095 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07006096 dpp_debug_print_point("DPP: Qi", group, Qi);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006097out:
6098 EC_KEY_free(Pi_ec);
6099 EVP_PKEY_free(Pi);
6100 BN_clear_free(hash_bn);
6101 if (ret_group)
6102 *ret_group = group2;
6103 return Qi;
6104fail:
6105 EC_POINT_free(Qi);
6106 Qi = NULL;
6107 goto out;
6108}
6109
6110
6111static EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
6112 const u8 *mac_resp, const char *code,
6113 const char *identifier, BN_CTX *bnctx,
6114 const EC_GROUP **ret_group)
6115{
6116 u8 hash[DPP_MAX_HASH_LEN];
6117 const u8 *addr[3];
6118 size_t len[3];
6119 unsigned int num_elem = 0;
6120 EC_POINT *Qr = NULL;
6121 EVP_PKEY *Pr = NULL;
6122 EC_KEY *Pr_ec = NULL;
6123 const EC_POINT *Pr_point;
6124 BIGNUM *hash_bn = NULL;
6125 const EC_GROUP *group = NULL;
6126 EC_GROUP *group2 = NULL;
6127
6128 /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
6129
6130 wpa_printf(MSG_DEBUG, "DPP: MAC-Responder: " MACSTR, MAC2STR(mac_resp));
6131 addr[num_elem] = mac_resp;
6132 len[num_elem] = ETH_ALEN;
6133 num_elem++;
6134 if (identifier) {
6135 wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
6136 identifier);
6137 addr[num_elem] = (const u8 *) identifier;
6138 len[num_elem] = os_strlen(identifier);
6139 num_elem++;
6140 }
6141 wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
6142 addr[num_elem] = (const u8 *) code;
6143 len[num_elem] = os_strlen(code);
6144 num_elem++;
6145 if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
6146 goto fail;
6147 wpa_hexdump_key(MSG_DEBUG,
6148 "DPP: H(MAC-Responder | [identifier |] code)",
6149 hash, curve->hash_len);
6150 Pr = dpp_pkex_get_role_elem(curve, 0);
6151 if (!Pr)
6152 goto fail;
6153 dpp_debug_print_key("DPP: Pr", Pr);
6154 Pr_ec = EVP_PKEY_get1_EC_KEY(Pr);
6155 if (!Pr_ec)
6156 goto fail;
6157 Pr_point = EC_KEY_get0_public_key(Pr_ec);
6158
6159 group = EC_KEY_get0_group(Pr_ec);
6160 if (!group)
6161 goto fail;
6162 group2 = EC_GROUP_dup(group);
6163 if (!group2)
6164 goto fail;
6165 Qr = EC_POINT_new(group2);
6166 if (!Qr) {
6167 EC_GROUP_free(group2);
6168 goto fail;
6169 }
6170 hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
6171 if (!hash_bn ||
6172 EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1)
6173 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07006174 if (EC_POINT_is_at_infinity(group, Qr)) {
6175 wpa_printf(MSG_INFO, "DPP: Qr is the point-at-infinity");
6176 goto fail;
6177 }
6178 dpp_debug_print_point("DPP: Qr", group, Qr);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006179out:
6180 EC_KEY_free(Pr_ec);
6181 EVP_PKEY_free(Pr);
6182 BN_clear_free(hash_bn);
6183 if (ret_group)
6184 *ret_group = group2;
6185 return Qr;
6186fail:
6187 EC_POINT_free(Qr);
6188 Qr = NULL;
6189 goto out;
6190}
6191
6192
Roshan Pius3a1667e2018-07-03 15:17:14 -07006193#ifdef CONFIG_TESTING_OPTIONS
6194static int dpp_test_gen_invalid_key(struct wpabuf *msg,
6195 const struct dpp_curve_params *curve)
6196{
6197 BN_CTX *ctx;
6198 BIGNUM *x, *y;
6199 int ret = -1;
6200 EC_GROUP *group;
6201 EC_POINT *point;
6202
6203 group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
6204 if (!group)
6205 return -1;
6206
6207 ctx = BN_CTX_new();
6208 point = EC_POINT_new(group);
6209 x = BN_new();
6210 y = BN_new();
6211 if (!ctx || !point || !x || !y)
6212 goto fail;
6213
6214 if (BN_rand(x, curve->prime_len * 8, 0, 0) != 1)
6215 goto fail;
6216
6217 /* Generate a random y coordinate that results in a point that is not
6218 * on the curve. */
6219 for (;;) {
6220 if (BN_rand(y, curve->prime_len * 8, 0, 0) != 1)
6221 goto fail;
6222
6223 if (EC_POINT_set_affine_coordinates_GFp(group, point, x, y,
6224 ctx) != 1) {
Hai Shalom39ba6fc2019-01-22 12:40:38 -08006225#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(OPENSSL_IS_BORINGSSL)
Roshan Pius3a1667e2018-07-03 15:17:14 -07006226 /* Unlike older OpenSSL versions, OpenSSL 1.1.1 and BoringSSL
6227 * return an error from EC_POINT_set_affine_coordinates_GFp()
6228 * when the point is not on the curve. */
6229 break;
Hai Shalom39ba6fc2019-01-22 12:40:38 -08006230#else /* >=1.1.0 or OPENSSL_IS_BORINGSSL */
Roshan Pius3a1667e2018-07-03 15:17:14 -07006231 goto fail;
Hai Shalom39ba6fc2019-01-22 12:40:38 -08006232#endif /* >= 1.1.0 or OPENSSL_IS_BORINGSSL */
Roshan Pius3a1667e2018-07-03 15:17:14 -07006233 }
6234
6235 if (!EC_POINT_is_on_curve(group, point, ctx))
6236 break;
6237 }
6238
6239 if (dpp_bn2bin_pad(x, wpabuf_put(msg, curve->prime_len),
6240 curve->prime_len) < 0 ||
6241 dpp_bn2bin_pad(y, wpabuf_put(msg, curve->prime_len),
6242 curve->prime_len) < 0)
6243 goto fail;
6244
6245 ret = 0;
6246fail:
6247 if (ret < 0)
6248 wpa_printf(MSG_INFO, "DPP: Failed to generate invalid key");
6249 BN_free(x);
6250 BN_free(y);
6251 EC_POINT_free(point);
6252 BN_CTX_free(ctx);
6253
6254 return ret;
6255}
6256#endif /* CONFIG_TESTING_OPTIONS */
6257
6258
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006259static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
6260{
6261 EC_KEY *X_ec = NULL;
6262 const EC_POINT *X_point;
6263 BN_CTX *bnctx = NULL;
6264 const EC_GROUP *group;
6265 EC_POINT *Qi = NULL, *M = NULL;
6266 struct wpabuf *M_buf = NULL;
6267 BIGNUM *Mx = NULL, *My = NULL;
6268 struct wpabuf *msg = NULL;
6269 size_t attr_len;
6270 const struct dpp_curve_params *curve = pkex->own_bi->curve;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006271
6272 wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request");
6273
6274 /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
6275 bnctx = BN_CTX_new();
6276 if (!bnctx)
6277 goto fail;
6278 Qi = dpp_pkex_derive_Qi(curve, pkex->own_mac, pkex->code,
6279 pkex->identifier, bnctx, &group);
6280 if (!Qi)
6281 goto fail;
6282
6283 /* Generate a random ephemeral keypair x/X */
Roshan Pius3a1667e2018-07-03 15:17:14 -07006284#ifdef CONFIG_TESTING_OPTIONS
6285 if (dpp_pkex_ephemeral_key_override_len) {
6286 const struct dpp_curve_params *tmp_curve;
6287
6288 wpa_printf(MSG_INFO,
6289 "DPP: TESTING - override ephemeral key x/X");
6290 pkex->x = dpp_set_keypair(&tmp_curve,
6291 dpp_pkex_ephemeral_key_override,
6292 dpp_pkex_ephemeral_key_override_len);
6293 } else {
6294 pkex->x = dpp_gen_keypair(curve);
6295 }
6296#else /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006297 pkex->x = dpp_gen_keypair(curve);
Roshan Pius3a1667e2018-07-03 15:17:14 -07006298#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006299 if (!pkex->x)
6300 goto fail;
6301
6302 /* M = X + Qi */
6303 X_ec = EVP_PKEY_get1_EC_KEY(pkex->x);
6304 if (!X_ec)
6305 goto fail;
6306 X_point = EC_KEY_get0_public_key(X_ec);
6307 if (!X_point)
6308 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07006309 dpp_debug_print_point("DPP: X", group, X_point);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006310 M = EC_POINT_new(group);
6311 Mx = BN_new();
6312 My = BN_new();
6313 if (!M || !Mx || !My ||
6314 EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 ||
6315 EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1)
6316 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07006317 dpp_debug_print_point("DPP: M", group, M);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006318
6319 /* Initiator -> Responder: group, [identifier,] M */
6320 attr_len = 4 + 2;
6321 if (pkex->identifier)
6322 attr_len += 4 + os_strlen(pkex->identifier);
6323 attr_len += 4 + 2 * curve->prime_len;
6324 msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_REQ, attr_len);
6325 if (!msg)
6326 goto fail;
6327
Roshan Pius3a1667e2018-07-03 15:17:14 -07006328#ifdef CONFIG_TESTING_OPTIONS
6329 if (dpp_test == DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ) {
6330 wpa_printf(MSG_INFO, "DPP: TESTING - no Finite Cyclic Group");
6331 goto skip_finite_cyclic_group;
6332 }
6333#endif /* CONFIG_TESTING_OPTIONS */
6334
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006335 /* Finite Cyclic Group attribute */
6336 wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
6337 wpabuf_put_le16(msg, 2);
6338 wpabuf_put_le16(msg, curve->ike_group);
6339
Roshan Pius3a1667e2018-07-03 15:17:14 -07006340#ifdef CONFIG_TESTING_OPTIONS
6341skip_finite_cyclic_group:
6342#endif /* CONFIG_TESTING_OPTIONS */
6343
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006344 /* Code Identifier attribute */
6345 if (pkex->identifier) {
6346 wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
6347 wpabuf_put_le16(msg, os_strlen(pkex->identifier));
6348 wpabuf_put_str(msg, pkex->identifier);
6349 }
6350
Roshan Pius3a1667e2018-07-03 15:17:14 -07006351#ifdef CONFIG_TESTING_OPTIONS
6352 if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
6353 wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
6354 goto out;
6355 }
6356#endif /* CONFIG_TESTING_OPTIONS */
6357
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006358 /* M in Encrypted Key attribute */
6359 wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
6360 wpabuf_put_le16(msg, 2 * curve->prime_len);
6361
Roshan Pius3a1667e2018-07-03 15:17:14 -07006362#ifdef CONFIG_TESTING_OPTIONS
6363 if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
6364 wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
6365 if (dpp_test_gen_invalid_key(msg, curve) < 0)
6366 goto fail;
6367 goto out;
6368 }
6369#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006370
Roshan Pius3a1667e2018-07-03 15:17:14 -07006371 if (dpp_bn2bin_pad(Mx, wpabuf_put(msg, curve->prime_len),
6372 curve->prime_len) < 0 ||
6373 dpp_bn2bin_pad(Mx, pkex->Mx, curve->prime_len) < 0 ||
6374 dpp_bn2bin_pad(My, wpabuf_put(msg, curve->prime_len),
6375 curve->prime_len) < 0)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006376 goto fail;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006377
6378out:
6379 wpabuf_free(M_buf);
6380 EC_KEY_free(X_ec);
6381 EC_POINT_free(M);
6382 EC_POINT_free(Qi);
6383 BN_clear_free(Mx);
6384 BN_clear_free(My);
6385 BN_CTX_free(bnctx);
6386 return msg;
6387fail:
6388 wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request");
6389 wpabuf_free(msg);
6390 msg = NULL;
6391 goto out;
6392}
6393
6394
Roshan Pius3a1667e2018-07-03 15:17:14 -07006395static void dpp_pkex_fail(struct dpp_pkex *pkex, const char *txt)
6396{
6397 wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
6398}
6399
6400
6401struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006402 const u8 *own_mac,
6403 const char *identifier,
6404 const char *code)
6405{
6406 struct dpp_pkex *pkex;
6407
Roshan Pius3a1667e2018-07-03 15:17:14 -07006408#ifdef CONFIG_TESTING_OPTIONS
6409 if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
6410 wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
6411 MAC2STR(dpp_pkex_own_mac_override));
6412 own_mac = dpp_pkex_own_mac_override;
6413 }
6414#endif /* CONFIG_TESTING_OPTIONS */
6415
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006416 pkex = os_zalloc(sizeof(*pkex));
6417 if (!pkex)
6418 return NULL;
Roshan Pius3a1667e2018-07-03 15:17:14 -07006419 pkex->msg_ctx = msg_ctx;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006420 pkex->initiator = 1;
6421 pkex->own_bi = bi;
6422 os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
6423 if (identifier) {
6424 pkex->identifier = os_strdup(identifier);
6425 if (!pkex->identifier)
6426 goto fail;
6427 }
6428 pkex->code = os_strdup(code);
6429 if (!pkex->code)
6430 goto fail;
6431 pkex->exchange_req = dpp_pkex_build_exchange_req(pkex);
6432 if (!pkex->exchange_req)
6433 goto fail;
6434 return pkex;
6435fail:
6436 dpp_pkex_free(pkex);
6437 return NULL;
6438}
6439
6440
Roshan Pius3a1667e2018-07-03 15:17:14 -07006441static struct wpabuf *
6442dpp_pkex_build_exchange_resp(struct dpp_pkex *pkex,
6443 enum dpp_status_error status,
6444 const BIGNUM *Nx, const BIGNUM *Ny)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006445{
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006446 struct wpabuf *msg = NULL;
6447 size_t attr_len;
Roshan Pius3a1667e2018-07-03 15:17:14 -07006448 const struct dpp_curve_params *curve = pkex->own_bi->curve;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006449
6450 /* Initiator -> Responder: DPP Status, [identifier,] N */
6451 attr_len = 4 + 1;
Roshan Pius3a1667e2018-07-03 15:17:14 -07006452 if (pkex->identifier)
6453 attr_len += 4 + os_strlen(pkex->identifier);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006454 attr_len += 4 + 2 * curve->prime_len;
6455 msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len);
6456 if (!msg)
6457 goto fail;
6458
Roshan Pius3a1667e2018-07-03 15:17:14 -07006459#ifdef CONFIG_TESTING_OPTIONS
6460 if (dpp_test == DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP) {
6461 wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
6462 goto skip_status;
6463 }
6464
6465 if (dpp_test == DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP) {
6466 wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
6467 status = 255;
6468 }
6469#endif /* CONFIG_TESTING_OPTIONS */
6470
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006471 /* DPP Status */
Roshan Pius3a1667e2018-07-03 15:17:14 -07006472 dpp_build_attr_status(msg, status);
6473
6474#ifdef CONFIG_TESTING_OPTIONS
6475skip_status:
6476#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006477
6478 /* Code Identifier attribute */
6479 if (pkex->identifier) {
6480 wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
6481 wpabuf_put_le16(msg, os_strlen(pkex->identifier));
6482 wpabuf_put_str(msg, pkex->identifier);
6483 }
6484
Roshan Pius3a1667e2018-07-03 15:17:14 -07006485 if (status != DPP_STATUS_OK)
6486 goto skip_encrypted_key;
6487
6488#ifdef CONFIG_TESTING_OPTIONS
6489 if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
6490 wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
6491 goto skip_encrypted_key;
6492 }
6493#endif /* CONFIG_TESTING_OPTIONS */
6494
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006495 /* N in Encrypted Key attribute */
6496 wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
6497 wpabuf_put_le16(msg, 2 * curve->prime_len);
6498
Roshan Pius3a1667e2018-07-03 15:17:14 -07006499#ifdef CONFIG_TESTING_OPTIONS
6500 if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
6501 wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
6502 if (dpp_test_gen_invalid_key(msg, curve) < 0)
6503 goto fail;
6504 goto skip_encrypted_key;
6505 }
6506#endif /* CONFIG_TESTING_OPTIONS */
6507
6508 if (dpp_bn2bin_pad(Nx, wpabuf_put(msg, curve->prime_len),
6509 curve->prime_len) < 0 ||
6510 dpp_bn2bin_pad(Nx, pkex->Nx, curve->prime_len) < 0 ||
6511 dpp_bn2bin_pad(Ny, wpabuf_put(msg, curve->prime_len),
6512 curve->prime_len) < 0)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006513 goto fail;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006514
Roshan Pius3a1667e2018-07-03 15:17:14 -07006515skip_encrypted_key:
6516 if (status == DPP_STATUS_BAD_GROUP) {
6517 /* Finite Cyclic Group attribute */
6518 wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
6519 wpabuf_put_le16(msg, 2);
6520 wpabuf_put_le16(msg, curve->ike_group);
6521 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006522
Roshan Pius3a1667e2018-07-03 15:17:14 -07006523 return msg;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006524fail:
Roshan Pius3a1667e2018-07-03 15:17:14 -07006525 wpabuf_free(msg);
6526 return NULL;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006527}
6528
6529
6530static int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
6531 const u8 *Mx, size_t Mx_len,
6532 const u8 *Nx, size_t Nx_len,
6533 const char *code,
6534 const u8 *Kx, size_t Kx_len,
6535 u8 *z, unsigned int hash_len)
6536{
6537 u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
6538 int res;
6539 u8 *info, *pos;
6540 size_t info_len;
6541
6542 /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
6543 */
6544
6545 /* HKDF-Extract(<>, IKM=K.x) */
6546 os_memset(salt, 0, hash_len);
6547 if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0)
6548 return -1;
6549 wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
6550 prk, hash_len);
6551 info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code);
6552 info = os_malloc(info_len);
6553 if (!info)
6554 return -1;
6555 pos = info;
6556 os_memcpy(pos, mac_init, ETH_ALEN);
6557 pos += ETH_ALEN;
6558 os_memcpy(pos, mac_resp, ETH_ALEN);
6559 pos += ETH_ALEN;
6560 os_memcpy(pos, Mx, Mx_len);
6561 pos += Mx_len;
6562 os_memcpy(pos, Nx, Nx_len);
6563 pos += Nx_len;
6564 os_memcpy(pos, code, os_strlen(code));
6565
6566 /* HKDF-Expand(PRK, info, L) */
6567 if (hash_len == 32)
6568 res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len,
6569 z, hash_len);
6570 else if (hash_len == 48)
6571 res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len,
6572 z, hash_len);
6573 else if (hash_len == 64)
6574 res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len,
6575 z, hash_len);
6576 else
6577 res = -1;
6578 os_free(info);
6579 os_memset(prk, 0, hash_len);
6580 if (res < 0)
6581 return -1;
6582
6583 wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)",
6584 z, hash_len);
6585 return 0;
6586}
6587
6588
Roshan Pius3a1667e2018-07-03 15:17:14 -07006589struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
6590 struct dpp_bootstrap_info *bi,
6591 const u8 *own_mac,
6592 const u8 *peer_mac,
6593 const char *identifier,
6594 const char *code,
6595 const u8 *buf, size_t len)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006596{
Roshan Pius3a1667e2018-07-03 15:17:14 -07006597 const u8 *attr_group, *attr_id, *attr_key;
6598 u16 attr_group_len, attr_id_len, attr_key_len;
6599 const struct dpp_curve_params *curve = bi->curve;
6600 u16 ike_group;
6601 struct dpp_pkex *pkex = NULL;
6602 EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006603 BN_CTX *bnctx = NULL;
Roshan Pius3a1667e2018-07-03 15:17:14 -07006604 const EC_GROUP *group;
6605 BIGNUM *Mx = NULL, *My = NULL;
6606 EC_KEY *Y_ec = NULL, *X_ec = NULL;;
6607 const EC_POINT *Y_point;
6608 BIGNUM *Nx = NULL, *Ny = NULL;
6609 u8 Kx[DPP_MAX_SHARED_SECRET_LEN];
6610 size_t Kx_len;
6611 int res;
6612 EVP_PKEY_CTX *ctx = NULL;
6613
6614 if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) {
6615 wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
6616 "PKEX counter t limit reached - ignore message");
6617 return NULL;
6618 }
6619
6620#ifdef CONFIG_TESTING_OPTIONS
6621 if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
6622 wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
6623 MAC2STR(dpp_pkex_peer_mac_override));
6624 peer_mac = dpp_pkex_peer_mac_override;
6625 }
6626 if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
6627 wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
6628 MAC2STR(dpp_pkex_own_mac_override));
6629 own_mac = dpp_pkex_own_mac_override;
6630 }
6631#endif /* CONFIG_TESTING_OPTIONS */
6632
6633 attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER,
6634 &attr_id_len);
6635 if (!attr_id && identifier) {
6636 wpa_printf(MSG_DEBUG,
6637 "DPP: No PKEX code identifier received, but expected one");
6638 return NULL;
6639 }
6640 if (attr_id && identifier &&
6641 (os_strlen(identifier) != attr_id_len ||
6642 os_memcmp(identifier, attr_id, attr_id_len) != 0)) {
6643 wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
6644 return NULL;
6645 }
6646
6647 attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
6648 &attr_group_len);
6649 if (!attr_group || attr_group_len != 2) {
6650 wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
6651 "Missing or invalid Finite Cyclic Group attribute");
6652 return NULL;
6653 }
6654 ike_group = WPA_GET_LE16(attr_group);
6655 if (ike_group != curve->ike_group) {
6656 wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
6657 "Mismatching PKEX curve: peer=%u own=%u",
6658 ike_group, curve->ike_group);
6659 pkex = os_zalloc(sizeof(*pkex));
6660 if (!pkex)
6661 goto fail;
6662 pkex->own_bi = bi;
6663 pkex->failed = 1;
6664 pkex->exchange_resp = dpp_pkex_build_exchange_resp(
6665 pkex, DPP_STATUS_BAD_GROUP, NULL, NULL);
6666 if (!pkex->exchange_resp)
6667 goto fail;
6668 return pkex;
6669 }
6670
6671 /* M in Encrypted Key attribute */
6672 attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY,
6673 &attr_key_len);
6674 if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 ||
6675 attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) {
6676 wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
6677 "Missing Encrypted Key attribute");
6678 return NULL;
6679 }
6680
6681 /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
6682 bnctx = BN_CTX_new();
6683 if (!bnctx)
6684 goto fail;
6685 Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx,
6686 &group);
6687 if (!Qi)
6688 goto fail;
6689
6690 /* X' = M - Qi */
6691 X = EC_POINT_new(group);
6692 M = EC_POINT_new(group);
6693 Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
6694 My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
6695 if (!X || !M || !Mx || !My ||
6696 EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 ||
6697 EC_POINT_is_at_infinity(group, M) ||
6698 !EC_POINT_is_on_curve(group, M, bnctx) ||
6699 EC_POINT_invert(group, Qi, bnctx) != 1 ||
6700 EC_POINT_add(group, X, M, Qi, bnctx) != 1 ||
6701 EC_POINT_is_at_infinity(group, X) ||
6702 !EC_POINT_is_on_curve(group, X, bnctx)) {
6703 wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
6704 "Invalid Encrypted Key value");
6705 bi->pkex_t++;
6706 goto fail;
6707 }
6708 dpp_debug_print_point("DPP: M", group, M);
6709 dpp_debug_print_point("DPP: X'", group, X);
6710
6711 pkex = os_zalloc(sizeof(*pkex));
6712 if (!pkex)
6713 goto fail;
6714 pkex->t = bi->pkex_t;
6715 pkex->msg_ctx = msg_ctx;
6716 pkex->own_bi = bi;
6717 os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
6718 os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
6719 if (identifier) {
6720 pkex->identifier = os_strdup(identifier);
6721 if (!pkex->identifier)
6722 goto fail;
6723 }
6724 pkex->code = os_strdup(code);
6725 if (!pkex->code)
6726 goto fail;
6727
6728 os_memcpy(pkex->Mx, attr_key, attr_key_len / 2);
6729
6730 X_ec = EC_KEY_new();
6731 if (!X_ec ||
6732 EC_KEY_set_group(X_ec, group) != 1 ||
6733 EC_KEY_set_public_key(X_ec, X) != 1)
6734 goto fail;
6735 pkex->x = EVP_PKEY_new();
6736 if (!pkex->x ||
6737 EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1)
6738 goto fail;
6739
6740 /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
6741 Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL);
6742 if (!Qr)
6743 goto fail;
6744
6745 /* Generate a random ephemeral keypair y/Y */
6746#ifdef CONFIG_TESTING_OPTIONS
6747 if (dpp_pkex_ephemeral_key_override_len) {
6748 const struct dpp_curve_params *tmp_curve;
6749
6750 wpa_printf(MSG_INFO,
6751 "DPP: TESTING - override ephemeral key y/Y");
6752 pkex->y = dpp_set_keypair(&tmp_curve,
6753 dpp_pkex_ephemeral_key_override,
6754 dpp_pkex_ephemeral_key_override_len);
6755 } else {
6756 pkex->y = dpp_gen_keypair(curve);
6757 }
6758#else /* CONFIG_TESTING_OPTIONS */
6759 pkex->y = dpp_gen_keypair(curve);
6760#endif /* CONFIG_TESTING_OPTIONS */
6761 if (!pkex->y)
6762 goto fail;
6763
6764 /* N = Y + Qr */
6765 Y_ec = EVP_PKEY_get1_EC_KEY(pkex->y);
6766 if (!Y_ec)
6767 goto fail;
6768 Y_point = EC_KEY_get0_public_key(Y_ec);
6769 if (!Y_point)
6770 goto fail;
6771 dpp_debug_print_point("DPP: Y", group, Y_point);
6772 N = EC_POINT_new(group);
6773 Nx = BN_new();
6774 Ny = BN_new();
6775 if (!N || !Nx || !Ny ||
6776 EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 ||
6777 EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1)
6778 goto fail;
6779 dpp_debug_print_point("DPP: N", group, N);
6780
6781 pkex->exchange_resp = dpp_pkex_build_exchange_resp(pkex, DPP_STATUS_OK,
6782 Nx, Ny);
6783 if (!pkex->exchange_resp)
6784 goto fail;
6785
6786 /* K = y * X' */
6787 ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
6788 if (!ctx ||
6789 EVP_PKEY_derive_init(ctx) != 1 ||
6790 EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 ||
6791 EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 ||
6792 Kx_len > DPP_MAX_SHARED_SECRET_LEN ||
6793 EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) {
6794 wpa_printf(MSG_ERROR,
6795 "DPP: Failed to derive ECDH shared secret: %s",
6796 ERR_error_string(ERR_get_error(), NULL));
6797 goto fail;
6798 }
6799
6800 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
6801 Kx, Kx_len);
6802
6803 /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
6804 */
6805 res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac,
6806 pkex->Mx, curve->prime_len,
6807 pkex->Nx, curve->prime_len, pkex->code,
6808 Kx, Kx_len, pkex->z, curve->hash_len);
6809 os_memset(Kx, 0, Kx_len);
6810 if (res < 0)
6811 goto fail;
6812
6813 pkex->exchange_done = 1;
6814
6815out:
6816 EVP_PKEY_CTX_free(ctx);
6817 BN_CTX_free(bnctx);
6818 EC_POINT_free(Qi);
6819 EC_POINT_free(Qr);
6820 BN_free(Mx);
6821 BN_free(My);
6822 BN_free(Nx);
6823 BN_free(Ny);
6824 EC_POINT_free(M);
6825 EC_POINT_free(N);
6826 EC_POINT_free(X);
6827 EC_KEY_free(X_ec);
6828 EC_KEY_free(Y_ec);
6829 return pkex;
6830fail:
6831 wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed");
6832 dpp_pkex_free(pkex);
6833 pkex = NULL;
6834 goto out;
6835}
6836
6837
6838static struct wpabuf *
6839dpp_pkex_build_commit_reveal_req(struct dpp_pkex *pkex,
6840 const struct wpabuf *A_pub, const u8 *u)
6841{
6842 const struct dpp_curve_params *curve = pkex->own_bi->curve;
6843 struct wpabuf *msg = NULL;
6844 size_t clear_len, attr_len;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006845 struct wpabuf *clear = NULL;
6846 u8 *wrapped;
Roshan Pius3a1667e2018-07-03 15:17:14 -07006847 u8 octet;
6848 const u8 *addr[2];
6849 size_t len[2];
6850
6851 /* {A, u, [bootstrapping info]}z */
6852 clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
6853 clear = wpabuf_alloc(clear_len);
6854 attr_len = 4 + clear_len + AES_BLOCK_SIZE;
6855#ifdef CONFIG_TESTING_OPTIONS
6856 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ)
6857 attr_len += 5;
6858#endif /* CONFIG_TESTING_OPTIONS */
6859 msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len);
6860 if (!clear || !msg)
6861 goto fail;
6862
6863#ifdef CONFIG_TESTING_OPTIONS
6864 if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ) {
6865 wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
6866 goto skip_bootstrap_key;
6867 }
6868 if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ) {
6869 wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
6870 wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
6871 wpabuf_put_le16(clear, 2 * curve->prime_len);
6872 if (dpp_test_gen_invalid_key(clear, curve) < 0)
6873 goto fail;
6874 goto skip_bootstrap_key;
6875 }
6876#endif /* CONFIG_TESTING_OPTIONS */
6877
6878 /* A in Bootstrap Key attribute */
6879 wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
6880 wpabuf_put_le16(clear, wpabuf_len(A_pub));
6881 wpabuf_put_buf(clear, A_pub);
6882
6883#ifdef CONFIG_TESTING_OPTIONS
6884skip_bootstrap_key:
6885 if (dpp_test == DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ) {
6886 wpa_printf(MSG_INFO, "DPP: TESTING - no I-Auth tag");
6887 goto skip_i_auth_tag;
6888 }
6889 if (dpp_test == DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ) {
6890 wpa_printf(MSG_INFO, "DPP: TESTING - I-Auth tag mismatch");
6891 wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
6892 wpabuf_put_le16(clear, curve->hash_len);
6893 wpabuf_put_data(clear, u, curve->hash_len - 1);
6894 wpabuf_put_u8(clear, u[curve->hash_len - 1] ^ 0x01);
6895 goto skip_i_auth_tag;
6896 }
6897#endif /* CONFIG_TESTING_OPTIONS */
6898
6899 /* u in I-Auth tag attribute */
6900 wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
6901 wpabuf_put_le16(clear, curve->hash_len);
6902 wpabuf_put_data(clear, u, curve->hash_len);
6903
6904#ifdef CONFIG_TESTING_OPTIONS
6905skip_i_auth_tag:
6906 if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ) {
6907 wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
6908 goto skip_wrapped_data;
6909 }
6910#endif /* CONFIG_TESTING_OPTIONS */
6911
6912 addr[0] = wpabuf_head_u8(msg) + 2;
6913 len[0] = DPP_HDR_LEN;
6914 octet = 0;
6915 addr[1] = &octet;
6916 len[1] = sizeof(octet);
6917 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
6918 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
6919
6920 wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
6921 wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
6922 wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
6923
6924 wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
6925 if (aes_siv_encrypt(pkex->z, curve->hash_len,
6926 wpabuf_head(clear), wpabuf_len(clear),
6927 2, addr, len, wrapped) < 0)
6928 goto fail;
6929 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
6930 wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
6931
6932#ifdef CONFIG_TESTING_OPTIONS
6933 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) {
6934 wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
6935 dpp_build_attr_status(msg, DPP_STATUS_OK);
6936 }
6937skip_wrapped_data:
6938#endif /* CONFIG_TESTING_OPTIONS */
6939
6940out:
6941 wpabuf_free(clear);
6942 return msg;
6943
6944fail:
6945 wpabuf_free(msg);
6946 msg = NULL;
6947 goto out;
6948}
6949
6950
6951struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
6952 const u8 *peer_mac,
6953 const u8 *buf, size_t buflen)
6954{
6955 const u8 *attr_status, *attr_id, *attr_key, *attr_group;
6956 u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len;
6957 const EC_GROUP *group;
6958 BN_CTX *bnctx = NULL;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006959 struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
6960 const struct dpp_curve_params *curve = pkex->own_bi->curve;
6961 EC_POINT *Qr = NULL, *Y = NULL, *N = NULL;
6962 BIGNUM *Nx = NULL, *Ny = NULL;
6963 EVP_PKEY_CTX *ctx = NULL;
6964 EC_KEY *Y_ec = NULL;
6965 size_t Jx_len, Kx_len;
6966 u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
6967 const u8 *addr[4];
6968 size_t len[4];
6969 u8 u[DPP_MAX_HASH_LEN];
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006970 int res;
6971
Roshan Pius3a1667e2018-07-03 15:17:14 -07006972 if (pkex->failed || pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
6973 return NULL;
6974
6975#ifdef CONFIG_TESTING_OPTIONS
6976 if (dpp_test == DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP) {
6977 wpa_printf(MSG_INFO,
6978 "DPP: TESTING - stop at PKEX Exchange Response");
6979 pkex->failed = 1;
6980 return NULL;
6981 }
6982
6983 if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
6984 wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
6985 MAC2STR(dpp_pkex_peer_mac_override));
6986 peer_mac = dpp_pkex_peer_mac_override;
6987 }
6988#endif /* CONFIG_TESTING_OPTIONS */
6989
6990 os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
6991
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006992 attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS,
6993 &attr_status_len);
6994 if (!attr_status || attr_status_len != 1) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07006995 dpp_pkex_fail(pkex, "No DPP Status attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07006996 return NULL;
6997 }
6998 wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]);
Roshan Pius3a1667e2018-07-03 15:17:14 -07006999
7000 if (attr_status[0] == DPP_STATUS_BAD_GROUP) {
7001 attr_group = dpp_get_attr(buf, buflen,
7002 DPP_ATTR_FINITE_CYCLIC_GROUP,
7003 &attr_group_len);
7004 if (attr_group && attr_group_len == 2) {
7005 wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
7006 "Peer indicated mismatching PKEX group - proposed %u",
7007 WPA_GET_LE16(attr_group));
7008 return NULL;
7009 }
7010 }
7011
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007012 if (attr_status[0] != DPP_STATUS_OK) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007013 dpp_pkex_fail(pkex, "PKEX failed (peer indicated failure)");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007014 return NULL;
7015 }
7016
7017 attr_id = dpp_get_attr(buf, buflen, DPP_ATTR_CODE_IDENTIFIER,
7018 &attr_id_len);
7019 if (!attr_id && pkex->identifier) {
7020 wpa_printf(MSG_DEBUG,
7021 "DPP: No PKEX code identifier received, but expected one");
7022 return NULL;
7023 }
7024 if (attr_id && pkex->identifier &&
7025 (os_strlen(pkex->identifier) != attr_id_len ||
7026 os_memcmp(pkex->identifier, attr_id, attr_id_len) != 0)) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007027 dpp_pkex_fail(pkex, "PKEX code identifier mismatch");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007028 return NULL;
7029 }
7030
7031 /* N in Encrypted Key attribute */
7032 attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY,
7033 &attr_key_len);
7034 if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007035 dpp_pkex_fail(pkex, "Missing Encrypted Key attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007036 return NULL;
7037 }
7038
7039 /* Qr = H(MAC-Responder | [identifier |] code) * Pr */
7040 bnctx = BN_CTX_new();
7041 if (!bnctx)
7042 goto fail;
7043 Qr = dpp_pkex_derive_Qr(curve, pkex->peer_mac, pkex->code,
7044 pkex->identifier, bnctx, &group);
7045 if (!Qr)
7046 goto fail;
7047
7048 /* Y' = N - Qr */
7049 Y = EC_POINT_new(group);
7050 N = EC_POINT_new(group);
7051 Nx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
7052 Ny = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
7053 if (!Y || !N || !Nx || !Ny ||
7054 EC_POINT_set_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1 ||
7055 EC_POINT_is_at_infinity(group, N) ||
7056 !EC_POINT_is_on_curve(group, N, bnctx) ||
7057 EC_POINT_invert(group, Qr, bnctx) != 1 ||
7058 EC_POINT_add(group, Y, N, Qr, bnctx) != 1 ||
7059 EC_POINT_is_at_infinity(group, Y) ||
Roshan Pius3a1667e2018-07-03 15:17:14 -07007060 !EC_POINT_is_on_curve(group, Y, bnctx)) {
7061 dpp_pkex_fail(pkex, "Invalid Encrypted Key value");
7062 pkex->t++;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007063 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07007064 }
7065 dpp_debug_print_point("DPP: N", group, N);
7066 dpp_debug_print_point("DPP: Y'", group, Y);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007067
7068 pkex->exchange_done = 1;
7069
7070 /* ECDH: J = a * Y’ */
7071 Y_ec = EC_KEY_new();
7072 if (!Y_ec ||
7073 EC_KEY_set_group(Y_ec, group) != 1 ||
7074 EC_KEY_set_public_key(Y_ec, Y) != 1)
7075 goto fail;
7076 pkex->y = EVP_PKEY_new();
7077 if (!pkex->y ||
7078 EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1)
7079 goto fail;
7080 ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL);
7081 if (!ctx ||
7082 EVP_PKEY_derive_init(ctx) != 1 ||
7083 EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 ||
7084 EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 ||
7085 Jx_len > DPP_MAX_SHARED_SECRET_LEN ||
7086 EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) {
7087 wpa_printf(MSG_ERROR,
7088 "DPP: Failed to derive ECDH shared secret: %s",
7089 ERR_error_string(ERR_get_error(), NULL));
7090 goto fail;
7091 }
7092
7093 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
7094 Jx, Jx_len);
7095
7096 /* u = HMAC(J.x, MAC-Initiator | A.x | Y’.x | X.x ) */
7097 A_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
7098 Y_pub = dpp_get_pubkey_point(pkex->y, 0);
7099 X_pub = dpp_get_pubkey_point(pkex->x, 0);
7100 if (!A_pub || !Y_pub || !X_pub)
7101 goto fail;
7102 addr[0] = pkex->own_mac;
7103 len[0] = ETH_ALEN;
7104 addr[1] = wpabuf_head(A_pub);
7105 len[1] = wpabuf_len(A_pub) / 2;
7106 addr[2] = wpabuf_head(Y_pub);
7107 len[2] = wpabuf_len(Y_pub) / 2;
7108 addr[3] = wpabuf_head(X_pub);
7109 len[3] = wpabuf_len(X_pub) / 2;
7110 if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
7111 goto fail;
7112 wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len);
7113
7114 /* K = x * Y’ */
7115 EVP_PKEY_CTX_free(ctx);
7116 ctx = EVP_PKEY_CTX_new(pkex->x, NULL);
7117 if (!ctx ||
7118 EVP_PKEY_derive_init(ctx) != 1 ||
7119 EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 ||
7120 EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 ||
7121 Kx_len > DPP_MAX_SHARED_SECRET_LEN ||
7122 EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) {
7123 wpa_printf(MSG_ERROR,
7124 "DPP: Failed to derive ECDH shared secret: %s",
7125 ERR_error_string(ERR_get_error(), NULL));
7126 goto fail;
7127 }
7128
7129 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
7130 Kx, Kx_len);
7131
7132 /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
7133 */
7134 res = dpp_pkex_derive_z(pkex->own_mac, pkex->peer_mac,
7135 pkex->Mx, curve->prime_len,
7136 attr_key /* N.x */, attr_key_len / 2,
7137 pkex->code, Kx, Kx_len,
7138 pkex->z, curve->hash_len);
7139 os_memset(Kx, 0, Kx_len);
7140 if (res < 0)
7141 goto fail;
7142
Roshan Pius3a1667e2018-07-03 15:17:14 -07007143 msg = dpp_pkex_build_commit_reveal_req(pkex, A_pub, u);
7144 if (!msg)
7145 goto fail;
7146
7147out:
7148 wpabuf_free(A_pub);
7149 wpabuf_free(X_pub);
7150 wpabuf_free(Y_pub);
7151 EC_POINT_free(Qr);
7152 EC_POINT_free(Y);
7153 EC_POINT_free(N);
7154 BN_free(Nx);
7155 BN_free(Ny);
7156 EC_KEY_free(Y_ec);
7157 EVP_PKEY_CTX_free(ctx);
7158 BN_CTX_free(bnctx);
7159 return msg;
7160fail:
7161 wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed");
7162 goto out;
7163}
7164
7165
7166static struct wpabuf *
7167dpp_pkex_build_commit_reveal_resp(struct dpp_pkex *pkex,
7168 const struct wpabuf *B_pub, const u8 *v)
7169{
7170 const struct dpp_curve_params *curve = pkex->own_bi->curve;
7171 struct wpabuf *msg = NULL;
7172 const u8 *addr[2];
7173 size_t len[2];
7174 u8 octet;
7175 u8 *wrapped;
7176 struct wpabuf *clear = NULL;
7177 size_t clear_len, attr_len;
7178
7179 /* {B, v [bootstrapping info]}z */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007180 clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
7181 clear = wpabuf_alloc(clear_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07007182 attr_len = 4 + clear_len + AES_BLOCK_SIZE;
7183#ifdef CONFIG_TESTING_OPTIONS
7184 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP)
7185 attr_len += 5;
7186#endif /* CONFIG_TESTING_OPTIONS */
7187 msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007188 if (!clear || !msg)
7189 goto fail;
7190
Roshan Pius3a1667e2018-07-03 15:17:14 -07007191#ifdef CONFIG_TESTING_OPTIONS
7192 if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP) {
7193 wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
7194 goto skip_bootstrap_key;
7195 }
7196 if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP) {
7197 wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
7198 wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
7199 wpabuf_put_le16(clear, 2 * curve->prime_len);
7200 if (dpp_test_gen_invalid_key(clear, curve) < 0)
7201 goto fail;
7202 goto skip_bootstrap_key;
7203 }
7204#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007205
Roshan Pius3a1667e2018-07-03 15:17:14 -07007206 /* B in Bootstrap Key attribute */
7207 wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
7208 wpabuf_put_le16(clear, wpabuf_len(B_pub));
7209 wpabuf_put_buf(clear, B_pub);
7210
7211#ifdef CONFIG_TESTING_OPTIONS
7212skip_bootstrap_key:
7213 if (dpp_test == DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP) {
7214 wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth tag");
7215 goto skip_r_auth_tag;
7216 }
7217 if (dpp_test == DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP) {
7218 wpa_printf(MSG_INFO, "DPP: TESTING - R-Auth tag mismatch");
7219 wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
7220 wpabuf_put_le16(clear, curve->hash_len);
7221 wpabuf_put_data(clear, v, curve->hash_len - 1);
7222 wpabuf_put_u8(clear, v[curve->hash_len - 1] ^ 0x01);
7223 goto skip_r_auth_tag;
7224 }
7225#endif /* CONFIG_TESTING_OPTIONS */
7226
7227 /* v in R-Auth tag attribute */
7228 wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007229 wpabuf_put_le16(clear, curve->hash_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07007230 wpabuf_put_data(clear, v, curve->hash_len);
7231
7232#ifdef CONFIG_TESTING_OPTIONS
7233skip_r_auth_tag:
7234 if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP) {
7235 wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
7236 goto skip_wrapped_data;
7237 }
7238#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007239
7240 addr[0] = wpabuf_head_u8(msg) + 2;
7241 len[0] = DPP_HDR_LEN;
Roshan Pius3a1667e2018-07-03 15:17:14 -07007242 octet = 1;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007243 addr[1] = &octet;
7244 len[1] = sizeof(octet);
7245 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
7246 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
7247
7248 wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
7249 wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
7250 wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
7251
7252 wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
7253 if (aes_siv_encrypt(pkex->z, curve->hash_len,
7254 wpabuf_head(clear), wpabuf_len(clear),
7255 2, addr, len, wrapped) < 0)
7256 goto fail;
7257 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
7258 wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
7259
Roshan Pius3a1667e2018-07-03 15:17:14 -07007260#ifdef CONFIG_TESTING_OPTIONS
7261 if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP) {
7262 wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
7263 dpp_build_attr_status(msg, DPP_STATUS_OK);
7264 }
7265skip_wrapped_data:
7266#endif /* CONFIG_TESTING_OPTIONS */
7267
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007268out:
7269 wpabuf_free(clear);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007270 return msg;
Roshan Pius3a1667e2018-07-03 15:17:14 -07007271
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007272fail:
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007273 wpabuf_free(msg);
7274 msg = NULL;
7275 goto out;
7276}
7277
7278
7279struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
7280 const u8 *hdr,
7281 const u8 *buf, size_t buflen)
7282{
7283 const struct dpp_curve_params *curve = pkex->own_bi->curve;
Roshan Pius3a1667e2018-07-03 15:17:14 -07007284 EVP_PKEY_CTX *ctx = NULL;
7285 size_t Jx_len, Lx_len;
7286 u8 Jx[DPP_MAX_SHARED_SECRET_LEN];
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007287 u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
7288 const u8 *wrapped_data, *b_key, *peer_u;
7289 u16 wrapped_data_len, b_key_len, peer_u_len = 0;
7290 const u8 *addr[4];
7291 size_t len[4];
7292 u8 octet;
7293 u8 *unwrapped = NULL;
7294 size_t unwrapped_len = 0;
7295 struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
7296 struct wpabuf *B_pub = NULL;
7297 u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN];
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007298
Roshan Pius3a1667e2018-07-03 15:17:14 -07007299#ifdef CONFIG_TESTING_OPTIONS
7300 if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_REQ) {
7301 wpa_printf(MSG_INFO,
7302 "DPP: TESTING - stop at PKEX CR Request");
7303 pkex->failed = 1;
7304 return NULL;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007305 }
Roshan Pius3a1667e2018-07-03 15:17:14 -07007306#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007307
Roshan Pius3a1667e2018-07-03 15:17:14 -07007308 if (!pkex->exchange_done || pkex->failed ||
7309 pkex->t >= PKEX_COUNTER_T_LIMIT || pkex->initiator)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007310 goto fail;
7311
7312 wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
7313 &wrapped_data_len);
7314 if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007315 dpp_pkex_fail(pkex,
7316 "Missing or invalid required Wrapped Data attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007317 goto fail;
7318 }
7319
7320 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
7321 wrapped_data, wrapped_data_len);
7322 unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
7323 unwrapped = os_malloc(unwrapped_len);
7324 if (!unwrapped)
7325 goto fail;
7326
7327 addr[0] = hdr;
7328 len[0] = DPP_HDR_LEN;
7329 octet = 0;
7330 addr[1] = &octet;
7331 len[1] = sizeof(octet);
7332 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
7333 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
7334
7335 if (aes_siv_decrypt(pkex->z, curve->hash_len,
7336 wrapped_data, wrapped_data_len,
7337 2, addr, len, unwrapped) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007338 dpp_pkex_fail(pkex,
7339 "AES-SIV decryption failed - possible PKEX code mismatch");
7340 pkex->failed = 1;
7341 pkex->t++;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007342 goto fail;
7343 }
7344 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
7345 unwrapped, unwrapped_len);
7346
7347 if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007348 dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007349 goto fail;
7350 }
7351
7352 b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
7353 &b_key_len);
7354 if (!b_key || b_key_len != 2 * curve->prime_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007355 dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007356 goto fail;
7357 }
7358 pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
7359 b_key_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07007360 if (!pkex->peer_bootstrap_key) {
7361 dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007362 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07007363 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007364 dpp_debug_print_key("DPP: Peer bootstrap public key",
7365 pkex->peer_bootstrap_key);
7366
7367 /* ECDH: J' = y * A' */
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007368 ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
7369 if (!ctx ||
7370 EVP_PKEY_derive_init(ctx) != 1 ||
7371 EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 ||
7372 EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 ||
7373 Jx_len > DPP_MAX_SHARED_SECRET_LEN ||
7374 EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) {
7375 wpa_printf(MSG_ERROR,
7376 "DPP: Failed to derive ECDH shared secret: %s",
7377 ERR_error_string(ERR_get_error(), NULL));
7378 goto fail;
7379 }
7380
7381 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
7382 Jx, Jx_len);
7383
7384 /* u' = HMAC(J'.x, MAC-Initiator | A'.x | Y.x | X'.x) */
7385 A_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
7386 Y_pub = dpp_get_pubkey_point(pkex->y, 0);
7387 X_pub = dpp_get_pubkey_point(pkex->x, 0);
7388 if (!A_pub || !Y_pub || !X_pub)
7389 goto fail;
7390 addr[0] = pkex->peer_mac;
7391 len[0] = ETH_ALEN;
7392 addr[1] = wpabuf_head(A_pub);
7393 len[1] = wpabuf_len(A_pub) / 2;
7394 addr[2] = wpabuf_head(Y_pub);
7395 len[2] = wpabuf_len(Y_pub) / 2;
7396 addr[3] = wpabuf_head(X_pub);
7397 len[3] = wpabuf_len(X_pub) / 2;
7398 if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
7399 goto fail;
7400
7401 peer_u = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
7402 &peer_u_len);
7403 if (!peer_u || peer_u_len != curve->hash_len ||
7404 os_memcmp(peer_u, u, curve->hash_len) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007405 dpp_pkex_fail(pkex, "No valid u (I-Auth tag) found");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007406 wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'",
7407 u, curve->hash_len);
7408 wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07007409 pkex->t++;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007410 goto fail;
7411 }
7412 wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received");
7413
7414 /* ECDH: L = b * X' */
7415 EVP_PKEY_CTX_free(ctx);
7416 ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL);
7417 if (!ctx ||
7418 EVP_PKEY_derive_init(ctx) != 1 ||
7419 EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 ||
7420 EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 ||
7421 Lx_len > DPP_MAX_SHARED_SECRET_LEN ||
7422 EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) {
7423 wpa_printf(MSG_ERROR,
7424 "DPP: Failed to derive ECDH shared secret: %s",
7425 ERR_error_string(ERR_get_error(), NULL));
7426 goto fail;
7427 }
7428
7429 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
7430 Lx, Lx_len);
7431
7432 /* v = HMAC(L.x, MAC-Responder | B.x | X'.x | Y.x) */
7433 B_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
7434 if (!B_pub)
7435 goto fail;
7436 addr[0] = pkex->own_mac;
7437 len[0] = ETH_ALEN;
7438 addr[1] = wpabuf_head(B_pub);
7439 len[1] = wpabuf_len(B_pub) / 2;
7440 addr[2] = wpabuf_head(X_pub);
7441 len[2] = wpabuf_len(X_pub) / 2;
7442 addr[3] = wpabuf_head(Y_pub);
7443 len[3] = wpabuf_len(Y_pub) / 2;
7444 if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
7445 goto fail;
7446 wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len);
7447
Roshan Pius3a1667e2018-07-03 15:17:14 -07007448 msg = dpp_pkex_build_commit_reveal_resp(pkex, B_pub, v);
7449 if (!msg)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007450 goto fail;
7451
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007452out:
7453 EVP_PKEY_CTX_free(ctx);
7454 os_free(unwrapped);
7455 wpabuf_free(A_pub);
7456 wpabuf_free(B_pub);
7457 wpabuf_free(X_pub);
7458 wpabuf_free(Y_pub);
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007459 return msg;
7460fail:
Roshan Pius3a1667e2018-07-03 15:17:14 -07007461 wpa_printf(MSG_DEBUG,
7462 "DPP: PKEX Commit-Reveal Request processing failed");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007463 goto out;
7464}
7465
7466
7467int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
7468 const u8 *buf, size_t buflen)
7469{
7470 const struct dpp_curve_params *curve = pkex->own_bi->curve;
7471 const u8 *wrapped_data, *b_key, *peer_v;
7472 u16 wrapped_data_len, b_key_len, peer_v_len = 0;
7473 const u8 *addr[4];
7474 size_t len[4];
7475 u8 octet;
7476 u8 *unwrapped = NULL;
7477 size_t unwrapped_len = 0;
7478 int ret = -1;
7479 u8 v[DPP_MAX_HASH_LEN];
7480 size_t Lx_len;
7481 u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
7482 EVP_PKEY_CTX *ctx = NULL;
7483 struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
7484
Roshan Pius3a1667e2018-07-03 15:17:14 -07007485#ifdef CONFIG_TESTING_OPTIONS
7486 if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_RESP) {
7487 wpa_printf(MSG_INFO,
7488 "DPP: TESTING - stop at PKEX CR Response");
7489 pkex->failed = 1;
7490 goto fail;
7491 }
7492#endif /* CONFIG_TESTING_OPTIONS */
7493
7494 if (!pkex->exchange_done || pkex->failed ||
7495 pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
7496 goto fail;
7497
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007498 wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
7499 &wrapped_data_len);
7500 if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007501 dpp_pkex_fail(pkex,
7502 "Missing or invalid required Wrapped Data attribute");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007503 goto fail;
7504 }
7505
7506 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
7507 wrapped_data, wrapped_data_len);
7508 unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
7509 unwrapped = os_malloc(unwrapped_len);
7510 if (!unwrapped)
7511 goto fail;
7512
7513 addr[0] = hdr;
7514 len[0] = DPP_HDR_LEN;
7515 octet = 1;
7516 addr[1] = &octet;
7517 len[1] = sizeof(octet);
7518 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
7519 wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
7520
7521 if (aes_siv_decrypt(pkex->z, curve->hash_len,
7522 wrapped_data, wrapped_data_len,
7523 2, addr, len, unwrapped) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007524 dpp_pkex_fail(pkex,
7525 "AES-SIV decryption failed - possible PKEX code mismatch");
7526 pkex->t++;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007527 goto fail;
7528 }
7529 wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
7530 unwrapped, unwrapped_len);
7531
7532 if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007533 dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007534 goto fail;
7535 }
7536
7537 b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
7538 &b_key_len);
7539 if (!b_key || b_key_len != 2 * curve->prime_len) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007540 dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007541 goto fail;
7542 }
7543 pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
7544 b_key_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07007545 if (!pkex->peer_bootstrap_key) {
7546 dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007547 goto fail;
Roshan Pius3a1667e2018-07-03 15:17:14 -07007548 }
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007549 dpp_debug_print_key("DPP: Peer bootstrap public key",
7550 pkex->peer_bootstrap_key);
7551
7552 /* ECDH: L' = x * B' */
7553 ctx = EVP_PKEY_CTX_new(pkex->x, NULL);
7554 if (!ctx ||
7555 EVP_PKEY_derive_init(ctx) != 1 ||
7556 EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 ||
7557 EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 ||
7558 Lx_len > DPP_MAX_SHARED_SECRET_LEN ||
7559 EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) {
7560 wpa_printf(MSG_ERROR,
7561 "DPP: Failed to derive ECDH shared secret: %s",
7562 ERR_error_string(ERR_get_error(), NULL));
7563 goto fail;
7564 }
7565
7566 wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
7567 Lx, Lx_len);
7568
7569 /* v' = HMAC(L.x, MAC-Responder | B'.x | X.x | Y'.x) */
7570 B_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
7571 X_pub = dpp_get_pubkey_point(pkex->x, 0);
7572 Y_pub = dpp_get_pubkey_point(pkex->y, 0);
7573 if (!B_pub || !X_pub || !Y_pub)
7574 goto fail;
7575 addr[0] = pkex->peer_mac;
7576 len[0] = ETH_ALEN;
7577 addr[1] = wpabuf_head(B_pub);
7578 len[1] = wpabuf_len(B_pub) / 2;
7579 addr[2] = wpabuf_head(X_pub);
7580 len[2] = wpabuf_len(X_pub) / 2;
7581 addr[3] = wpabuf_head(Y_pub);
7582 len[3] = wpabuf_len(Y_pub) / 2;
7583 if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
7584 goto fail;
7585
7586 peer_v = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_AUTH_TAG,
7587 &peer_v_len);
7588 if (!peer_v || peer_v_len != curve->hash_len ||
7589 os_memcmp(peer_v, v, curve->hash_len) != 0) {
Roshan Pius3a1667e2018-07-03 15:17:14 -07007590 dpp_pkex_fail(pkex, "No valid v (R-Auth tag) found");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007591 wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'",
7592 v, curve->hash_len);
7593 wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len);
Roshan Pius3a1667e2018-07-03 15:17:14 -07007594 pkex->t++;
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07007595 goto fail;
7596 }
7597 wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received");
7598
7599 ret = 0;
7600out:
7601 wpabuf_free(B_pub);
7602 wpabuf_free(X_pub);
7603 wpabuf_free(Y_pub);
7604 EVP_PKEY_CTX_free(ctx);
7605 os_free(unwrapped);
7606 return ret;
7607fail:
7608 goto out;
7609}
7610
7611
7612void dpp_pkex_free(struct dpp_pkex *pkex)
7613{
7614 if (!pkex)
7615 return;
7616
7617 os_free(pkex->identifier);
7618 os_free(pkex->code);
7619 EVP_PKEY_free(pkex->x);
7620 EVP_PKEY_free(pkex->y);
7621 EVP_PKEY_free(pkex->peer_bootstrap_key);
7622 wpabuf_free(pkex->exchange_req);
7623 wpabuf_free(pkex->exchange_resp);
7624 os_free(pkex);
7625}
Roshan Pius3a1667e2018-07-03 15:17:14 -07007626
7627
7628#ifdef CONFIG_TESTING_OPTIONS
7629char * dpp_corrupt_connector_signature(const char *connector)
7630{
7631 char *tmp, *pos, *signed3 = NULL;
7632 unsigned char *signature = NULL;
7633 size_t signature_len = 0, signed3_len;
7634
7635 tmp = os_zalloc(os_strlen(connector) + 5);
7636 if (!tmp)
7637 goto fail;
7638 os_memcpy(tmp, connector, os_strlen(connector));
7639
7640 pos = os_strchr(tmp, '.');
7641 if (!pos)
7642 goto fail;
7643
7644 pos = os_strchr(pos + 1, '.');
7645 if (!pos)
7646 goto fail;
7647 pos++;
7648
7649 wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s",
7650 pos);
7651 signature = base64_url_decode((const unsigned char *) pos,
7652 os_strlen(pos), &signature_len);
7653 if (!signature || signature_len == 0)
7654 goto fail;
7655 wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature",
7656 signature, signature_len);
7657 signature[signature_len - 1] ^= 0x01;
7658 wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature",
7659 signature, signature_len);
7660 signed3 = (char *) base64_url_encode(signature, signature_len,
7661 &signed3_len, 0);
7662 if (!signed3)
7663 goto fail;
7664 os_memcpy(pos, signed3, signed3_len);
7665 pos[signed3_len] = '\0';
7666 wpa_printf(MSG_DEBUG, "DPP: Corrupted base64url encoded signature: %s",
7667 pos);
7668
7669out:
7670 os_free(signature);
7671 os_free(signed3);
7672 return tmp;
7673fail:
7674 os_free(tmp);
7675 tmp = NULL;
7676 goto out;
7677}
7678#endif /* CONFIG_TESTING_OPTIONS */