Skip to contentSkip to conversation

eap-wsc: Zero a temporary buffer before freeing

PATCH Applied Andrew Zaborowski 2 participants 9 messages Aug 13, 2020 – Aug 14, 2020 Kbuild

lore
Share & tools
lore
#0 lore raw

PATCH 10/16 eap-wsc: Zero a temporary buffer before freeing

---
 src/eap-wsc.c | 2 ++
 1 file changed, 2 insertions(+)
src/eap-wsc.c +2 -0
index ccb52979..ca915471 100644
@@ -1157,11 +1157,13 @@ static bool load_hexencoded(struct l_settings *settings, const char *key,
 		return false;
 
 	if (decoded_len != len) {
+		explicit_bzero(decoded, decoded_len);
 		l_free(decoded);
 		return false;
 	}
 
 	memcpy(to, decoded, len);
+	explicit_bzero(decoded, decoded_len);
 	l_free(decoded);
 
 	return true;
Signature · 1 line
2.25.1
#1 lore raw

PATCH 11/16 eap-wsc: Registrar mode settings loading

Alongside the current EAP-WSC enrollee side support, add the initial part of registrar side. In the same file, register a new method with the name string of "WSC-R". In this patch only the load_settings method is added. validate_identity and handle_response are added in later patches.

---
 src/eap-wsc.c | 433 +++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 325 insertions(+), 108 deletions(-)
src/eap-wsc.c +325 -108
index ca915471..2770c87a 100644
@@ -57,28 +57,40 @@ enum wsc_flag {
 };
 
 enum state {
+	/* Enrollee states */
 	STATE_EXPECT_START = 0,
 	STATE_EXPECT_M2,
 	STATE_EXPECT_M4,
 	STATE_EXPECT_M6,
 	STATE_EXPECT_M8,
 	STATE_FINISHED,
+	/* Registrar states */
+	STATE_EXPECT_IDENTITY,
+	STATE_EXPECT_M1,
+	STATE_EXPECT_M3,
+	STATE_EXPECT_M5,
+	STATE_EXPECT_M7,
+	STATE_EXPECT_DONE,
 };
 
 static struct l_key *dh5_generator;
 static struct l_key *dh5_prime;
 
 struct eap_wsc_state {
+	bool registrar;
 	struct wsc_m1 *m1;
 	struct wsc_m2 *m2;
+	struct wsc_credential wpa2_cred;
+	struct wsc_credential open_cred;
 	uint8_t *sent_pdu;
 	size_t sent_len;
 	struct l_key *private;
 	char *device_password;
-	uint8_t e_snonce1[16];
-	uint8_t e_snonce2[16];
+	uint8_t local_snonce1[16];
+	uint8_t local_snonce2[16];
 	uint8_t iv1[16];
 	uint8_t iv2[16];
+	uint8_t iv3[16];
 	uint8_t psk1[16];
 	uint8_t psk2[16];
 	uint8_t r_hash2[32];
@@ -305,10 +317,12 @@ static void eap_wsc_free(struct eap_state *eap)
 	l_free(wsc->m1);
 	l_free(wsc->m2);
 
-	explicit_bzero(wsc->e_snonce1, 16);
-	explicit_bzero(wsc->e_snonce2, 16);
+	explicit_bzero(wsc->local_snonce1, 16);
+	explicit_bzero(wsc->local_snonce2, 16);
 	explicit_bzero(wsc->psk1, 16);
 	explicit_bzero(wsc->psk2, 16);
+	explicit_bzero(&wsc->wpa2_cred, sizeof(struct wsc_credential));
+	explicit_bzero(&wsc->open_cred, sizeof(struct wsc_credential));
 
 	l_free(wsc);
 }
@@ -541,9 +555,9 @@ static void eap_wsc_send_m7(struct eap_state *eap,
 	size_t encrypted_len;
 	bool r;
 
-	memcpy(m7es.e_snonce2, wsc->e_snonce2, sizeof(wsc->e_snonce2));
+	memcpy(m7es.e_snonce2, wsc->local_snonce2, sizeof(wsc->local_snonce2));
 	pdu = wsc_build_m7_encrypted_settings(&m7es, &pdu_len);
-	explicit_bzero(m7es.e_snonce2, sizeof(wsc->e_snonce2));
+	explicit_bzero(m7es.e_snonce2, sizeof(wsc->local_snonce2));
 	if (!pdu)
 		return;
 
@@ -642,9 +656,9 @@ static void eap_wsc_send_m5(struct eap_state *eap,
 	size_t encrypted_len;
 	bool r;
 
-	memcpy(m5es.e_snonce1, wsc->e_snonce1, sizeof(wsc->e_snonce1));
+	memcpy(m5es.e_snonce1, wsc->local_snonce1, sizeof(wsc->local_snonce1));
 	pdu = wsc_build_m5_encrypted_settings(&m5es, &pdu_len);
-	explicit_bzero(m5es.e_snonce1, sizeof(wsc->e_snonce1));
+	explicit_bzero(m5es.e_snonce1, sizeof(wsc->local_snonce1));
 	if (!pdu)
 		return;
 
@@ -773,8 +787,8 @@ static void eap_wsc_send_m3(struct eap_state *eap,
 	 * E-Hash1 = HMACAuthKey(E-S1 || PSK1 || PKE || PKR)
 	 * E-Hash2 = HMACAuthKey(E-S2 || PSK2 || PKE || PKR)
 	 */
-	iov[0].iov_base = wsc->e_snonce1;
-	iov[0].iov_len = sizeof(wsc->e_snonce1);
+	iov[0].iov_base = wsc->local_snonce1;
+	iov[0].iov_len = sizeof(wsc->local_snonce1);
 	iov[1].iov_base = wsc->psk1;
 	iov[1].iov_len = sizeof(wsc->psk1);
 	iov[2].iov_base = wsc->m1->public_key;
@@ -785,8 +799,8 @@ static void eap_wsc_send_m3(struct eap_state *eap,
 	l_checksum_get_digest(wsc->hmac_auth_key,
 					m3.e_hash1, sizeof(m3.e_hash1));
 
-	iov[0].iov_base = wsc->e_snonce2;
-	iov[0].iov_len = sizeof(wsc->e_snonce2);
+	iov[0].iov_base = wsc->local_snonce2;
+	iov[0].iov_len = sizeof(wsc->local_snonce2);
 	iov[1].iov_base = wsc->psk2;
 	iov[1].iov_len = sizeof(wsc->psk2);
 	l_checksum_updatev(wsc->hmac_auth_key, iov, 4);
@@ -1211,129 +1225,165 @@ static bool load_constrained_string(struct l_settings *settings,
 	return true;
 }
 
-static bool eap_wsc_load_settings(struct eap_state *eap,
-					struct l_settings *settings,
-					const char *prefix)
+static bool load_device_data(struct l_settings *settings,
+				char *manufacturer,
+				char *model_name,
+				char *model_number,
+				char *serial_number,
+				struct wsc_primary_device_type *dev_type,
+				char *device_name,
+				uint8_t *rf_bands,
+				uint32_t *os_version)
 {
-	struct eap_wsc_state *wsc;
-	const char *v;
-	uint8_t private_key[192];
-	size_t len;
+	struct wsc_m1 *m1;
 	unsigned int u32;
 
-	wsc = l_new(struct eap_wsc_state, 1);
-
-	wsc->m1 = l_new(struct wsc_m1, 1);
-	wsc->m1->version2 = true;
-
-	v = l_settings_get_value(settings, "WSC", "EnrolleeMAC");
-	if (!v)
-		goto err;
-
-	if (!util_string_to_address(v, wsc->m1->addr))
-		goto err;
-
-	if (!wsc_uuid_from_addr(wsc->m1->addr, wsc->m1->uuid_e))
-		goto err;
-
-	if (!load_hexencoded(settings, "EnrolleeNonce",
-						wsc->m1->enrollee_nonce, 16))
-		l_getrandom(wsc->m1->enrollee_nonce, 16);
-
-	if (load_hexencoded(settings, "PrivateKey", private_key, 192)) {
-		if (!l_key_validate_dh_payload(private_key, 192,
-						crypto_dh5_prime,
-						crypto_dh5_prime_size)) {
-			explicit_bzero(private_key, 192);
-			goto err;
-		}
-
-		wsc->private = l_key_new(L_KEY_RAW, private_key, 192);
-		explicit_bzero(private_key, 192);
-	} else
-		wsc->private = l_key_generate_dh_private(crypto_dh5_prime,
-							crypto_dh5_prime_size);
-
-	if (!wsc->private)
-		goto err;
-
-	len = sizeof(wsc->m1->public_key);
-	if (!l_key_compute_dh_public(dh5_generator, wsc->private, dh5_prime,
-						wsc->m1->public_key, &len))
-		goto err;
-
-	if (len != sizeof(wsc->m1->public_key))
-		goto err;
-
-	wsc->m1->auth_type_flags = WSC_AUTHENTICATION_TYPE_WPA2_PERSONAL |
-					WSC_AUTHENTICATION_TYPE_WPA_PERSONAL |
-					WSC_AUTHENTICATION_TYPE_OPEN;
-	wsc->m1->encryption_type_flags = WSC_ENCRYPTION_TYPE_NONE |
-						WSC_ENCRYPTION_TYPE_AES_TKIP;
-	wsc->m1->connection_type_flags = WSC_CONNECTION_TYPE_ESS;
-
-	if (!l_settings_get_uint(settings, "WSC",
-						"ConfigurationMethods", &u32))
-		u32 = WSC_CONFIGURATION_METHOD_VIRTUAL_DISPLAY_PIN;
-
-	wsc->m1->config_methods = u32;
-	wsc->m1->state = WSC_STATE_NOT_CONFIGURED;
-
 	if (!load_constrained_string(settings, "Manufacturer",
-			wsc->m1->manufacturer, sizeof(wsc->m1->manufacturer)))
-		strcpy(wsc->m1->manufacturer, " ");
+				manufacturer, sizeof(m1->manufacturer)))
+		strcpy(manufacturer, " ");
 
 	if (!load_constrained_string(settings, "ModelName",
-			wsc->m1->model_name, sizeof(wsc->m1->model_name)))
-		strcpy(wsc->m1->model_name, " ");
+				model_name, sizeof(m1->model_name)))
+		strcpy(model_name, " ");
 
 	if (!load_constrained_string(settings, "ModelNumber",
-			wsc->m1->model_number, sizeof(wsc->m1->model_number)))
-		strcpy(wsc->m1->model_number, " ");
+				model_number, sizeof(m1->model_number)))
+		strcpy(model_number, " ");
 
 	if (!load_constrained_string(settings, "SerialNumber",
-			wsc->m1->serial_number, sizeof(wsc->m1->serial_number)))
-		strcpy(wsc->m1->serial_number, " ");
+				serial_number, sizeof(m1->serial_number)))
+		strcpy(serial_number, " ");
 
-	if (!load_primary_device_type(settings,
-					&wsc->m1->primary_device_type)) {
+	if (!load_primary_device_type(settings, dev_type)) {
 		/* Make ourselves a WFA standard PC by default */
-		wsc->m1->primary_device_type.category = 1;
-		memcpy(wsc->m1->primary_device_type.oui, wsc_wfa_oui, 3);
-		wsc->m1->primary_device_type.oui_type = 0x04;
-		wsc->m1->primary_device_type.subcategory = 1;
+		dev_type->category = 1;
+		memcpy(dev_type->oui, wsc_wfa_oui, 3);
+		dev_type->oui_type = 0x04;
+		dev_type->subcategory = 1;
 	}
 
 	if (!load_constrained_string(settings, "DeviceName",
-			wsc->m1->device_name, sizeof(wsc->m1->device_name)))
-		strcpy(wsc->m1->device_name, " ");
+				device_name, sizeof(m1->device_name)))
+		strcpy(device_name, " ");
 
 	if (!l_settings_get_uint(settings, "WSC", "RFBand", &u32))
-		goto err;
+		return false;
 
 	switch (u32) {
 	case WSC_RF_BAND_2_4_GHZ:
 	case WSC_RF_BAND_5_0_GHZ:
 	case WSC_RF_BAND_60_GHZ:
-		wsc->m1->rf_bands = u32;
+		*rf_bands = u32;
 		break;
 	default:
-		goto err;
+		return false;
 	}
 
-	wsc->m1->association_state = WSC_ASSOCIATION_STATE_NOT_ASSOCIATED;
-	wsc->m1->configuration_error = WSC_CONFIGURATION_ERROR_NO_ERROR;
-
-	if (!l_settings_get_uint(settings, "WSC",
-						"OSVersion", &u32))
+	if (!l_settings_get_uint(settings, "WSC", "OSVersion", &u32))
 		u32 = 0;
 
-	wsc->m1->os_version = u32 & 0x7fffffff;
+	*os_version = u32 & 0x7fffffff;
+
+	return true;
+}
+
+static bool eap_wsc_load_common(struct eap_state *eap,
+					struct l_settings *settings,
+					bool registrar)
+{
+	struct eap_wsc_state *wsc;
+	const char *v;
+	uint8_t private_key[192];
+	size_t len;
+	unsigned int u32;
+	char *str;
+
+	wsc = l_new(struct eap_wsc_state, 1);
+	wsc->registrar = registrar;
+
+	if (registrar)
+		wsc->state = STATE_EXPECT_IDENTITY;
+	else
+		wsc->state = STATE_EXPECT_START;
+
+	/*
+	 * On the Enrollee we fill in all of the M1 data so that it's ready
+	 * to send.  On the Registrar we store some information in wsc->m1
+	 * for the actual M1's validation, and we fill in the elements of M2
+	 * that don't depend on the received M1 data.
+	 */
+	wsc->m1 = l_new(struct wsc_m1, 1);
+
+	if (registrar)
+		wsc->m2 = l_new(struct wsc_m2, 1);
+	else {
+		wsc->m1->version2 = true;
+		wsc->m1->state = WSC_STATE_NOT_CONFIGURED;
+		wsc->m1->association_state = WSC_ASSOCIATION_STATE_NOT_ASSOCIATED;
+		wsc->m1->configuration_error = WSC_CONFIGURATION_ERROR_NO_ERROR;
+
+		wsc->m1->auth_type_flags =
+					WSC_AUTHENTICATION_TYPE_WPA2_PERSONAL |
+					WSC_AUTHENTICATION_TYPE_WPA_PERSONAL |
+					WSC_AUTHENTICATION_TYPE_OPEN;
+		wsc->m1->encryption_type_flags = WSC_ENCRYPTION_TYPE_NONE |
+						WSC_ENCRYPTION_TYPE_AES_TKIP;
+		wsc->m1->connection_type_flags = WSC_CONNECTION_TYPE_ESS;
+	}
+
+	v = l_settings_get_value(settings, "WSC", "EnrolleeMAC");
+	if (!v)
+		goto err;
+
+	if (!util_string_to_address(v, wsc->m1->addr))
+		goto err;
+
+	if (!wsc_uuid_from_addr(wsc->m1->addr, wsc->m1->uuid_e))
+		goto err;
+
+	if (registrar && !load_hexencoded(settings, "UUID-R",
+						wsc->m2->uuid_r, 16))
+		goto err;
+
+	if (!l_settings_get_uint(settings, "WSC", "ConfigurationMethods", &u32))
+		u32 = WSC_CONFIGURATION_METHOD_VIRTUAL_DISPLAY_PIN;
+
+	if (!registrar)
+		wsc->m1->config_methods = u32;
+	else
+		wsc->m2->config_methods = u32;
+
+	if (!registrar) {
+		if (!load_device_data(settings,
+					wsc->m1->manufacturer,
+					wsc->m1->model_name,
+					wsc->m1->model_number,
+					wsc->m1->serial_number,
+					&wsc->m1->primary_device_type,
+					wsc->m1->device_name,
+					&wsc->m1->rf_bands,
+					&wsc->m1->os_version))
+			goto err;
+	} else {
+		if (!load_device_data(settings,
+					wsc->m2->manufacturer,
+					wsc->m2->model_name,
+					wsc->m2->model_number,
+					wsc->m2->serial_number,
+					&wsc->m2->primary_device_type,
+					wsc->m2->device_name,
+					&wsc->m2->rf_bands,
+					&wsc->m2->os_version))
+			goto err;
+	}
 
 	if (!l_settings_get_uint(settings, "WSC", "DevicePasswordId", &u32))
 		u32 = WSC_DEVICE_PASSWORD_ID_PUSH_BUTTON;
 
-	wsc->m1->device_password_id = u32;
+	if (!registrar)
+		wsc->m1->device_password_id = u32;
+	else
+		wsc->m2->device_password_id = u32;
 
 	wsc->device_password = l_settings_get_string(settings, "WSC",
 							"DevicePassword");
@@ -1361,11 +1411,141 @@ static bool eap_wsc_load_settings(struct eap_state *eap,
 	} else
 		wsc->device_password = l_strdup("00000000");
 
-	if (!load_hexencoded(settings, "E-SNonce1", wsc->e_snonce1, 16))
-		l_getrandom(wsc->e_snonce1, 16);
+	if (registrar) {
+		wsc->m2->auth_type_flags = 0;
 
-	if (!load_hexencoded(settings, "E-SNonce2", wsc->e_snonce2, 16))
-		l_getrandom(wsc->e_snonce2, 16);
+		str = l_settings_get_string(settings, "WSC", "WPA2-SSID");
+		if (str) {
+			if (strlen(str) > 32)
+				goto err;
+
+			wsc->m2->auth_type_flags |=
+				WSC_AUTHENTICATION_TYPE_WPA2_PERSONAL;
+			wsc->m2->encryption_type_flags |=
+				WSC_ENCRYPTION_TYPE_AES_TKIP;
+			wsc->wpa2_cred.auth_type =
+				WSC_AUTHENTICATION_TYPE_WPA2_PERSONAL;
+			wsc->wpa2_cred.encryption_type =
+				WSC_ENCRYPTION_TYPE_AES_TKIP;
+
+			wsc->wpa2_cred.ssid_len = strlen(str);
+			memcpy(wsc->wpa2_cred.ssid, str,
+				wsc->wpa2_cred.ssid_len);
+			l_free(str);
+
+			str = l_settings_get_string(settings, "WSC",
+							"WPA2-Passphrase");
+			if (str) {
+				len = strlen(str);
+				if (len < 8 || len > 63) {
+					explicit_bzero(str, len);
+					goto err;
+				}
+
+				memcpy(wsc->wpa2_cred.network_key, str, len);
+				wsc->wpa2_cred.network_key_len = len;
+				explicit_bzero(str, len);
+			} else {
+				uint8_t buf[32];
+				int i;
+				static const char hexdigits[16] =
+					"0123456789ABCDEF";
+
+				if (!load_hexencoded(settings, "WPA2-PSK",
+							buf, 32))
+					goto err;
+
+				for (i = 0; i < 32; i++) {
+					wsc->wpa2_cred.network_key[i * 2] =
+						hexdigits[buf[i] >> 4];
+					wsc->wpa2_cred.network_key[i * 2 + 1] =
+						hexdigits[buf[i] & 15];
+				}
+
+				wsc->wpa2_cred.network_key_len = 64;
+				explicit_bzero(buf, 32);
+			}
+
+			memcpy(wsc->wpa2_cred.addr, wsc->m1->addr, 6);
+		}
+
+		str = l_settings_get_string(settings, "WSC", "Open-SSID");
+		if (str) {
+			if (strlen(str) > 32)
+				goto err;
+
+			wsc->m2->auth_type_flags |=
+				WSC_AUTHENTICATION_TYPE_OPEN;
+			wsc->m2->encryption_type_flags |=
+				WSC_ENCRYPTION_TYPE_NONE;
+			wsc->open_cred.auth_type =
+				WSC_AUTHENTICATION_TYPE_OPEN;
+			wsc->open_cred.encryption_type =
+				WSC_ENCRYPTION_TYPE_NONE;
+
+			wsc->open_cred.ssid_len = strlen(str);
+			memcpy(wsc->open_cred.ssid, str,
+				wsc->open_cred.ssid_len);
+			l_free(str);
+
+			wsc->open_cred.network_key_len = 0;
+
+			memcpy(wsc->open_cred.addr, wsc->m1->addr, 6);
+		}
+
+		if (!wsc->m2->auth_type_flags)
+			goto err;
+
+		wsc->m2->connection_type_flags = WSC_CONNECTION_TYPE_ESS;
+	}
+
+	/*
+	 * The settings we read below probably shouldn't be used except to
+	 * eliminate randomness in debugging/tests.
+	 */
+	if (registrar) {
+		if (!load_hexencoded(settings, "RegistrarNonce",
+						wsc->m2->registrar_nonce, 16))
+			l_getrandom(wsc->m2->registrar_nonce, 16);
+	} else {
+		if (!load_hexencoded(settings, "EnrolleeNonce",
+						wsc->m1->enrollee_nonce, 16))
+			l_getrandom(wsc->m1->enrollee_nonce, 16);
+	}
+
+	if (load_hexencoded(settings, "PrivateKey", private_key, 192)) {
+		if (!l_key_validate_dh_payload(private_key, 192,
+						crypto_dh5_prime,
+						crypto_dh5_prime_size)) {
+			explicit_bzero(private_key, 192);
+			goto err;
+		}
+
+		wsc->private = l_key_new(L_KEY_RAW, private_key, 192);
+		explicit_bzero(private_key, 192);
+	} else
+		wsc->private = l_key_generate_dh_private(crypto_dh5_prime,
+							crypto_dh5_prime_size);
+
+	if (!wsc->private)
+		goto err;
+
+	len = sizeof(wsc->m1->public_key);
+	if (!l_key_compute_dh_public(dh5_generator, wsc->private, dh5_prime,
+					registrar ? wsc->m2->public_key :
+					wsc->m1->public_key, &len))
+		goto err;
+
+	if (len != sizeof(wsc->m1->public_key))
+		goto err;
+
+	if (!load_hexencoded(settings, registrar ? "R-SNonce1" : "E-SNonce1",
+							wsc->local_snonce1, 16))
+		l_getrandom(wsc->local_snonce1, 16);
+
+	if (!load_hexencoded(settings, registrar ? "R-SNonce2" : "E-SNonce2",
+							wsc->local_snonce2, 16))
+		l_getrandom(wsc->local_snonce2, 16);
 
 	if (!load_hexencoded(settings, "IV1", wsc->iv1, 16))
 		l_getrandom(wsc->iv1, 16);
@@ -1373,6 +1553,9 @@ static bool eap_wsc_load_settings(struct eap_state *eap,
 	if (!load_hexencoded(settings, "IV2", wsc->iv2, 16))
 		l_getrandom(wsc->iv2, 16);
 
+	if (registrar && !load_hexencoded(settings, "IV3", wsc->iv3, 16))
+		l_getrandom(wsc->iv3, 16);
+
 	eap_set_data(eap, wsc);
 
 	return true;
@@ -1387,12 +1570,22 @@ err:
 	if (wsc->private)
 		l_key_free(wsc->private);
 
+	explicit_bzero(&wsc->wpa2_cred, sizeof(struct wsc_credential));
+	explicit_bzero(&wsc->open_cred, sizeof(struct wsc_credential));
 	l_free(wsc->m1);
+	l_free(wsc->m2);
 	l_free(wsc);
 
 	return false;
 }
 
+static bool eap_wsc_load_settings(struct eap_state *eap,
+					struct l_settings *settings,
+					const char *prefix)
+{
+	return eap_wsc_load_common(eap, settings, false);
+}
+
 static struct eap_method eap_wsc = {
 	.vendor_id = { 0x00, 0x37, 0x2a },
 	.vendor_type = 0x00000001,
@@ -1405,6 +1598,23 @@ static struct eap_method eap_wsc = {
 	.load_settings = eap_wsc_load_settings,
 };
 
+static bool eap_wsc_r_load_settings(struct eap_state *eap,
+					struct l_settings *settings,
+					const char *prefix)
+{
+	return eap_wsc_load_common(eap, settings, true);
+}
+
+static struct eap_method eap_wsc_r = {
+	.name = "WSC-R",
+	.request_type = EAP_TYPE_EXPANDED,
+	.vendor_id = { 0x00, 0x37, 0x2a },
+	.vendor_type = 0x00000001,
+	.exports_msk = true,
+	.free = eap_wsc_free,
+	.load_settings = eap_wsc_r_load_settings,
+};
+
 static int eap_wsc_init(void)
 {
 	int r = -ENOTSUP;
@@ -1422,12 +1632,18 @@ static int eap_wsc_init(void)
 		goto fail_prime;
 
 	r = eap_register_method(&eap_wsc);
+	if (r)
+		goto fail_register;
+
+	r = eap_register_method(&eap_wsc_r);
 	if (!r)
 		return 0;
 
+	eap_unregister_method(&eap_wsc);
+
+fail_register:
 	l_key_free(dh5_prime);
 	dh5_prime = NULL;
-
 fail_prime:
 	l_key_free(dh5_generator);
 	dh5_generator = NULL;
@@ -1440,6 +1656,7 @@ static void eap_wsc_exit(void)
 	l_debug("");
 
 	eap_unregister_method(&eap_wsc);
+	eap_unregister_method(&eap_wsc_r);
 
 	l_key_free(dh5_prime);
 	l_key_free(dh5_generator);
Signature · 1 line
2.25.1
#2 lore raw

Re: [PATCH 11/16] eap-wsc: Registrar mode settings loading

Hi Andrew,

Quoted 9 lines · Andrew Zaborowski

Alongside the current EAP-WSC enrollee side support, add the initial part of registrar side. In the same file, register a new method with the name string of "WSC-R". In this patch only the load_settings method is added. validate_identity and handle_response are added in later patches.

---
  src/eap-wsc.c | 433 +++++++++++++++++++++++++++++++++++++-------------
  1 file changed, 325 insertions(+), 108 deletions(-)

<snip>

src/eap-wsc.c static bool load_constrained_string(struct l_settings *settings, · line 1292

Quoted 3 lines
src/eap-wsc.c +3 -0
+static bool eap_wsc_load_common(struct eap_state *eap,
+					struct l_settings *settings,
+					bool registrar)

so based on the name, I'd expect this function to do the common things, not if-else everything based on registrar variable. The if-else also leads to excessive nesting, which we should avoid...

Perhaps it would be nicer if things that are only applicable to the particular role are moved in the specific load_settings functions below.

Quoted 5 lines
src/eap-wsc.c +5 -0
+{
+	struct eap_wsc_state *wsc;
+	const char *v;
+	uint8_t private_key[192];
+	size_t len;

src/eap-wsc.c static bool load_constrained_string(struct l_settings *settings, · line 1307

Quoted 10 lines
src/eap-wsc.c +10 -0
+	unsigned int u32;
+	char *str;
+
+	wsc = l_new(struct eap_wsc_state, 1);
+	wsc->registrar = registrar;
+
+	if (registrar)
+		wsc->state = STATE_EXPECT_IDENTITY;
+	else
+		wsc->state = STATE_EXPECT_START;

This for example can be set directly without a need for if-else

Quoted 14 lines
src/eap-wsc.c +14 -0
+
+	/*
+	 * On the Enrollee we fill in all of the M1 data so that it's ready
+	 * to send.  On the Registrar we store some information in wsc->m1
+	 * for the actual M1's validation, and we fill in the elements of M2
+	 * that don't depend on the received M1 data.
+	 */
+	wsc->m1 = l_new(struct wsc_m1, 1);
+
+	if (registrar)
+		wsc->m2 = l_new(struct wsc_m2, 1);
+	else {
+		wsc->m1->version2 = true;
+		wsc->m1->state = WSC_STATE_NOT_CONFIGURED;

src/eap-wsc.c static bool load_constrained_string(struct l_settings *settings, · line 1331

Quoted 10 lines
src/eap-wsc.c +10 -0
+		wsc->m1->association_state = WSC_ASSOCIATION_STATE_NOT_ASSOCIATED;
+		wsc->m1->configuration_error = WSC_CONFIGURATION_ERROR_NO_ERROR;
+
+		wsc->m1->auth_type_flags =
+					WSC_AUTHENTICATION_TYPE_WPA2_PERSONAL |
+					WSC_AUTHENTICATION_TYPE_WPA_PERSONAL |
+					WSC_AUTHENTICATION_TYPE_OPEN;
+		wsc->m1->encryption_type_flags = WSC_ENCRYPTION_TYPE_NONE |
+						WSC_ENCRYPTION_TYPE_AES_TKIP;
+		wsc->m1->connection_type_flags = WSC_CONNECTION_TYPE_ESS;

This can be set up directly in the enrolee version.

src/eap-wsc.c static bool load_constrained_string(struct l_settings *settings, · line 1343

Quoted 12 lines
src/eap-wsc.c +12 -0
+	}
+
+	v = l_settings_get_value(settings, "WSC", "EnrolleeMAC");
+	if (!v)
+		goto err;
+
+	if (!util_string_to_address(v, wsc->m1->addr))
+		goto err;
+
+	if (!wsc_uuid_from_addr(wsc->m1->addr, wsc->m1->uuid_e))
+		goto err;
+

This is common, so maybe just assume m1/m2 are created by the enrollee/registrar specific methods.

src/eap-wsc.c static bool load_constrained_string(struct l_settings *settings, · line 1346

Quoted 3 lines
src/eap-wsc.c +3 -0
+	if (registrar && !load_hexencoded(settings, "UUID-R",
+						wsc->m2->uuid_r, 16))
+		goto err;

This can be put into the registrar method directly

src/eap-wsc.c static bool load_constrained_string(struct l_settings *settings, · line 1355

Quoted 9 lines
src/eap-wsc.c +9 -0
+
+	if (!l_settings_get_uint(settings, "WSC", "ConfigurationMethods", &u32))
+		u32 = WSC_CONFIGURATION_METHOD_VIRTUAL_DISPLAY_PIN;
+
+	if (!registrar)
+		wsc->m1->config_methods = u32;
+	else
+		wsc->m2->config_methods = u32;
+

Ok

Quoted 14 lines
src/eap-wsc.c +13 -0
+	if (!registrar) {
+		if (!load_device_data(settings,
+					wsc->m1->manufacturer,
+					wsc->m1->model_name,
+					wsc->m1->model_number,
+					wsc->m1->serial_number,
+					&wsc->m1->primary_device_type,
+					wsc->m1->device_name,
+					&wsc->m1->rf_bands,
+					&wsc->m1->os_version))
+			goto err;
+	} else {
+		if (!load_device_data(settings,

src/eap-wsc.c static bool load_constrained_string(struct l_settings *settings, · line 1378

Quoted 10 lines
src/eap-wsc.c +10 -0
+					wsc->m2->manufacturer,
+					wsc->m2->model_name,
+					wsc->m2->model_number,
+					wsc->m2->serial_number,
+					&wsc->m2->primary_device_type,
+					wsc->m2->device_name,
+					&wsc->m2->rf_bands,
+					&wsc->m2->os_version))
+			goto err;
+	}

Probably these belong in the enrolee/registrar specific methods

Quoted 59 lines
  	if (!l_settings_get_uint(settings, "WSC", "DevicePasswordId", &u32))
  		u32 = WSC_DEVICE_PASSWORD_ID_PUSH_BUTTON;
src/eap-wsc.c +43 -5
-	wsc->m1->device_password_id = u32;
+	if (!registrar)
+		wsc->m1->device_password_id = u32;
+	else
+		wsc->m2->device_password_id = u32;
 
  	wsc->device_password = l_settings_get_string(settings, "WSC",
  							"DevicePassword");
@@ -1361,11 +1411,141 @@ static bool eap_wsc_load_settings(struct eap_state *eap,
  	} else
  		wsc->device_password = l_strdup("00000000");
 
-	if (!load_hexencoded(settings, "E-SNonce1", wsc->e_snonce1, 16))
-		l_getrandom(wsc->e_snonce1, 16);
+	if (registrar) {
+		wsc->m2->auth_type_flags = 0;
 
-	if (!load_hexencoded(settings, "E-SNonce2", wsc->e_snonce2, 16))
-		l_getrandom(wsc->e_snonce2, 16);
+		str = l_settings_get_string(settings, "WSC", "WPA2-SSID");
+		if (str) {
+			if (strlen(str) > 32)
+				goto err;
+
+			wsc->m2->auth_type_flags |=
+				WSC_AUTHENTICATION_TYPE_WPA2_PERSONAL;
+			wsc->m2->encryption_type_flags |=
+				WSC_ENCRYPTION_TYPE_AES_TKIP;
+			wsc->wpa2_cred.auth_type =
+				WSC_AUTHENTICATION_TYPE_WPA2_PERSONAL;
+			wsc->wpa2_cred.encryption_type =
+				WSC_ENCRYPTION_TYPE_AES_TKIP;
+
+			wsc->wpa2_cred.ssid_len = strlen(str);
+			memcpy(wsc->wpa2_cred.ssid, str,
+				wsc->wpa2_cred.ssid_len);
+			l_free(str);
+
+			str = l_settings_get_string(settings, "WSC",
+							"WPA2-Passphrase");
+			if (str) {
+				len = strlen(str);
+				if (len < 8 || len > 63) {
+					explicit_bzero(str, len);
+					goto err;
+				}
+
+				memcpy(wsc->wpa2_cred.network_key, str, len);
+				wsc->wpa2_cred.network_key_len = len;
+				explicit_bzero(str, len);
+			} else {
+				uint8_t buf[32];
+				int i;
+				static const char hexdigits[16] =
+					"0123456789ABCDEF";
+

src/eap-wsc.c static bool eap_wsc_load_settings(struct eap_state *eap, · line 1463

Quoted 10 lines
src/eap-wsc.c +10 -0
+				if (!load_hexencoded(settings, "WPA2-PSK",
+							buf, 32))
+					goto err;
+
+				for (i = 0; i < 32; i++) {
+					wsc->wpa2_cred.network_key[i * 2] =
+						hexdigits[buf[i] >> 4];
+					wsc->wpa2_cred.network_key[i * 2 + 1] =
+						hexdigits[buf[i] & 15];
+				}

Hmm, what is this doing actually? Is this some elaborate scheme to make sure that WPA2-PSK is a valid hex string?

Quoted 27 lines
src/eap-wsc.c +27 -0
+
+				wsc->wpa2_cred.network_key_len = 64;
+				explicit_bzero(buf, 32);
+			}
+
+			memcpy(wsc->wpa2_cred.addr, wsc->m1->addr, 6);
+		}
+
+		str = l_settings_get_string(settings, "WSC", "Open-SSID");
+		if (str) {
+			if (strlen(str) > 32)
+				goto err;
+
+			wsc->m2->auth_type_flags |=
+				WSC_AUTHENTICATION_TYPE_OPEN;
+			wsc->m2->encryption_type_flags |=
+				WSC_ENCRYPTION_TYPE_NONE;
+			wsc->open_cred.auth_type =
+				WSC_AUTHENTICATION_TYPE_OPEN;
+			wsc->open_cred.encryption_type =
+				WSC_ENCRYPTION_TYPE_NONE;
+
+			wsc->open_cred.ssid_len = strlen(str);
+			memcpy(wsc->open_cred.ssid, str,
+				wsc->open_cred.ssid_len);
+			l_free(str);
+

src/eap-wsc.c static bool eap_wsc_load_settings(struct eap_state *eap, · line 1500

Quoted 10 lines
src/eap-wsc.c +10 -0
+			wsc->open_cred.network_key_len = 0;
+
+			memcpy(wsc->open_cred.addr, wsc->m1->addr, 6);
+		}
+
+		if (!wsc->m2->auth_type_flags)
+			goto err;
+
+		wsc->m2->connection_type_flags = WSC_CONNECTION_TYPE_ESS;
+	}

This whole block seems to be registrar specific...

Quoted 5 lines
src/eap-wsc.c +5 -0
+
+	/*
+	 * The settings we read below probably shouldn't be used except to
+	 * eliminate randomness in debugging/tests.
+	 */

src/eap-wsc.c static bool eap_wsc_load_settings(struct eap_state *eap, · line 1515

Quoted 10 lines
src/eap-wsc.c +10 -0
+	if (registrar) {
+		if (!load_hexencoded(settings, "RegistrarNonce",
+						wsc->m2->registrar_nonce, 16))
+			l_getrandom(wsc->m2->registrar_nonce, 16);
+	} else {
+		if (!load_hexencoded(settings, "EnrolleeNonce",
+						wsc->m1->enrollee_nonce, 16))
+			l_getrandom(wsc->m1->enrollee_nonce, 16);
+	}
+

Individual methods here?

Quoted 15 lines
src/eap-wsc.c +15 -0
+	if (load_hexencoded(settings, "PrivateKey", private_key, 192)) {
+		if (!l_key_validate_dh_payload(private_key, 192,
+						crypto_dh5_prime,
+						crypto_dh5_prime_size)) {
+			explicit_bzero(private_key, 192);
+			goto err;
+		}
+
+		wsc->private = l_key_new(L_KEY_RAW, private_key, 192);
+		explicit_bzero(private_key, 192);
+	} else
+		wsc->private = l_key_generate_dh_private(crypto_dh5_prime,
+							crypto_dh5_prime_size);
+
+	if (!wsc->private)

src/eap-wsc.c static bool eap_wsc_load_settings(struct eap_state *eap, · line 1540

Quoted 10 lines
src/eap-wsc.c +10 -0
+		goto err;
+
+	len = sizeof(wsc->m1->public_key);
+	if (!l_key_compute_dh_public(dh5_generator, wsc->private, dh5_prime,
+					registrar ? wsc->m2->public_key :
+					wsc->m1->public_key, &len))
+		goto err;
+
+	if (len != sizeof(wsc->m1->public_key))
+		goto err;

Ok

src/eap-wsc.c static bool eap_wsc_load_settings(struct eap_state *eap, · line 1548

Quoted 8 lines
src/eap-wsc.c +8 -0
+
+	if (!load_hexencoded(settings, registrar ? "R-SNonce1" : "E-SNonce1",
+							wsc->local_snonce1, 16))
+		l_getrandom(wsc->local_snonce1, 16);
+
+	if (!load_hexencoded(settings, registrar ? "R-SNonce2" : "E-SNonce2",
+							wsc->local_snonce2, 16))
+		l_getrandom(wsc->local_snonce2, 16);

Ok

src/eap-wsc.c static bool eap_wsc_load_settings(struct eap_state *eap, · line 1558

Quoted 10 lines
src/eap-wsc.c +3 -0
  	if (!load_hexencoded(settings, "IV1", wsc->iv1, 16))
  		l_getrandom(wsc->iv1, 16);
@@ -1373,6 +1553,9 @@ static bool eap_wsc_load_settings(struct eap_state *eap,
  	if (!load_hexencoded(settings, "IV2", wsc->iv2, 16))
  		l_getrandom(wsc->iv2, 16);
 
+	if (registrar && !load_hexencoded(settings, "IV3", wsc->iv3, 16))
+		l_getrandom(wsc->iv3, 16);
+

Seems registrar specific here as well..

src/eap-wsc.c err: · line 1577

Quoted 12 lines
src/eap-wsc.c +3 -0
  	eap_set_data(eap, wsc);
 
  	return true;
@@ -1387,12 +1570,22 @@ err:
  	if (wsc->private)
  		l_key_free(wsc->private);
 
+	explicit_bzero(&wsc->wpa2_cred, sizeof(struct wsc_credential));
+	explicit_bzero(&wsc->open_cred, sizeof(struct wsc_credential));
  	l_free(wsc->m1);
+	l_free(wsc->m2);
  	l_free(wsc);

In theory this cleanup block can be broken up to be more granular. For example, the cred zeroing can be done only in the registrar loader.

But you also have most of this being done in eap_wsc_free, so depending on how lazy you want to be, consider just calling that, or break it up into smaller chunks and just call the relevant _free method.

src/eap-wsc.c err: · line 1581

Quoted 4 lines
src/eap-wsc.c +0 -0
  	return false;
  }
 

Regards,
-Denis

#3 lore raw

Re: [PATCH 11/16] eap-wsc: Registrar mode settings loading

Quoted 11 lines · Denis Kenzior

src/eap-wsc.c static bool load_constrained_string(struct l_settings *settings, · line 1292

Quoted 3 lines · Andrew Zaborowski
src/eap-wsc.c +3 -0
+static bool eap_wsc_load_common(struct eap_state *eap,
+                                     struct l_settings *settings,
+                                     bool registrar)

so based on the name, I'd expect this function to do the common things, not if-else everything based on registrar variable. The if-else also leads to excessive nesting, which we should avoid...

Perhaps it would be nicer if things that are only applicable to the particular role are moved in the specific load_settings functions below.

Ok, makes sense.

Quoted 46 lines
Quoted 6 lines
src/eap-wsc.c +5 -0
+{
+     struct eap_wsc_state *wsc;
+     const char *v;
+     uint8_t private_key[192];
+     size_t len;

src/eap-wsc.c static bool load_constrained_string(struct l_settings *settings, · line 1307

Quoted 10 lines
src/eap-wsc.c +10 -0
+     unsigned int u32;
+     char *str;
+
+     wsc = l_new(struct eap_wsc_state, 1);
+     wsc->registrar = registrar;
+
+     if (registrar)
+             wsc->state = STATE_EXPECT_IDENTITY;
+     else
+             wsc->state = STATE_EXPECT_START;

This for example can be set directly without a need for if-else

Quoted 14 lines
src/eap-wsc.c +14 -0
+
+     /*
+      * On the Enrollee we fill in all of the M1 data so that it's ready
+      * to send.  On the Registrar we store some information in wsc->m1
+      * for the actual M1's validation, and we fill in the elements of M2
+      * that don't depend on the received M1 data.
+      */
+     wsc->m1 = l_new(struct wsc_m1, 1);
+
+     if (registrar)
+             wsc->m2 = l_new(struct wsc_m2, 1);
+     else {
+             wsc->m1->version2 = true;
+             wsc->m1->state = WSC_STATE_NOT_CONFIGURED;

src/eap-wsc.c static bool load_constrained_string(struct l_settings *settings, · line 1331

Quoted 10 lines
src/eap-wsc.c +10 -0
+             wsc->m1->association_state = WSC_ASSOCIATION_STATE_NOT_ASSOCIATED;
+             wsc->m1->configuration_error = WSC_CONFIGURATION_ERROR_NO_ERROR;
+
+             wsc->m1->auth_type_flags =
+                                     WSC_AUTHENTICATION_TYPE_WPA2_PERSONAL |
+                                     WSC_AUTHENTICATION_TYPE_WPA_PERSONAL |
+                                     WSC_AUTHENTICATION_TYPE_OPEN;
+             wsc->m1->encryption_type_flags = WSC_ENCRYPTION_TYPE_NONE |
+                                             WSC_ENCRYPTION_TYPE_AES_TKIP;
+             wsc->m1->connection_type_flags = WSC_CONNECTION_TYPE_ESS;

This can be set up directly in the enrolee version.

Quoted 15 lines

src/eap-wsc.c static bool load_constrained_string(struct l_settings *settings, · line 1343

Quoted 12 lines
src/eap-wsc.c +12 -0
+     }
+
+     v = l_settings_get_value(settings, "WSC", "EnrolleeMAC");
+     if (!v)
+             goto err;
+
+     if (!util_string_to_address(v, wsc->m1->addr))
+             goto err;
+
+     if (!wsc_uuid_from_addr(wsc->m1->addr, wsc->m1->uuid_e))
+             goto err;
+

This is common, so maybe just assume m1/m2 are created by the enrollee/registrar specific methods.

BTW I was thinking it'd be easier if m1/m2 were static parts of eap_wsc_state rather than pointers.

Quoted 6 lines

src/eap-wsc.c static bool load_constrained_string(struct l_settings *settings, · line 1346

Quoted 4 lines
src/eap-wsc.c +3 -0
+     if (registrar && !load_hexencoded(settings, "UUID-R",
+                                             wsc->m2->uuid_r, 16))
+             goto err;

This can be put into the registrar method directly

Ok.

Quoted 38 lines

src/eap-wsc.c static bool load_constrained_string(struct l_settings *settings, · line 1355

Quoted 10 lines
src/eap-wsc.c +9 -0
+
+     if (!l_settings_get_uint(settings, "WSC", "ConfigurationMethods", &u32))
+             u32 = WSC_CONFIGURATION_METHOD_VIRTUAL_DISPLAY_PIN;
+
+     if (!registrar)
+             wsc->m1->config_methods = u32;
+     else
+             wsc->m2->config_methods = u32;
+

Ok

Quoted 14 lines
src/eap-wsc.c +13 -0
+     if (!registrar) {
+             if (!load_device_data(settings,
+                                     wsc->m1->manufacturer,
+                                     wsc->m1->model_name,
+                                     wsc->m1->model_number,
+                                     wsc->m1->serial_number,
+                                     &wsc->m1->primary_device_type,
+                                     wsc->m1->device_name,
+                                     &wsc->m1->rf_bands,
+                                     &wsc->m1->os_version))
+                     goto err;
+     } else {
+             if (!load_device_data(settings,

src/eap-wsc.c static bool load_constrained_string(struct l_settings *settings, · line 1378

Quoted 10 lines
src/eap-wsc.c +10 -0
+                                     wsc->m2->manufacturer,
+                                     wsc->m2->model_name,
+                                     wsc->m2->model_number,
+                                     wsc->m2->serial_number,
+                                     &wsc->m2->primary_device_type,
+                                     wsc->m2->device_name,
+                                     &wsc->m2->rf_bands,
+                                     &wsc->m2->os_version))
+                     goto err;
+     }
Quoted 1 line

Probably these belong in the enrolee/registrar specific methods

Ok.

Quoted 65 lines
Quoted 65 lines
      if (!l_settings_get_uint(settings, "WSC", "DevicePasswordId", &u32))
              u32 = WSC_DEVICE_PASSWORD_ID_PUSH_BUTTON;
src/eap-wsc.c +48 -5
-     wsc->m1->device_password_id = u32;
+     if (!registrar)
+             wsc->m1->device_password_id = u32;
+     else
+             wsc->m2->device_password_id = u32;
 
      wsc->device_password = l_settings_get_string(settings, "WSC",
                                                      "DevicePassword");
@@ -1361,11 +1411,141 @@ static bool eap_wsc_load_settings(struct eap_state *eap,
      } else
              wsc->device_password = l_strdup("00000000");
 
-     if (!load_hexencoded(settings, "E-SNonce1", wsc->e_snonce1, 16))
-             l_getrandom(wsc->e_snonce1, 16);
+     if (registrar) {
+             wsc->m2->auth_type_flags = 0;
 
-     if (!load_hexencoded(settings, "E-SNonce2", wsc->e_snonce2, 16))
-             l_getrandom(wsc->e_snonce2, 16);
+             str = l_settings_get_string(settings, "WSC", "WPA2-SSID");
+             if (str) {
+                     if (strlen(str) > 32)
+                             goto err;
+
+                     wsc->m2->auth_type_flags |=
+                             WSC_AUTHENTICATION_TYPE_WPA2_PERSONAL;
+                     wsc->m2->encryption_type_flags |=
+                             WSC_ENCRYPTION_TYPE_AES_TKIP;
+                     wsc->wpa2_cred.auth_type =
+                             WSC_AUTHENTICATION_TYPE_WPA2_PERSONAL;
+                     wsc->wpa2_cred.encryption_type =
+                             WSC_ENCRYPTION_TYPE_AES_TKIP;
+
+                     wsc->wpa2_cred.ssid_len = strlen(str);
+                     memcpy(wsc->wpa2_cred.ssid, str,
+                             wsc->wpa2_cred.ssid_len);
+                     l_free(str);
+
+                     str = l_settings_get_string(settings, "WSC",
+                                                     "WPA2-Passphrase");
+                     if (str) {
+                             len = strlen(str);
+                             if (len < 8 || len > 63) {
+                                     explicit_bzero(str, len);
+                                     goto err;
+                             }
+
+                             memcpy(wsc->wpa2_cred.network_key, str, len);
+                             wsc->wpa2_cred.network_key_len = len;
+                             explicit_bzero(str, len);
+                     } else {
+                             uint8_t buf[32];
+                             int i;
+                             static const char hexdigits[16] =
+                                     "0123456789ABCDEF";
+
+                             if (!load_hexencoded(settings, "WPA2-PSK",
+                                                     buf, 32))
+                                     goto err;
+
+                             for (i = 0; i < 32; i++) {
Quoted 8 lines

src/eap-wsc.c static bool eap_wsc_load_settings(struct eap_state *eap, · line 1463

Quoted 5 lines
src/eap-wsc.c +5 -0
+                                     wsc->wpa2_cred.network_key[i * 2] =
+                                             hexdigits[buf[i] >> 4];
+                                     wsc->wpa2_cred.network_key[i * 2 + 1] =
+                                             hexdigits[buf[i] & 15];
+                             }

Hmm, what is this doing actually? Is this some elaborate scheme to make sure that WPA2-PSK is a valid hex string?

My idea was to validate the hexstring and then save an uppercase version of it. I think actually it was the WSC DevicePassword that is required to be uppercase, not the Network Key. Nevertheless it's nice to have the Network Key all lower-case or all upper-case, I guess l_util_hexstring would work here.

I don't think it's slower than converting the setting value char by char if we have to validate it first.

Quoted 32 lines
Quoted 32 lines
src/eap-wsc.c +31 -0
+
+                             wsc->wpa2_cred.network_key_len = 64;
+                             explicit_bzero(buf, 32);
+                     }
+
+                     memcpy(wsc->wpa2_cred.addr, wsc->m1->addr, 6);
+             }
+
+             str = l_settings_get_string(settings, "WSC", "Open-SSID");
+             if (str) {
+                     if (strlen(str) > 32)
+                             goto err;
+
+                     wsc->m2->auth_type_flags |=
+                             WSC_AUTHENTICATION_TYPE_OPEN;
+                     wsc->m2->encryption_type_flags |=
+                             WSC_ENCRYPTION_TYPE_NONE;
+                     wsc->open_cred.auth_type =
+                             WSC_AUTHENTICATION_TYPE_OPEN;
+                     wsc->open_cred.encryption_type =
+                             WSC_ENCRYPTION_TYPE_NONE;
+
+                     wsc->open_cred.ssid_len = strlen(str);
+                     memcpy(wsc->open_cred.ssid, str,
+                             wsc->open_cred.ssid_len);
+                     l_free(str);
+
+                     wsc->open_cred.network_key_len = 0;
+
+                     memcpy(wsc->open_cred.addr, wsc->m1->addr, 6);
+             }
Quoted 8 lines

src/eap-wsc.c static bool eap_wsc_load_settings(struct eap_state *eap, · line 1500

Quoted 6 lines
src/eap-wsc.c +6 -0
+
+             if (!wsc->m2->auth_type_flags)
+                     goto err;
+
+             wsc->m2->connection_type_flags = WSC_CONNECTION_TYPE_ESS;
+     }

This whole block seems to be registrar specific...

Yep.

Quoted 10 lines
Quoted 10 lines
src/eap-wsc.c +9 -0
+
+     /*
+      * The settings we read below probably shouldn't be used except to
+      * eliminate randomness in debugging/tests.
+      */
+     if (registrar) {
+             if (!load_hexencoded(settings, "RegistrarNonce",
+                                             wsc->m2->registrar_nonce, 16))
+                     l_getrandom(wsc->m2->registrar_nonce, 16);
Quoted 8 lines

src/eap-wsc.c static bool eap_wsc_load_settings(struct eap_state *eap, · line 1515

Quoted 6 lines
src/eap-wsc.c +6 -0
+     } else {
+             if (!load_hexencoded(settings, "EnrolleeNonce",
+                                             wsc->m1->enrollee_nonce, 16))
+                     l_getrandom(wsc->m1->enrollee_nonce, 16);
+     }
+

Individual methods here?

Ok.

Quoted 69 lines
Quoted 16 lines
src/eap-wsc.c +15 -0
+     if (load_hexencoded(settings, "PrivateKey", private_key, 192)) {
+             if (!l_key_validate_dh_payload(private_key, 192,
+                                             crypto_dh5_prime,
+                                             crypto_dh5_prime_size)) {
+                     explicit_bzero(private_key, 192);
+                     goto err;
+             }
+
+             wsc->private = l_key_new(L_KEY_RAW, private_key, 192);
+             explicit_bzero(private_key, 192);
+     } else
+             wsc->private = l_key_generate_dh_private(crypto_dh5_prime,
+                                                     crypto_dh5_prime_size);
+
+     if (!wsc->private)

src/eap-wsc.c static bool eap_wsc_load_settings(struct eap_state *eap, · line 1540

Quoted 10 lines
src/eap-wsc.c +10 -0
+             goto err;
+
+     len = sizeof(wsc->m1->public_key);
+     if (!l_key_compute_dh_public(dh5_generator, wsc->private, dh5_prime,
+                                     registrar ? wsc->m2->public_key :
+                                     wsc->m1->public_key, &len))
+             goto err;
+
+     if (len != sizeof(wsc->m1->public_key))
+             goto err;

Ok

src/eap-wsc.c static bool eap_wsc_load_settings(struct eap_state *eap, · line 1548

Quoted 8 lines
src/eap-wsc.c +8 -0
+
+     if (!load_hexencoded(settings, registrar ? "R-SNonce1" : "E-SNonce1",
+                                                     wsc->local_snonce1, 16))
+             l_getrandom(wsc->local_snonce1, 16);
+
+     if (!load_hexencoded(settings, registrar ? "R-SNonce2" : "E-SNonce2",
+                                                     wsc->local_snonce2, 16))
+             l_getrandom(wsc->local_snonce2, 16);

Ok

src/eap-wsc.c static bool eap_wsc_load_settings(struct eap_state *eap, · line 1558

Quoted 10 lines
src/eap-wsc.c +3 -0
      if (!load_hexencoded(settings, "IV1", wsc->iv1, 16))
              l_getrandom(wsc->iv1, 16);
@@ -1373,6 +1553,9 @@ static bool eap_wsc_load_settings(struct eap_state *eap,
      if (!load_hexencoded(settings, "IV2", wsc->iv2, 16))
              l_getrandom(wsc->iv2, 16);
 
+     if (registrar && !load_hexencoded(settings, "IV3", wsc->iv3, 16))
+             l_getrandom(wsc->iv3, 16);
+

Seems registrar specific here as well..

src/eap-wsc.c err: · line 1577

Quoted 12 lines
src/eap-wsc.c +3 -0
      eap_set_data(eap, wsc);
 
      return true;
@@ -1387,12 +1570,22 @@ err:
      if (wsc->private)
              l_key_free(wsc->private);
 
+     explicit_bzero(&wsc->wpa2_cred, sizeof(struct wsc_credential));
+     explicit_bzero(&wsc->open_cred, sizeof(struct wsc_credential));
      l_free(wsc->m1);
+     l_free(wsc->m2);
      l_free(wsc);

In theory this cleanup block can be broken up to be more granular. For example, the cred zeroing can be done only in the registrar loader.

Quoted 3 lines

But you also have most of this being done in eap_wsc_free, so depending on how lazy you want to be, consider just calling that, or break it up into smaller chunks and just call the relevant _free method.

Yeah, let's use eap_wsc_free I guess.

Best regards

#4 lore raw

PATCH 12/16 eap-wsc: Registrar mode message handling

In reply to Andrew Zaborowski

This commit has all the changes to modify and generalise the current eap-wsc.c code to handle both the Enrollee and Registrar side of the protocol, reusing existing functions and structures.

---
 src/eap-wsc.c | 134 +++++++++++++++++++++++++++++++++++++++-----------
 src/eap-wsc.h |   1 +
 2 files changed, 106 insertions(+), 29 deletions(-)
src/eap-wsc.c +105 -29
index 2770c87a..34de6033 100644
@@ -93,6 +93,8 @@ struct eap_wsc_state {
 	uint8_t iv3[16];
 	uint8_t psk1[16];
 	uint8_t psk2[16];
+	uint8_t e_hash1[32];
+	uint8_t e_hash2[32];
 	uint8_t r_hash2[32];
 	enum state state;
 	struct l_checksum *hmac_auth_key;
@@ -171,22 +173,26 @@ static inline void keywrap_authenticator_put(struct eap_wsc_state *wsc,
 	l_checksum_get_digest(wsc->hmac_auth_key, pdu + len - 8, 8);
 }
 
-static inline bool r_hash_check(struct eap_wsc_state *wsc,
-				uint8_t *r_snonce,
+static inline bool x_hash_check(struct eap_wsc_state *wsc,
+				uint8_t *x_snonce,
 				uint8_t *psk,
-				uint8_t *r_hash_expected)
+				uint8_t *x_hash_expected)
 {
 	struct iovec iov[4];
-	uint8_t r_hash[32];
+	uint8_t hash[32];
 
 	/*
 	 * WSC 2.0.5, Section 7.4:
+	 * The Enrollee creates two 128-bit secret nonces, E-S1, E-S2 and
+	 * then computes
+	 * E-Hash1 = HMACAuthKey(E-S1 || PSK1 || PKE || PKR)
+	 * E-Hash2 = HMACAuthKey(E-S2 || PSK2 || PKE || PKR)
 	 * The Registrar creates two 128-bit secret nonces, R-S1, R-S2 and
 	 * then computes
 	 * R-Hash1 = HMACAuthKey(R-S1 || PSK1 || PKE || PKR)
 	 * R-Hash2 = HMACAuthKey(R-S2 || PSK2 || PKE || PKR)
 	 */
-	iov[0].iov_base = r_snonce;
+	iov[0].iov_base = x_snonce;
 	iov[0].iov_len = 16;
 	iov[1].iov_base = psk;
 	iov[1].iov_len = 16;
@@ -195,9 +201,9 @@ static inline bool r_hash_check(struct eap_wsc_state *wsc,
 	iov[3].iov_base = wsc->m2->public_key;
 	iov[3].iov_len = sizeof(wsc->m2->public_key);
 	l_checksum_updatev(wsc->hmac_auth_key, iov, 4);
-	l_checksum_get_digest(wsc->hmac_auth_key, r_hash, sizeof(r_hash));
+	l_checksum_get_digest(wsc->hmac_auth_key, hash, sizeof(hash));
 
-	return !memcmp(r_hash, r_hash_expected, sizeof(r_hash));
+	return !memcmp(hash, x_hash_expected, sizeof(hash));
 }
 
 static uint8_t *encrypted_settings_decrypt(struct eap_wsc_state *wsc,
@@ -327,6 +333,16 @@ static void eap_wsc_free(struct eap_state *eap)
 	l_free(wsc);
 }
 
+static void eap_wsc_r_send_start(struct eap_state *eap)
+{
+	uint8_t buf[EAP_WSC_HEADER_LEN];
+
+	buf[12] = WSC_OP_START;
+	buf[13] = 0;
+
+	eap_method_new_request(eap, buf, EAP_WSC_HEADER_LEN);
+}
+
 static void eap_wsc_send_fragment(struct eap_state *eap)
 {
 	struct eap_wsc_state *wsc = eap_get_data(eap);
@@ -353,12 +369,16 @@ static void eap_wsc_send_fragment(struct eap_state *eap)
 	}
 
 	memcpy(buf + header_len, wsc->sent_pdu + wsc->tx_frag_offset, len);
-	eap_method_respond(eap, buf, header_len + len);
+
+	if (wsc->registrar)
+		eap_method_new_request(eap, buf, header_len + len);
+	else
+		eap_method_respond(eap, buf, header_len + len);
 
 	wsc->tx_last_frag_len = len;
 }
 
-static void eap_wsc_send_response(struct eap_state *eap,
+static void eap_wsc_send_message(struct eap_state *eap,
 						uint8_t *pdu, size_t pdu_len)
 {
 	struct eap_wsc_state *wsc = eap_get_data(eap);
@@ -373,7 +393,11 @@ static void eap_wsc_send_response(struct eap_state *eap,
 		buf[13] = 0;
 		memcpy(buf + EAP_WSC_HEADER_LEN, pdu, pdu_len);
 
-		eap_method_respond(eap, buf, msg_len);
+		if (wsc->registrar)
+			eap_method_new_request(eap, buf, msg_len);
+		else
+			eap_method_respond(eap, buf, msg_len);
+
 		l_free(buf);
 		return;
 	}
@@ -414,8 +438,11 @@ static void eap_wsc_send_nack(struct eap_state *eap,
 		return;
 
 	nack.version2 = true;
-	memcpy(nack.enrollee_nonce, wsc->m1->enrollee_nonce,
+	if (wsc->state != STATE_EXPECT_M1)
+		memcpy(nack.enrollee_nonce, wsc->m1->enrollee_nonce,
 						sizeof(nack.enrollee_nonce));
+	else
+		memset(nack.enrollee_nonce, 0, sizeof(nack.enrollee_nonce));
 
 	if (wsc->m2)
 		memcpy(nack.registrar_nonce, wsc->m2->registrar_nonce,
@@ -433,7 +460,11 @@ static void eap_wsc_send_nack(struct eap_state *eap,
 	buf[13] = 0;
 	memcpy(buf + EAP_WSC_HEADER_LEN, pdu, pdu_len);
 
-	eap_method_respond(eap, buf, pdu_len + EAP_WSC_HEADER_LEN);
+	if (wsc->registrar)
+		eap_method_new_request(eap, buf, pdu_len + EAP_WSC_HEADER_LEN);
+	else
+		eap_method_respond(eap, buf, pdu_len + EAP_WSC_HEADER_LEN);
+
 	l_free(pdu);
 }
 
@@ -465,12 +496,16 @@ static void eap_wsc_send_done(struct eap_state *eap)
 
 static void eap_wsc_send_frag_ack(struct eap_state *eap)
 {
+	struct eap_wsc_state *wsc = eap_get_data(eap);
 	uint8_t buf[EAP_WSC_HEADER_LEN];
 
 	buf[12] = WSC_OP_FRAG_ACK;
 	buf[13] = 0;
 
-	eap_method_respond(eap, buf, EAP_WSC_HEADER_LEN);
+	if (wsc->registrar)
+		eap_method_new_request(eap, buf, EAP_WSC_HEADER_LEN);
+	else
+		eap_method_respond(eap, buf, EAP_WSC_HEADER_LEN);
 }
 
 static void eap_wsc_handle_m8(struct eap_state *eap,
@@ -579,7 +614,7 @@ static void eap_wsc_send_m7(struct eap_state *eap,
 		return;
 
 	authenticator_put(wsc, m6_pdu, m6_len, pdu, pdu_len);
-	eap_wsc_send_response(eap, pdu, pdu_len);
+	eap_wsc_send_message(eap, pdu, pdu_len);
 	wsc->state = STATE_EXPECT_M8;
 }
 
@@ -624,7 +659,7 @@ static void eap_wsc_handle_m6(struct eap_state *eap,
 	}
 
 	/* We now have R-SNonce2, verify R-Hash2 stored in eap_wsc_handle_m4 */
-	if (!r_hash_check(wsc, m6es.r_snonce2, wsc->psk2, wsc->r_hash2)) {
+	if (!x_hash_check(wsc, m6es.r_snonce2, wsc->psk2, wsc->r_hash2)) {
 		error = WSC_CONFIGURATION_ERROR_DEVICE_PASSWORD_AUTH_FAILURE;
 		goto cleanup;
 	}
@@ -680,7 +715,7 @@ static void eap_wsc_send_m5(struct eap_state *eap,
 		return;
 
 	authenticator_put(wsc, m4_pdu, m4_len, pdu, pdu_len);
-	eap_wsc_send_response(eap, pdu, pdu_len);
+	eap_wsc_send_message(eap, pdu, pdu_len);
 	wsc->state = STATE_EXPECT_M6;
 }
 
@@ -725,7 +760,7 @@ static void eap_wsc_handle_m4(struct eap_state *eap,
 	}
 
 	/* Since we have obtained R-SNonce1, we can now verify R-Hash1. */
-	if (!r_hash_check(wsc, m4es.r_snonce1, wsc->psk1, m4.r_hash1)) {
+	if (!x_hash_check(wsc, m4es.r_snonce1, wsc->psk1, m4.r_hash1)) {
 		error = WSC_CONFIGURATION_ERROR_DEVICE_PASSWORD_AUTH_FAILURE;
 		goto cleanup;
 	}
@@ -812,7 +847,7 @@ static void eap_wsc_send_m3(struct eap_state *eap,
 		return;
 
 	authenticator_put(wsc, m2_pdu, m2_len, pdu, pdu_len);
-	eap_wsc_send_response(eap, pdu, pdu_len);
+	eap_wsc_send_message(eap, pdu, pdu_len);
 	wsc->state = STATE_EXPECT_M4;
 }
 
@@ -928,7 +963,8 @@ static void eap_wsc_handle_nack(struct eap_state *eap,
 	if (wsc_parse_wsc_nack(pdu, len, &nack) != 0)
 		return;
 
-	if (memcmp(nack.enrollee_nonce, wsc->m1->enrollee_nonce,
+	if (wsc->state != STATE_EXPECT_M1 && memcmp(nack.enrollee_nonce,
+						wsc->m1->enrollee_nonce,
 						sizeof(nack.enrollee_nonce)))
 		return;
 
@@ -944,10 +980,13 @@ static void eap_wsc_handle_nack(struct eap_state *eap,
 	 * to.  Our choice is to reflect back what the request error code is
 	 * in the response.
 	 */
-	eap_wsc_send_nack(eap, nack.configuration_error);
+	if (!wsc->registrar)
+		eap_wsc_send_nack(eap, nack.configuration_error);
+
+	eap_method_error(eap);
 }
 
-static void eap_wsc_handle_request(struct eap_state *eap,
+static void eap_wsc_handle_message(struct eap_state *eap,
 					const uint8_t *pkt, size_t len)
 {
 	struct eap_wsc_state *wsc = eap_get_data(eap);
@@ -957,6 +996,16 @@ static void eap_wsc_handle_request(struct eap_state *eap,
 	size_t pdu_len;
 	size_t rx_header_offset = 0;
 
+	/*
+	 * pkt[0:len] is an EAP-Request packet contents if !wsc->registrar
+	 * and an EAP-Response otherwise.  Most of the parsing is going to
+	 * be identical though.
+	 *
+	 * Since we have disjoint sets of enum state values between
+	 * Enrollee and Registrar, most of the time we don't need to look
+	 * at wsc->registrar.
+	 */
+
 	if (len < 2)
 		return;
 
@@ -978,7 +1027,7 @@ static void eap_wsc_handle_request(struct eap_state *eap,
 		if (!pdu)
 			return;
 
-		eap_wsc_send_response(eap, pdu, pdu_len);
+		eap_wsc_send_message(eap, pdu, pdu_len);
 		wsc->state = STATE_EXPECT_M2;
 		return;
 	case WSC_OP_NACK:
@@ -988,8 +1037,21 @@ static void eap_wsc_handle_request(struct eap_state *eap,
 		eap_wsc_handle_nack(eap, pkt, len);
 		return;
 	case WSC_OP_ACK:
+		/* Not used in the in-band Enrollee Registration scenario */
+		return;
 	case WSC_OP_DONE:
-		/* Should never receive these as Enrollee */
+		if (wsc->state != STATE_EXPECT_DONE)
+			return;
+
+		/*
+		 * Notify higher layers that the registration protocol has
+		 * finished successfully as the EAP result is going to
+		 * always be the same.  May not be strictly needed as the
+		 * higher layers get the real confirmation when the enrollee
+		 * uses the credentials on their next connection.
+		 */
+		eap_method_event(eap, EAP_WSC_EVENT_CREDENTIAL_SENT, NULL);
+		eap_method_error(eap);
 		return;
 	case WSC_OP_FRAG_ACK:
 		if (wsc->tx_last_frag_len &&
@@ -1046,16 +1108,14 @@ static void eap_wsc_handle_request(struct eap_state *eap,
 		}
 
 		if (flags & WSC_FLAG_MF) {
-			if (!wsc->rx_pdu_buf) {
-				eap_method_error(eap);
-				return;
-			}
+			if (!wsc->rx_pdu_buf)
+				goto invalid_frag;
 
 			eap_wsc_send_frag_ack(eap);
 			return;
 		} else if (wsc->rx_pdu_buf) {
 			if (wsc->rx_pdu_buf_len != wsc->rx_pdu_buf_offset) {
-				l_error("Request fragment pkt size mismatch");
+				l_error("Message fragment pkt size mismatch");
 				goto invalid_frag;
 			}
 
@@ -1593,7 +1653,7 @@ static struct eap_method eap_wsc = {
 	.exports_msk = true,
 	.name = "WSC",
 	.free = eap_wsc_free,
-	.handle_request = eap_wsc_handle_request,
+	.handle_request = eap_wsc_handle_message,
 	.handle_retransmit = eap_wsc_handle_retransmit,
 	.load_settings = eap_wsc_load_settings,
 };
@@ -1605,6 +1665,20 @@ static bool eap_wsc_r_load_settings(struct eap_state *eap,
 	return eap_wsc_load_common(eap, settings, true);
 }
 
+static bool eap_wsc_r_validate_identity(struct eap_state *eap,
+					const char *identity)
+{
+	struct eap_wsc_state *wsc = eap_get_data(eap);
+
+	if (strcmp(identity, "WFA-SimpleConfig-Enrollee-1-0"))
+		return false;
+
+	/* Identity Request/Response done, directly send the WSC_Start */
+	eap_wsc_r_send_start(eap);
+	wsc->state = STATE_EXPECT_M1;
+	return true;
+}
+
 static struct eap_method eap_wsc_r = {
 	.name = "WSC-R",
 	.request_type = EAP_TYPE_EXPANDED,
@@ -1613,6 +1687,8 @@ static struct eap_method eap_wsc_r = {
 	.exports_msk = true,
 	.free = eap_wsc_free,
 	.load_settings = eap_wsc_r_load_settings,
+	.validate_identity = eap_wsc_r_validate_identity,
+	.handle_response = eap_wsc_handle_message,
 };
 
 static int eap_wsc_init(void)
src/eap-wsc.h +1 -0
index 886fce81..ac4b7077 100644
@@ -22,4 +22,5 @@
 
 enum EAP_WSC_EVENT {
 	EAP_WSC_EVENT_CREDENTIAL_OBTAINED	= 0x0050f200,
+	EAP_WSC_EVENT_CREDENTIAL_SENT		= 0x0050f201,
 };
Signature · 1 line
2.25.1
#5 lore raw

PATCH 13/16 eap-wsc: Handle the M{1,3,5,7} messages

In reply to Andrew Zaborowski

Parse, validate and respond to the M1, M3, M5 and M7 messages and send the M2, M4, M6 and M8.

---
 src/eap-wsc.c | 496 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 496 insertions(+)
src/eap-wsc.c +496 -0
index 34de6033..60396003 100644
@@ -954,6 +954,483 @@ clear_keys:
 	explicit_bzero(&keys, sizeof(keys));
 }
 
+static void eap_wsc_r_send_m8(struct eap_state *eap,
+				const uint8_t *m7_pdu, size_t m7_len)
+{
+	struct eap_wsc_state *wsc = eap_get_data(eap);
+	struct wsc_m8_encrypted_settings m8es = {};
+	struct wsc_m8 m8;
+	uint8_t *pdu;
+	size_t pdu_len;
+	/* At least: 2 * credential, 12 for Authenticator, 16 for IV + up to 16 pad */
+	uint8_t encrypted[1024];
+	size_t encrypted_len;
+	bool r;
+	struct wsc_credential creds[2];
+	unsigned int creds_cnt = 0;
+	unsigned int auth_types =
+		wsc->m1->auth_type_flags & wsc->m2->auth_type_flags;
+
+	if (auth_types & WSC_AUTHENTICATION_TYPE_OPEN)
+		memcpy(&creds[creds_cnt++], &wsc->open_cred,
+			sizeof(struct wsc_credential));
+
+	if (auth_types & WSC_AUTHENTICATION_TYPE_WPA2_PERSONAL)
+		memcpy(&creds[creds_cnt++], &wsc->wpa2_cred,
+			sizeof(struct wsc_credential));
+
+	pdu = wsc_build_m8_encrypted_settings(&m8es, creds, creds_cnt,
+						&pdu_len);
+	explicit_bzero(creds, creds_cnt * sizeof(struct wsc_credential));
+	if (!pdu)
+		return;
+
+	keywrap_authenticator_put(wsc, pdu, pdu_len);
+	r = encrypted_settings_encrypt(wsc, wsc->iv3, pdu, pdu_len,
+						encrypted, &encrypted_len);
+	explicit_bzero(pdu, pdu_len);
+	l_free(pdu);
+
+	if (!r)
+		return;
+
+	m8.version2 = true;
+	memcpy(m8.enrollee_nonce, wsc->m1->enrollee_nonce,
+						sizeof(m8.enrollee_nonce));
+
+	pdu = wsc_build_m8(&m8, encrypted, encrypted_len, &pdu_len);
+	if (!pdu)
+		return;
+
+	authenticator_put(wsc, m7_pdu, m7_len, pdu, pdu_len);
+	eap_wsc_send_message(eap, pdu, pdu_len);
+	wsc->state = STATE_EXPECT_DONE;
+}
+
+static void eap_wsc_r_handle_m7(struct eap_state *eap,
+					const uint8_t *pdu, size_t len)
+{
+	struct eap_wsc_state *wsc = eap_get_data(eap);
+	struct wsc_m7 m7;
+	struct iovec encrypted;
+	uint8_t *decrypted;
+	size_t decrypted_len;
+	struct wsc_m7_encrypted_settings m7es;
+	enum wsc_configuration_error error = WSC_CONFIGURATION_ERROR_NO_ERROR;
+
+	/* Spec unclear what to do here, see comments in eap_wsc_send_nack */
+	if (wsc_parse_m7(pdu, len, &m7, &encrypted) != 0)
+		goto send_nack;
+
+	if (memcmp(m7.registrar_nonce, wsc->m2->registrar_nonce,
+						sizeof(m7.registrar_nonce)))
+		goto send_nack;
+
+	if (!authenticator_check(wsc, pdu, len))
+		return;
+
+	decrypted = encrypted_settings_decrypt(wsc, encrypted.iov_base,
+							encrypted.iov_len,
+							&decrypted_len);
+	if (!decrypted) {
+		error = WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE;
+		goto send_nack;
+	}
+
+	if (wsc_parse_m7_encrypted_settings(decrypted, decrypted_len, &m7es)) {
+		error = WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE;
+		goto cleanup;
+	}
+
+	if (!keywrap_authenticator_check(wsc, decrypted, decrypted_len)) {
+		error = WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE;
+		goto cleanup;
+	}
+
+	/* We have E-SNonce2, verify E-Hash2 stored in eap_wsc_r_handle_m3 */
+	if (!x_hash_check(wsc, m7es.e_snonce2, wsc->psk2, wsc->e_hash2)) {
+		error = WSC_CONFIGURATION_ERROR_DEVICE_PASSWORD_AUTH_FAILURE;
+		goto cleanup;
+	}
+
+	eap_wsc_r_send_m8(eap, pdu, len);
+
+cleanup:
+	explicit_bzero(&m7es, sizeof(m7es));
+	explicit_bzero(decrypted, decrypted_len);
+	l_free(decrypted);
+
+	if (!error)
+		return;
+
+send_nack:
+	eap_wsc_send_nack(eap, error);
+}
+
+static void eap_wsc_r_send_m6(struct eap_state *eap,
+				const uint8_t *m5_pdu, size_t m5_len)
+{
+	struct eap_wsc_state *wsc = eap_get_data(eap);
+	struct wsc_m6_encrypted_settings m6es;
+	struct wsc_m6 m6;
+	uint8_t *pdu;
+	size_t pdu_len;
+	/* 20 for SNonce, 12 for Authenticator, 16 for IV + up to 16 pad */
+	uint8_t encrypted[64];
+	size_t encrypted_len;
+	bool r;
+
+	memcpy(m6es.r_snonce2, wsc->local_snonce2, sizeof(wsc->local_snonce2));
+	pdu = wsc_build_m6_encrypted_settings(&m6es, &pdu_len);
+	explicit_bzero(m6es.r_snonce2, sizeof(wsc->local_snonce2));
+	if (!pdu)
+		return;
+
+	keywrap_authenticator_put(wsc, pdu, pdu_len);
+	r = encrypted_settings_encrypt(wsc, wsc->iv2, pdu, pdu_len,
+						encrypted, &encrypted_len);
+	explicit_bzero(pdu, pdu_len);
+	l_free(pdu);
+
+	if (!r)
+		return;
+
+	m6.version2 = true;
+	memcpy(m6.enrollee_nonce, wsc->m1->enrollee_nonce,
+						sizeof(m6.enrollee_nonce));
+
+	pdu = wsc_build_m6(&m6, encrypted, encrypted_len, &pdu_len);
+	if (!pdu)
+		return;
+
+	authenticator_put(wsc, m5_pdu, m5_len, pdu, pdu_len);
+	eap_wsc_send_message(eap, pdu, pdu_len);
+	wsc->state = STATE_EXPECT_M7;
+}
+
+static void eap_wsc_r_handle_m5(struct eap_state *eap,
+					const uint8_t *pdu, size_t len)
+{
+	struct eap_wsc_state *wsc = eap_get_data(eap);
+	struct wsc_m5 m5;
+	struct iovec encrypted;
+	uint8_t *decrypted;
+	size_t decrypted_len;
+	struct wsc_m5_encrypted_settings m5es;
+	enum wsc_configuration_error error = WSC_CONFIGURATION_ERROR_NO_ERROR;
+
+	/* Spec unclear what to do here, see comments in eap_wsc_send_nack */
+	if (wsc_parse_m5(pdu, len, &m5, &encrypted) != 0)
+		goto send_nack;
+
+	if (memcmp(m5.registrar_nonce, wsc->m2->registrar_nonce,
+						sizeof(m5.registrar_nonce)))
+		goto send_nack;
+
+	if (!authenticator_check(wsc, pdu, len))
+		return;
+
+	decrypted = encrypted_settings_decrypt(wsc, encrypted.iov_base,
+							encrypted.iov_len,
+							&decrypted_len);
+	if (!decrypted) {
+		error = WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE;
+		goto send_nack;
+	}
+
+	if (wsc_parse_m5_encrypted_settings(decrypted, decrypted_len, &m5es)) {
+		error = WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE;
+		goto cleanup;
+	}
+
+	if (!keywrap_authenticator_check(wsc, decrypted, decrypted_len)) {
+		error = WSC_CONFIGURATION_ERROR_DECRYPTION_CRC_FAILURE;
+		goto cleanup;
+	}
+
+	/* We have E-SNonce1, verify E-Hash1 stored in eap_wsc_r_handle_m3 */
+	if (!x_hash_check(wsc, m5es.e_snonce1, wsc->psk1, wsc->e_hash1)) {
+		error = WSC_CONFIGURATION_ERROR_DEVICE_PASSWORD_AUTH_FAILURE;
+		goto cleanup;
+	}
+
+	eap_wsc_r_send_m6(eap, pdu, len);
+
+cleanup:
+	explicit_bzero(&m5es, sizeof(m5es));
+	explicit_bzero(decrypted, decrypted_len);
+	l_free(decrypted);
+
+	if (!error)
+		return;
+
+send_nack:
+	eap_wsc_send_nack(eap, error);
+}
+
+static void eap_wsc_r_send_m4(struct eap_state *eap,
+				const uint8_t *m3_pdu, size_t m3_len)
+{
+	struct eap_wsc_state *wsc = eap_get_data(eap);
+	struct wsc_m4_encrypted_settings m4es;
+	struct wsc_m4 m4;
+	uint8_t *pdu;
+	size_t pdu_len;
+	/* 20 for SNonce, 12 for Authenticator, 16 for IV + up to 16 pad */
+	uint8_t encrypted[64];
+	size_t encrypted_len;
+	bool r;
+	size_t len;
+	size_t len_half1;
+	struct iovec iov[4];
+
+	memcpy(m4es.r_snonce1, wsc->local_snonce1, sizeof(wsc->local_snonce1));
+	pdu = wsc_build_m4_encrypted_settings(&m4es, &pdu_len);
+	explicit_bzero(m4es.r_snonce1, sizeof(wsc->local_snonce1));
+	if (!pdu)
+		return;
+
+	keywrap_authenticator_put(wsc, pdu, pdu_len);
+	r = encrypted_settings_encrypt(wsc, wsc->iv1, pdu, pdu_len,
+						encrypted, &encrypted_len);
+	explicit_bzero(pdu, pdu_len);
+	l_free(pdu);
+
+	if (!r)
+		return;
+
+	len = strlen(wsc->device_password);
+
+	/* WSC 2.0.5, Section 7.4:
+	 * In case the UTF8 representation of the DevicePassword length is an
+	 * odd number (N), the first half of DevicePassword will have length
+	 * of N/2+1 and the second half of the DevicePassword will have length
+	 * of N/2.
+	 */
+	len_half1 = (len + 1) / 2;
+
+	l_checksum_update(wsc->hmac_auth_key, wsc->device_password, len_half1);
+	l_checksum_get_digest(wsc->hmac_auth_key, wsc->psk1, sizeof(wsc->psk1));
+
+	l_checksum_update(wsc->hmac_auth_key, wsc->device_password + len_half1,
+					len / 2);
+	l_checksum_get_digest(wsc->hmac_auth_key, wsc->psk2, sizeof(wsc->psk2));
+
+	m4.version2 = true;
+	memcpy(m4.enrollee_nonce, wsc->m1->enrollee_nonce,
+						sizeof(m4.enrollee_nonce));
+
+	/* WSC 2.0.5, Section 7.4:
+	 * The Registrar creates two 128-bit secret nonces, R-S1, R-S2 and then
+	 * computes:
+	 * R-Hash1 = HMACAuthKey(R-S1 || PSK1 || PKE || PKR)
+	 * R-Hash2 = HMACAuthKey(R-S2 || PSK2 || PKE || PKR)
+	 */
+	iov[0].iov_base = wsc->local_snonce1;
+	iov[0].iov_len = sizeof(wsc->local_snonce1);
+	iov[1].iov_base = wsc->psk1;
+	iov[1].iov_len = sizeof(wsc->psk1);
+	iov[2].iov_base = wsc->m1->public_key;
+	iov[2].iov_len = sizeof(wsc->m1->public_key);
+	iov[3].iov_base = wsc->m2->public_key;
+	iov[3].iov_len = sizeof(wsc->m2->public_key);
+	l_checksum_updatev(wsc->hmac_auth_key, iov, 4);
+	l_checksum_get_digest(wsc->hmac_auth_key,
+					m4.r_hash1, sizeof(m4.r_hash1));
+
+	iov[0].iov_base = wsc->local_snonce2;
+	iov[0].iov_len = sizeof(wsc->local_snonce2);
+	iov[1].iov_base = wsc->psk2;
+	iov[1].iov_len = sizeof(wsc->psk2);
+	l_checksum_updatev(wsc->hmac_auth_key, iov, 4);
+	l_checksum_get_digest(wsc->hmac_auth_key,
+					m4.r_hash2, sizeof(m4.r_hash2));
+
+	pdu = wsc_build_m4(&m4, encrypted, encrypted_len, &pdu_len);
+	if (!pdu)
+		return;
+
+	authenticator_put(wsc, m3_pdu, m3_len, pdu, pdu_len);
+	eap_wsc_send_message(eap, pdu, pdu_len);
+	wsc->state = STATE_EXPECT_M5;
+}
+
+static void eap_wsc_r_handle_m3(struct eap_state *eap,
+					const uint8_t *pdu, size_t len)
+{
+	struct eap_wsc_state *wsc = eap_get_data(eap);
+	struct wsc_m3 m3;
+
+	if (wsc_parse_m3(pdu, len, &m3) != 0)
+		goto send_nack;
+
+	if (memcmp(m3.registrar_nonce, wsc->m2->registrar_nonce,
+						sizeof(m3.registrar_nonce)))
+		goto send_nack;
+
+	if (!authenticator_check(wsc, pdu, len))
+		return;
+
+	/* We can't verify these until we receive the E-S1/E-S2 in M5/M7 */
+	memcpy(wsc->e_hash1, m3.e_hash1, sizeof(m3.e_hash1));
+	memcpy(wsc->e_hash2, m3.e_hash2, sizeof(m3.e_hash2));
+
+	eap_wsc_r_send_m4(eap, pdu, len);
+	return;
+
+send_nack:
+	/* Spec unclear what to do here, see comments in eap_wsc_send_nack */
+	eap_wsc_send_nack(eap, WSC_CONFIGURATION_ERROR_NO_ERROR);
+}
+
+static void eap_wsc_r_send_m2(struct eap_state *eap,
+				const uint8_t *m1_pdu, size_t m1_len)
+{
+	struct eap_wsc_state *wsc = eap_get_data(eap);
+	uint8_t *pdu;
+	size_t pdu_len;
+
+	wsc->m2->version2 = true;
+	memcpy(wsc->m2->enrollee_nonce, wsc->m1->enrollee_nonce, 16);
+	wsc->m2->association_state = WSC_ASSOCIATION_STATE_NOT_ASSOCIATED;
+	wsc->m2->configuration_error = WSC_CONFIGURATION_ERROR_NO_ERROR;
+
+	pdu = wsc_build_m2(wsc->m2, &pdu_len);
+	if (!pdu)
+		return;
+
+	authenticator_put(wsc, m1_pdu, m1_len, pdu, pdu_len);
+	eap_wsc_send_message(eap, pdu, pdu_len);
+	wsc->state = STATE_EXPECT_M3;
+}
+
+static void eap_wsc_r_handle_m1(struct eap_state *eap,
+					const uint8_t *pdu, size_t len)
+{
+	struct eap_wsc_state *wsc = eap_get_data(eap);
+	struct l_key *remote_public;
+	uint8_t shared_secret[192] = { 0 };
+	size_t shared_secret_len = sizeof(shared_secret);
+	struct l_checksum *sha256;
+	uint8_t dhkey[32];
+	struct l_checksum *hmac_sha256;
+	struct iovec iov[3];
+	uint8_t kdk[32];
+	struct wsc_session_key keys;
+	bool r;
+	uint8_t expected_enrollee_mac[6];
+	uint8_t expected_uuid_e[16];
+
+	memcpy(expected_enrollee_mac, wsc->m1->addr, 6);
+	memcpy(expected_uuid_e, wsc->m1->uuid_e, 16);
+
+	/* Spec unclear what to do here, see comments in eap_wsc_send_nack */
+	if (wsc_parse_m1(pdu, len, wsc->m1) != 0) {
+		eap_wsc_send_nack(eap, WSC_CONFIGURATION_ERROR_NO_ERROR);
+		return;
+	}
+
+	if (memcmp(expected_enrollee_mac, wsc->m1->addr, 6) ||
+			memcmp(expected_uuid_e, wsc->m1->uuid_e, 16)) {
+		eap_wsc_send_nack(eap,
+			WSC_CONFIGURATION_ERROR_MULTIPLE_PBC_SESSIONS_DETECTED);
+		eap_method_error(eap);
+		return;
+	}
+
+	if (wsc->m1->state != WSC_STATE_NOT_CONFIGURED ||
+			!(wsc->m1->auth_type_flags &
+				wsc->m2->auth_type_flags) ||
+			!(wsc->m1->encryption_type_flags &
+				wsc->m2->encryption_type_flags) ||
+			!(wsc->m1->connection_type_flags &
+				wsc->m2->connection_type_flags) ||
+			!(wsc->m1->config_methods &
+				wsc->m2->config_methods) ||
+			!(wsc->m1->rf_bands & wsc->m2->rf_bands)) {
+		eap_wsc_send_nack(eap, WSC_CONFIGURATION_ERROR_NO_ERROR);
+		return;
+	}
+
+	if (wsc->m1->configuration_error != WSC_CONFIGURATION_ERROR_NO_ERROR ||
+			wsc->m1->device_password_id !=
+			wsc->m2->device_password_id) {
+		eap_wsc_send_nack(eap, WSC_CONFIGURATION_ERROR_NO_ERROR);
+		return;
+	}
+
+	/* m1->request_to_enroll not checked */
+
+	if (!l_key_validate_dh_payload(wsc->m1->public_key,
+					sizeof(wsc->m1->public_key),
+					crypto_dh5_prime,
+					crypto_dh5_prime_size))
+		return;
+
+	/*
+	 * Done validating the M1, we can now derive the keys for most of
+	 * the rest of the registration protocol.
+	 */
+
+	remote_public = l_key_new(L_KEY_RAW, wsc->m1->public_key,
+						sizeof(wsc->m1->public_key));
+	if (!remote_public)
+		return;
+
+	r = l_key_compute_dh_secret(remote_public, wsc->private, dh5_prime,
+					shared_secret, &shared_secret_len);
+	l_key_free(remote_public);
+
+	if (!r)
+		return;
+
+	sha256 = l_checksum_new(L_CHECKSUM_SHA256);
+	if (!sha256)
+		return;
+
+	l_checksum_update(sha256, shared_secret, shared_secret_len);
+	explicit_bzero(shared_secret, shared_secret_len);
+	l_checksum_get_digest(sha256, dhkey, sizeof(dhkey));
+	l_checksum_free(sha256);
+
+	hmac_sha256 = l_checksum_new_hmac(L_CHECKSUM_SHA256,
+							dhkey, sizeof(dhkey));
+	explicit_bzero(dhkey, sizeof(dhkey));
+
+	if (!hmac_sha256)
+		return;
+
+	iov[0].iov_base = wsc->m1->enrollee_nonce;
+	iov[0].iov_len = 16;
+	iov[1].iov_base = wsc->m1->addr;
+	iov[1].iov_len = 6;
+	iov[2].iov_base = wsc->m2->registrar_nonce;
+	iov[2].iov_len = 16;
+
+	l_checksum_updatev(hmac_sha256, iov, 3);
+	l_checksum_get_digest(hmac_sha256, kdk, sizeof(kdk));
+	l_checksum_free(hmac_sha256);
+
+	r = wsc_kdf(kdk, &keys, sizeof(keys));
+	explicit_bzero(kdk, sizeof(kdk));
+	if (!r)
+		return;
+
+	wsc->hmac_auth_key = l_checksum_new_hmac(L_CHECKSUM_SHA256,
+						keys.auth_key,
+						sizeof(keys.auth_key));
+
+	/*
+	 * AuthKey is uploaded into the kernel, once we upload KeyWrapKey,
+	 * the keys variable is no longer useful.  Make sure to wipe it
+	 */
+	wsc->aes_cbc_128 = l_cipher_new(L_CIPHER_AES_CBC, keys.keywrap_key,
+						sizeof(keys.keywrap_key));
+	explicit_bzero(&keys, sizeof(keys));
+
+	eap_wsc_r_send_m2(eap, pdu, len);
+}
+
 static void eap_wsc_handle_nack(struct eap_state *eap,
 					const uint8_t *pdu, size_t len)
 {
@@ -1130,6 +1607,7 @@ static void eap_wsc_handle_message(struct eap_state *eap,
 		return;
 
 	switch (wsc->state) {
+	/* Enrollee state machine */
 	case STATE_EXPECT_START:
 		return;
 	case STATE_EXPECT_M2:
@@ -1147,6 +1625,24 @@ static void eap_wsc_handle_message(struct eap_state *eap,
 	case STATE_FINISHED:
 		eap_wsc_send_nack(eap, WSC_CONFIGURATION_ERROR_NO_ERROR);
 		return;
+
+	/* Registrar state machine */
+	case STATE_EXPECT_M1:
+		eap_wsc_r_handle_m1(eap, pkt, len);
+		break;
+	case STATE_EXPECT_M3:
+		eap_wsc_r_handle_m3(eap, pkt, len);
+		break;
+	case STATE_EXPECT_M5:
+		eap_wsc_r_handle_m5(eap, pkt, len);
+		break;
+	case STATE_EXPECT_M7:
+		eap_wsc_r_handle_m7(eap, pkt, len);
+		break;
+	case STATE_EXPECT_IDENTITY:
+	case STATE_EXPECT_DONE:
+		eap_wsc_send_nack(eap, WSC_CONFIGURATION_ERROR_NO_ERROR);
+		return;
 	}
 
 	if (wsc->rx_pdu_buf) {
Signature · 1 line
2.25.1
#6 lore raw

PATCH 14/16 unit: Add an authenticator-side 4-Way Handshake test

In reply to Andrew Zaborowski

Test the eapol.c code responsible for the access point mode 4-way handshake with correct IEs and PSK on both sides (success scenario).

---
 unit/test-eapol.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 191 insertions(+)
unit/test-eapol.c +191 -0
index f6af6f06..b04466cb 100644
@@ -3513,6 +3513,194 @@ static void eapol_ft_handshake_test(const void *data)
 	eapol_exit();
 }
 
+struct test_ap_sta_data {
+	struct handshake_state *ap_hs;
+	struct handshake_state *sta_hs;
+	const uint8_t ap_address[6];
+	const uint8_t sta_address[6];
+
+	uint8_t to_sta_data[1024];
+	int to_sta_data_len;
+	uint8_t ap_tk[32];
+	uint8_t sta_tk[32];
+	bool ap_success;
+	bool sta_success;
+	int to_sta_msg_cnt;
+	int to_ap_msg_cnt;
+	struct eapol_sm *ap_sm;
+	struct eapol_sm *sta_sm;
+};
+
+static int test_ap_sta_eapol_tx(uint32_t ifindex,
+					const uint8_t *dest, uint16_t proto,
+					const struct eapol_frame *ef,
+					bool noencrypt, void *user_data)
+{
+	struct test_ap_sta_data *s = user_data;
+	size_t len = sizeof(struct eapol_header) +
+		L_BE16_TO_CPU(ef->header.packet_len);
+
+	assert(ifindex == s->ap_hs->ifindex || ifindex == s->sta_hs->ifindex);
+	assert(proto == ETH_P_PAE && !noencrypt);
+
+	if (ifindex == s->ap_hs->ifindex) {	/* From AP to STA */
+		assert(!memcmp(dest, s->sta_address, 6));
+		assert(len < sizeof(s->to_sta_data));
+		memcpy(s->to_sta_data, ef, len);
+		s->to_sta_data_len = len;
+		s->to_sta_msg_cnt++;
+		return 0;
+	}
+
+	/* From STA to AP */
+	assert(!memcmp(dest, s->ap_address, 6));
+	s->to_ap_msg_cnt++;
+	__eapol_rx_packet(1, s->sta_address, proto, (const void *) ef, len,
+				noencrypt);
+	return 0;
+}
+
+static void test_ap_sta_run(struct test_ap_sta_data *s)
+{
+	eap_init();
+	eapol_init();
+	__eapol_set_tx_packet_func(test_ap_sta_eapol_tx);
+	__eapol_set_tx_user_data(s);
+
+	s->ap_success = false;
+	s->sta_success = false;
+	s->to_sta_msg_cnt = 0;
+	s->to_ap_msg_cnt = 0;
+	s->to_sta_data_len = 0,
+
+	s->sta_sm = eapol_sm_new(s->sta_hs);
+	eapol_register(s->sta_sm);
+	eapol_start(s->sta_sm);
+
+	s->ap_sm = eapol_sm_new(s->ap_hs);
+	eapol_register(s->ap_sm);
+
+	while (s->to_sta_data_len) {
+		int len = s->to_sta_data_len;
+		s->to_sta_data_len = 0;
+		__eapol_rx_packet(s->sta_hs->ifindex, s->ap_address, ETH_P_PAE,
+					s->to_sta_data, len, false);
+	}
+
+	if (s->ap_sm)
+		eapol_sm_free(s->ap_sm);
+
+	if (s->sta_sm)
+		eapol_sm_free(s->sta_sm);
+
+	eapol_exit();
+	eap_exit();
+}
+
+struct test_ap_sta_hs {
+	struct handshake_state super;
+	struct test_ap_sta_data *s;
+};
+
+static struct handshake_state *test_ap_sta_hs_new(struct test_ap_sta_data *s,
+							uint32_t ifindex)
+{
+	struct test_ap_sta_hs *ths = l_new(struct test_ap_sta_hs, 1);
+
+	ths->super.ifindex = ifindex;
+	ths->super.free = (void (*)(struct handshake_state *s)) l_free;
+	ths->s = s;
+
+	return &ths->super;
+}
+
+static bool random_nonce(uint8_t nonce[])
+{
+	return l_getrandom(nonce, 32);
+}
+
+static void test_ap_sta_hs_event(struct handshake_state *hs,
+				enum handshake_event event,
+				void *user_data, ...)
+{
+	assert(event != HANDSHAKE_EVENT_FAILED);
+}
+
+static void test_ap_sta_install_tk(struct handshake_state *hs,
+					const uint8_t *tk, uint32_t cipher)
+{
+	struct test_ap_sta_hs *ths =
+		l_container_of(hs, struct test_ap_sta_hs, super);
+	assert(hs == ths->s->ap_hs || hs == ths->s->sta_hs);
+	assert(cipher == CRYPTO_CIPHER_CCMP);
+
+	if (hs == ths->s->ap_hs) {
+		assert(!ths->s->ap_success);
+		memcpy(ths->s->ap_tk, tk, 16);
+		ths->s->ap_success = true;
+	} else {
+		assert(!ths->s->sta_success);
+		memcpy(ths->s->sta_tk, tk, 16);
+		ths->s->sta_success = true;
+	}
+}
+
+static void eapol_ap_sta_handshake_test(const void *data)
+{
+	static const unsigned char ap_rsne[] = {
+		0x30, 0x14, 0x01, 0x00, 0x00, 0x0f, 0xac, 0x04,
+		0x01, 0x00, 0x00, 0x0f, 0xac, 0x04, 0x01, 0x00,
+		0x00, 0x0f, 0xac, 0x02, 0x81, 0x00 };
+	static const unsigned char sta_rsne[] = {
+		0x30, 0x12, 0x01, 0x00, 0x00, 0x0f, 0xac, 0x04,
+		0x01, 0x00, 0x00, 0x0f, 0xac, 0x04, 0x01, 0x00,
+		0x00, 0x0f, 0xac, 0x02 };
+	static const char *ssid = "TestWPA2PSK";
+	static const uint8_t psk[32] = {	/* secretsecret */
+		0x6a, 0xa3, 0xf0, 0x0b, 0x68, 0xbd, 0x8b, 0x46,
+		0x69, 0x83, 0xa5, 0x29, 0xa3, 0xfa, 0x57, 0x1c,
+		0x6c, 0x7b, 0x72, 0x41, 0x1d, 0xce, 0x33, 0x02,
+		0xa2, 0x2d, 0xdf, 0x77, 0xd1, 0x93, 0xdb, 0x5f };
+	struct test_ap_sta_data s = {
+		.ap_hs = test_ap_sta_hs_new(&s, 1),
+		.sta_hs = test_ap_sta_hs_new(&s, 2),
+		.ap_address = { 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 },
+		.sta_address = { 0x02, 0x03, 0x04, 0x05, 0x06, 0x08 },
+	};
+
+	__handshake_set_get_nonce_func(random_nonce);
+	__handshake_set_install_tk_func(test_ap_sta_install_tk);
+	__handshake_set_install_gtk_func(NULL);
+
+	handshake_state_set_authenticator(s.ap_hs, true);
+	handshake_state_set_event_func(s.ap_hs, test_ap_sta_hs_event, &s);
+	handshake_state_set_authenticator_address(s.ap_hs, s.ap_address);
+	handshake_state_set_supplicant_address(s.ap_hs, s.sta_address);
+	handshake_state_set_supplicant_ie(s.ap_hs, sta_rsne);
+	handshake_state_set_authenticator_ie(s.ap_hs, ap_rsne);
+	handshake_state_set_ssid(s.ap_hs, (void *) ssid, strlen(ssid));
+	handshake_state_set_pmk(s.ap_hs, psk, 32);
+
+	handshake_state_set_authenticator(s.sta_hs, false);
+	handshake_state_set_event_func(s.sta_hs, test_ap_sta_hs_event, &s);
+	handshake_state_set_authenticator_address(s.sta_hs, s.ap_address);
+	handshake_state_set_supplicant_address(s.sta_hs, s.sta_address);
+	handshake_state_set_supplicant_ie(s.sta_hs, sta_rsne);
+	handshake_state_set_authenticator_ie(s.sta_hs, ap_rsne);
+	handshake_state_set_ssid(s.sta_hs, (void *) ssid, strlen(ssid));
+	handshake_state_set_pmk(s.sta_hs, psk, 32);
+
+	test_ap_sta_run(&s);
+
+	handshake_state_free(s.ap_hs);
+	handshake_state_free(s.sta_hs);
+	__handshake_set_install_tk_func(NULL);
+
+	assert(s.ap_success && s.sta_success);
+	assert(s.to_ap_msg_cnt == 2 && s.to_sta_msg_cnt == 2);
+	assert(!memcmp(s.ap_tk, s.sta_tk, 16));
+}
+
 #define IS_ENABLED(config_macro) _IS_ENABLED1(config_macro)
 #define _IS_ENABLED1(config_macro) _IS_ENABLED2(_XXXX##config_macro)
 #define _XXXX1 _YYYY,
@@ -3653,6 +3841,9 @@ int main(int argc, char *argv[])
 	l_test_add("EAPoL/FT-Using-PSK 4-Way Handshake",
 			&eapol_ft_handshake_test, NULL);
 
+	l_test_add("EAPoL/Supplicant+Authenticator 4-Way Handshake",
+			&eapol_ap_sta_handshake_test, NULL);
+
 done:
 	return l_test_run();
 }
Signature · 1 line
2.25.1
#7 lore raw

PATCH 15/16 unit: Authenticator 4-way handshake error scenario

In reply to Andrew Zaborowski
---
 unit/test-eapol.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 68 insertions(+)
unit/test-eapol.c +68 -0
index b04466cb..e15e567a 100644
@@ -3701,6 +3701,72 @@ static void eapol_ap_sta_handshake_test(const void *data)
 	assert(!memcmp(s.ap_tk, s.sta_tk, 16));
 }
 
+static void eapol_ap_sta_handshake_bad_psk_test(const void *data)
+{
+	static const unsigned char ap_rsne[] = {
+		0x30, 0x14, 0x01, 0x00, 0x00, 0x0f, 0xac, 0x04,
+		0x01, 0x00, 0x00, 0x0f, 0xac, 0x04, 0x01, 0x00,
+		0x00, 0x0f, 0xac, 0x02, 0x81, 0x00 };
+	static const unsigned char sta_rsne[] = {
+		0x30, 0x12, 0x01, 0x00, 0x00, 0x0f, 0xac, 0x04,
+		0x01, 0x00, 0x00, 0x0f, 0xac, 0x04, 0x01, 0x00,
+		0x00, 0x0f, 0xac, 0x02 };
+	static const char *ssid = "TestWPA2PSK";
+	static const uint8_t psk1[32] = {	/* secretsecret */
+		0x6a, 0xa3, 0xf0, 0x0b, 0x68, 0xbd, 0x8b, 0x46,
+		0x69, 0x83, 0xa5, 0x29, 0xa3, 0xfa, 0x57, 0x1c,
+		0x6c, 0x7b, 0x72, 0x41, 0x1d, 0xce, 0x33, 0x02,
+		0xa2, 0x2d, 0xdf, 0x77, 0xd1, 0x93, 0xdb, 0x5f };
+	static const uint8_t psk2[32] = {	/* wrongpasswd */
+		0x04, 0x86, 0x45, 0x37, 0x4c, 0x95, 0xdd, 0x1e,
+		0x96, 0xbb, 0xdf, 0x12, 0x0a, 0x1c, 0x4c, 0x5b,
+		0x6d, 0x22, 0xc1, 0x1c, 0xa2, 0xe0, 0x15, 0x99,
+		0xbf, 0x82, 0x10, 0xd3, 0x59, 0x2c, 0xfb, 0x2d };
+	struct test_ap_sta_data s = {
+		.ap_hs = test_ap_sta_hs_new(&s, 1),
+		.sta_hs = test_ap_sta_hs_new(&s, 2),
+		.ap_address = { 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 },
+		.sta_address = { 0x02, 0x03, 0x04, 0x05, 0x06, 0x08 },
+	};
+
+	__handshake_set_get_nonce_func(random_nonce);
+	__handshake_set_install_tk_func(test_ap_sta_install_tk);
+	__handshake_set_install_gtk_func(NULL);
+
+	handshake_state_set_authenticator(s.ap_hs, true);
+	handshake_state_set_event_func(s.ap_hs, test_ap_sta_hs_event, &s);
+	handshake_state_set_authenticator_address(s.ap_hs, s.ap_address);
+	handshake_state_set_supplicant_address(s.ap_hs, s.sta_address);
+	handshake_state_set_supplicant_ie(s.ap_hs, sta_rsne);
+	handshake_state_set_authenticator_ie(s.ap_hs, ap_rsne);
+	handshake_state_set_ssid(s.ap_hs, (void *) ssid, strlen(ssid));
+	handshake_state_set_pmk(s.ap_hs, psk1, 32);
+
+	handshake_state_set_authenticator(s.sta_hs, false);
+	handshake_state_set_event_func(s.sta_hs, test_ap_sta_hs_event, &s);
+	handshake_state_set_authenticator_address(s.sta_hs, s.ap_address);
+	handshake_state_set_supplicant_address(s.sta_hs, s.sta_address);
+	handshake_state_set_supplicant_ie(s.sta_hs, sta_rsne);
+	handshake_state_set_authenticator_ie(s.sta_hs, ap_rsne);
+	handshake_state_set_ssid(s.sta_hs, (void *) ssid, strlen(ssid));
+	handshake_state_set_pmk(s.sta_hs, psk2, 32);
+
+	test_ap_sta_run(&s);
+
+	handshake_state_free(s.ap_hs);
+	handshake_state_free(s.sta_hs);
+	__handshake_set_install_tk_func(NULL);
+
+	/*
+	 * There should have been 2 EAPoL-Key frame exchanged: 1/4 and
+	 * 2/4 after which the AP will have detected wrong MIC and not
+	 * sent any more messages with both sides waiting for the
+	 * timeout.
+	 */
+	assert(!s.ap_success && !s.sta_success);
+	assert(s.to_ap_msg_cnt == 1 && s.to_sta_msg_cnt == 1);
+}
+
 #define IS_ENABLED(config_macro) _IS_ENABLED1(config_macro)
 #define _IS_ENABLED1(config_macro) _IS_ENABLED2(_XXXX##config_macro)
 #define _XXXX1 _YYYY,
@@ -3843,6 +3909,8 @@ int main(int argc, char *argv[])
 
 	l_test_add("EAPoL/Supplicant+Authenticator 4-Way Handshake",
 			&eapol_ap_sta_handshake_test, NULL);
+	l_test_add("EAPoL/Supplicant+Authenticator 4-Way Handshake Bad PSK",
+			&eapol_ap_sta_handshake_bad_psk_test, NULL);
 
 done:
 	return l_test_run();
Signature · 1 line
2.25.1
#8 lore raw

PATCH 16/16 unit: Test a EAP-WSC-R setup in test-eapol

In reply to Andrew Zaborowski
---
 Makefile.am       |   4 +-
 unit/test-eapol.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 169 insertions(+), 1 deletion(-)
Makefile.am +3 -1
index e83dbeee..9bb97760 100644
@@ -457,7 +457,9 @@ unit_test_eapol_SOURCES = unit/test-eapol.c \
 				src/eap-md5.c src/util.c \
 				src/eap-tls-common.h src/eap-tls-common.c \
 				src/erp.h src/erp.c \
-				src/mschaputil.h src/mschaputil.c
+				src/mschaputil.h src/mschaputil.c \
+				src/eap-wsc.h src/eap-wsc.c \
+				src/wscutil.h src/wscutil.c
 unit_test_eapol_LDADD = $(ell_ldadd)
 unit_test_eapol_DEPENDENCIES = $(ell_dependencies) \
 				unit/cert-server.pem \
unit/test-eapol.c +166 -0

184 lines not on this page · raw message

Signature · 1 line
2.25.1