Merge branch 'feature/put_supplicant_source_code_from_VNC_to_IDF' into 'master'
wpa_supplicant: move part of codes to IDF See merge request idf/esp-idf!2272
This commit is contained in:
commit
b9aee83d9b
|
@ -15,6 +15,8 @@
|
|||
#ifndef ESP_WPA2_H
|
||||
#define ESP_WPA2_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_wifi_crypto_types.h"
|
||||
|
||||
|
@ -121,7 +123,7 @@ void esp_wifi_sta_wpa2_ent_clear_password(void);
|
|||
* @attention 1. The API only passes the parameter password to the global pointer variable in wpa2 enterprise module.
|
||||
* @attention 2. The new password is used to substitute the old password when eap-mschapv2 failure request message with error code ERROR_PASSWD_EXPIRED is received.
|
||||
*
|
||||
* @param password: point to address where stores the password;
|
||||
* @param new_password: point to address where stores the password;
|
||||
* @param len: length of password
|
||||
*
|
||||
* @return
|
||||
|
@ -130,7 +132,7 @@ void esp_wifi_sta_wpa2_ent_clear_password(void);
|
|||
* - ESP_ERR_NO_MEM: fail(internal memory malloc fail)
|
||||
*/
|
||||
|
||||
esp_err_t esp_wifi_sta_wpa2_ent_set_new_password(const unsigned char *password, int len);
|
||||
esp_err_t esp_wifi_sta_wpa2_ent_set_new_password(const unsigned char *new_password, int len);
|
||||
|
||||
/**
|
||||
* @brief Clear new password for MSCHAPv2 method..
|
||||
|
@ -144,12 +146,12 @@ void esp_wifi_sta_wpa2_ent_clear_new_password(void);
|
|||
* @attention 2. The ca_cert should be zero terminated.
|
||||
*
|
||||
* @param ca_cert: point to address where stores the CA certificate;
|
||||
* @param len: length of ca_cert
|
||||
* @param ca_cert_len: length of ca_cert
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: succeed
|
||||
*/
|
||||
esp_err_t esp_wifi_sta_wpa2_ent_set_ca_cert(const unsigned char *ca_cert, int len);
|
||||
esp_err_t esp_wifi_sta_wpa2_ent_set_ca_cert(const unsigned char *ca_cert, int ca_cert_len);
|
||||
|
||||
/**
|
||||
* @brief Clear CA certificate for PEAP/TTLS method.
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit f46327a4c68b1e4e6a3fb3febd3ba3312df16ff1
|
||||
Subproject commit 92a091649f7c8476580177e2a769b15d9c4c191f
|
|
@ -1,4 +1,4 @@
|
|||
COMPONENT_ADD_INCLUDEDIRS := include port/include ../esp32/include
|
||||
COMPONENT_SRCDIRS := src/crypto port src/fast_crypto
|
||||
COMPONENT_SRCDIRS := src/crypto port src/fast_crypto src/wpa2/eap_peer src/wpa2/tls src/wpa2/utils src/wps
|
||||
|
||||
CFLAGS += -DEMBEDDED_SUPP -D__ets__ -Wno-strict-aliasing
|
||||
CFLAGS += -DEMBEDDED_SUPP -DIEEE8021X_EAPOL -DEAP_PEER_METHOD -DEAP_MSCHAPv2 -DEAP_TTLS -DEAP_TLS -DEAP_PEAP -DUSE_WPA2_TASK -DCONFIG_WPS2 -DCONFIG_WPS_PIN -DUSE_WPS_TASK -DESPRESSIF_USE -DESP32_WORKAROUND -D__ets__ -Wno-strict-aliasing
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#ifndef LIST_H
|
||||
#define LIST_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* struct dl_list - Doubly-linked list
|
||||
*/
|
||||
|
|
|
@ -15,15 +15,11 @@
|
|||
#ifndef WPA_H
|
||||
#define WPA_H
|
||||
|
||||
#include "c_types.h"
|
||||
#include "os_type.h"
|
||||
#include "rom/ets_sys.h"
|
||||
#include "common.h"
|
||||
#include "ets_sys.h"
|
||||
#include "wpa/defs.h"
|
||||
#include "wpa/wpa_common.h"
|
||||
|
||||
//#include "net80211/ieee80211_var.h"
|
||||
//#include "net80211/ieee80211_node.h"
|
||||
|
||||
#define WPA_SM_STATE(_sm) ((_sm)->wpa_state)
|
||||
|
||||
|
@ -51,10 +47,6 @@ struct wpa_sm {
|
|||
u8 pmk[PMK_LEN];
|
||||
size_t pmk_len;
|
||||
|
||||
// char *passphrase; //wlan password
|
||||
// u8 *ssid; //wlan network name
|
||||
// size_t ssid_len;
|
||||
|
||||
struct wpa_ptk ptk, tptk;
|
||||
int ptk_set, tptk_set;
|
||||
u8 snonce[WPA_NONCE_LEN];
|
||||
|
@ -64,8 +56,6 @@ struct wpa_sm {
|
|||
int rx_replay_counter_set;
|
||||
u8 request_counter[WPA_REPLAY_COUNTER_LEN];
|
||||
|
||||
// void *network_ctx;
|
||||
|
||||
unsigned int pairwise_cipher;
|
||||
unsigned int group_cipher;
|
||||
unsigned int key_mgmt;
|
||||
|
@ -74,7 +64,7 @@ struct wpa_sm {
|
|||
int rsn_enabled; /* Whether RSN is enabled in configuration */
|
||||
|
||||
int countermeasures; /*TKIP countermeasures state flag, 1:in countermeasures state*/
|
||||
os_timer_t cm_timer;
|
||||
ETSTimer cm_timer;
|
||||
|
||||
u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
|
||||
size_t assoc_wpa_ie_len;
|
||||
|
@ -95,21 +85,17 @@ struct wpa_sm {
|
|||
struct install_key install_ptk;
|
||||
struct install_key install_gtk;
|
||||
int key_entry_valid; //present current avaliable entry for bssid, for pairkey:0,5,10,15,20, gtk: pairkey_no+i (i:1~4)
|
||||
|
||||
// char *msg; //send eapol msg buff
|
||||
// size_t msg_len; //msg length:6 + sizeof(eth) + data_len
|
||||
|
||||
// struct netif *ifp;
|
||||
struct pbuf *pb;
|
||||
|
||||
void (* sendto) (struct pbuf *pb);
|
||||
void (*config_assoc_ie) (uint8 proto, u8 *assoc_buf, u32 assoc_wpa_ie_len);
|
||||
void (*install_ppkey) (enum wpa_alg alg, uint8 *addr, int key_idx, int set_tx,
|
||||
uint8 *seq, size_t seq_len, uint8 *key, size_t key_len, int key_entry_valid);
|
||||
void (*wpa_deauthenticate)(uint8 reason_code);
|
||||
void (*config_assoc_ie) (u8 proto, u8 *assoc_buf, u32 assoc_wpa_ie_len);
|
||||
void (*install_ppkey) (enum wpa_alg alg, u8 *addr, int key_idx, int set_tx,
|
||||
u8 *seq, unsigned int seq_len, u8 *key, unsigned int key_len, int key_entry_valid);
|
||||
void (*wpa_deauthenticate)(u8 reason_code);
|
||||
void (*wpa_neg_complete)();
|
||||
struct wpa_gtk_data gd; //used for calllback save param
|
||||
uint16 key_info; //used for txcallback param
|
||||
u16 key_info; //used for txcallback param
|
||||
};
|
||||
|
||||
struct l2_ethhdr {
|
||||
|
@ -185,9 +171,9 @@ struct l2_ethhdr {
|
|||
|
||||
#define KEYENTRY_TABLE_MAP(key_entry_valid) ((key_entry_valid)%5)
|
||||
|
||||
void pp_michael_mic_failure(uint16 isunicast);
|
||||
|
||||
void wpa_sm_set_state(enum wpa_states state);
|
||||
|
||||
char * dup_binstr(const void *src, size_t len);
|
||||
|
||||
#endif /* WPA_H */
|
||||
|
||||
|
|
|
@ -15,8 +15,22 @@
|
|||
#ifndef WPA_DEBUG_H
|
||||
#define WPA_DEBUG_H
|
||||
|
||||
#include "wpabuf.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#ifdef ESPRESSIF_USE
|
||||
|
||||
#define TAG "wpa"
|
||||
|
||||
#define MSG_ERROR ESP_LOG_ERROR
|
||||
#define MSG_WARNING ESP_LOG_WARN
|
||||
#define MSG_INFO ESP_LOG_INFO
|
||||
#define MSG_DEBUG ESP_LOG_DEBUG
|
||||
#define MSG_MSGDUMP ESP_LOG_VERBOSE
|
||||
|
||||
#else
|
||||
enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR };
|
||||
#endif
|
||||
|
||||
/** EAP authentication completed successfully */
|
||||
#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS "
|
||||
|
@ -44,8 +58,8 @@ void wpa_debug_print_timestamp(void);
|
|||
*
|
||||
* Note: New line '\n' is added to the end of the text when printing to stdout.
|
||||
*/
|
||||
//#define DEBUG_PRINT
|
||||
//#define MSG_PRINT
|
||||
#define DEBUG_PRINT
|
||||
#define MSG_PRINT
|
||||
|
||||
/**
|
||||
* wpa_hexdump - conditional hex dump
|
||||
|
@ -59,7 +73,7 @@ void wpa_debug_print_timestamp(void);
|
|||
* configuration. The contents of buf is printed out has hex dump.
|
||||
*/
|
||||
#ifdef DEBUG_PRINT
|
||||
#define wpa_printf(level,fmt, args...) ets_printf(fmt,## args)
|
||||
#define wpa_printf(level,fmt, args...) ESP_LOG_LEVEL_LOCAL(level, TAG, fmt, ##args)
|
||||
|
||||
static inline void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len)
|
||||
{
|
||||
|
|
|
@ -10,15 +10,45 @@
|
|||
#define EAP_H
|
||||
|
||||
#include "wpa/defs.h"
|
||||
#include "eap/eap_defs.h"
|
||||
#include "wpa2/eap_peer/eap_defs.h"
|
||||
|
||||
struct eap_sm;
|
||||
|
||||
struct eap_method_type {
|
||||
int vendor;
|
||||
u32 method;
|
||||
EapType method;
|
||||
};
|
||||
|
||||
u8 *g_wpa_anonymous_identity;
|
||||
int g_wpa_anonymous_identity_len;
|
||||
u8 *g_wpa_username;
|
||||
int g_wpa_username_len;
|
||||
const u8 *g_wpa_client_cert;
|
||||
int g_wpa_client_cert_len;
|
||||
const u8 *g_wpa_private_key;
|
||||
int g_wpa_private_key_len;
|
||||
const u8 *g_wpa_private_key_passwd;
|
||||
int g_wpa_private_key_passwd_len;
|
||||
|
||||
const u8 *g_wpa_ca_cert;
|
||||
int g_wpa_ca_cert_len;
|
||||
|
||||
u8 *g_wpa_password;
|
||||
int g_wpa_password_len;
|
||||
|
||||
u8 *g_wpa_new_password;
|
||||
int g_wpa_new_password_len;
|
||||
|
||||
const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len);
|
||||
void eap_deinit_prev_method(struct eap_sm *sm, const char *txt);
|
||||
struct wpabuf * eap_sm_build_nak(struct eap_sm *sm, EapType type, u8 id);
|
||||
int eap_peer_blob_init(struct eap_sm *sm);
|
||||
void eap_peer_blob_deinit(struct eap_sm *sm);
|
||||
int eap_peer_config_init(
|
||||
struct eap_sm *sm, u8 *private_key_passwd,
|
||||
int private_key_passwd_len);
|
||||
void eap_peer_config_deinit(struct eap_sm *sm);
|
||||
void eap_sm_abort(struct eap_sm *sm);
|
||||
int eap_peer_register_methods(void);
|
||||
|
||||
#endif /* EAP_H */
|
||||
|
|
|
@ -26,6 +26,10 @@ struct eap_peer_config {
|
|||
*/
|
||||
size_t identity_len;
|
||||
|
||||
u8 *anonymous_identity;
|
||||
|
||||
size_t anonymous_identity_len;
|
||||
|
||||
/**
|
||||
* password - Password string for EAP
|
||||
*
|
||||
|
@ -139,8 +143,29 @@ struct eap_peer_config {
|
|||
*/
|
||||
u8 *private_key_passwd;
|
||||
|
||||
/**
|
||||
* Phase 2
|
||||
*/
|
||||
u8 *ca_cert2;
|
||||
|
||||
u8 *ca_path2;
|
||||
|
||||
u8 *client_cert2;
|
||||
|
||||
u8 *private_key2;
|
||||
|
||||
u8 *private_key2_password;
|
||||
|
||||
/**
|
||||
* eap_methods - Allowed EAP methods
|
||||
*/
|
||||
struct eap_method_type *eap_methods;
|
||||
|
||||
|
||||
char *phase1;
|
||||
|
||||
char *phase2;
|
||||
|
||||
/**
|
||||
* pin - PIN for USIM, GSM SIM, and smartcards
|
||||
*
|
||||
|
@ -152,6 +177,10 @@ struct eap_peer_config {
|
|||
*/
|
||||
char *pin;
|
||||
|
||||
int mschapv2_retry;
|
||||
u8 *new_password;
|
||||
size_t new_password_len;
|
||||
|
||||
/**
|
||||
* fragment_size - Maximum EAP fragment size in bytes (default 1398)
|
||||
*
|
||||
|
@ -204,7 +233,7 @@ struct wpa_config_blob {
|
|||
/**
|
||||
* data - Pointer to binary data
|
||||
*/
|
||||
u8 *data;
|
||||
const u8 *data;
|
||||
|
||||
/**
|
||||
* len - Length of binary data
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "eap.h"
|
||||
#include "eap_common.h"
|
||||
#include "eap_config.h"
|
||||
#include "esp_wpa2.h"
|
||||
|
||||
/* RFC 4137 - EAP Peer state machine */
|
||||
|
||||
|
@ -54,11 +55,48 @@ struct eap_method_ret {
|
|||
Boolean allowNotifications;
|
||||
};
|
||||
|
||||
struct eap_sm;
|
||||
|
||||
struct eap_method {
|
||||
/**
|
||||
* vendor -EAP Vendor-ID
|
||||
*/
|
||||
int vendor;
|
||||
|
||||
/**
|
||||
* method - EAP type number
|
||||
*/
|
||||
EapType method;
|
||||
|
||||
/**
|
||||
* name - Name of the method (e.g., "TLS")
|
||||
*/
|
||||
const char *name;
|
||||
|
||||
struct eap_method *next;
|
||||
|
||||
void * (*init)(struct eap_sm *sm);
|
||||
void (*deinit)(struct eap_sm *sm, void *priv);
|
||||
struct wpabuf * (*process)(struct eap_sm *sm, void *priv,
|
||||
struct eap_method_ret *ret,
|
||||
const struct wpabuf *reqData);
|
||||
bool (*isKeyAvailable)(struct eap_sm *sm, void *priv);
|
||||
u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
|
||||
int (*get_status)(struct eap_sm *sm, void *priv, char *buf,
|
||||
size_t buflen, int verbose);
|
||||
const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len);
|
||||
void (*free)(struct eap_method *method);
|
||||
bool (*has_reauth_data)(struct eap_sm *sm, void *priv);
|
||||
void (*deinit_for_reauth)(struct eap_sm *sm, void *priv);
|
||||
void * (*init_for_reauth)(struct eap_sm *sm, void *priv);
|
||||
u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len);
|
||||
};
|
||||
|
||||
#define CLIENT_CERT_NAME "CLC"
|
||||
#define CA_CERT_NAME "CAC"
|
||||
#define PRIVATE_KEY_NAME "PVK"
|
||||
#define BLOB_NAME_LEN 3
|
||||
#define BLOB_NUM 2
|
||||
#define BLOB_NUM 3
|
||||
|
||||
/**
|
||||
* struct eap_sm - EAP state machine data
|
||||
|
@ -80,9 +118,26 @@ struct eap_sm {
|
|||
u8 wpa2_sig_cnt[SIG_WPA2_NUM];
|
||||
#endif
|
||||
u8 finish_state;
|
||||
|
||||
int init_phase2;
|
||||
bool peap_done;
|
||||
|
||||
u8 *eapKeyData;
|
||||
size_t eapKeyDataLen;
|
||||
struct wpabuf *lastRespData;
|
||||
const struct eap_method *m;
|
||||
};
|
||||
|
||||
wpa2_crypto_funcs_t wpa2_crypto_funcs;
|
||||
|
||||
const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);
|
||||
const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len);
|
||||
const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash);
|
||||
const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len);
|
||||
struct eap_peer_config * eap_get_config(struct eap_sm *sm);
|
||||
const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm, const char *name);
|
||||
bool wifi_sta_get_enterprise_disable_time_check(void);
|
||||
|
||||
struct wpabuf * eap_sm_build_identity_resp(struct eap_sm *sm, u8 id, int encrypted);
|
||||
|
||||
#endif /* EAP_I_H */
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* EAP peer: Method registration
|
||||
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef EAP_METHODS_H
|
||||
#define EAP_METHODS_H
|
||||
|
||||
#include "eap_defs.h"
|
||||
|
||||
const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method);
|
||||
const struct eap_method * eap_peer_get_methods(size_t *count);
|
||||
|
||||
u32 eap_get_phase2_type(const char *name, int *vendor);
|
||||
struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
|
||||
size_t *count);
|
||||
|
||||
struct eap_method * eap_peer_method_alloc(int verdor, EapType method,
|
||||
const char *name);
|
||||
|
||||
void eap_peer_method_free(struct eap_method *method);
|
||||
int eap_peer_method_register(struct eap_method *method);
|
||||
|
||||
void eap_peer_unregister_methods(void);
|
||||
|
||||
//int eap_peer_md5_register(void);
|
||||
int eap_peer_tls_register(void);
|
||||
int eap_peer_peap_register(void);
|
||||
int eap_peer_ttls_register(void);
|
||||
int eap_peer_mschapv2_register(void);
|
||||
|
||||
#endif /* EAP_METHODS_H */
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* EAP-PEAP common routines
|
||||
* Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef EAP_PEAP_COMMON_H
|
||||
#define EAP_PEAP_COMMON_H
|
||||
|
||||
int peap_prfplus(int version, const u8 *key, size_t key_len,
|
||||
const char *label, const u8 *seed, size_t seed_len,
|
||||
u8 *buf, size_t buf_len);
|
||||
|
||||
#endif /* EAP_PEAP_COMMON_H */
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* EAP-TLV definitions (draft-josefsson-pppext-eap-tls-eap-10.txt)
|
||||
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef EAP_TLV_COMMON_H
|
||||
#define EAP_TLV_COMMON_H
|
||||
|
||||
/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-10.txt) */
|
||||
#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */
|
||||
#define EAP_TLV_NAK_TLV 4
|
||||
#define EAP_TLV_ERROR_CODE_TLV 5
|
||||
#define EAP_TLV_CONNECTION_BINDING_TLV 6
|
||||
#define EAP_TLV_VENDOR_SPECIFIC_TLV 7
|
||||
#define EAP_TLV_URI_TLV 8
|
||||
#define EAP_TLV_EAP_PAYLOAD_TLV 9
|
||||
#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10
|
||||
#define EAP_TLV_PAC_TLV 11 /* RFC 5422, Section 4.2 */
|
||||
#define EAP_TLV_CRYPTO_BINDING_TLV 12
|
||||
#define EAP_TLV_CALLING_STATION_ID_TLV 13
|
||||
#define EAP_TLV_CALLED_STATION_ID_TLV 14
|
||||
#define EAP_TLV_NAS_PORT_TYPE_TLV 15
|
||||
#define EAP_TLV_SERVER_IDENTIFIER_TLV 16
|
||||
#define EAP_TLV_IDENTITY_TYPE_TLV 17
|
||||
#define EAP_TLV_SERVER_TRUSTED_ROOT_TLV 18
|
||||
#define EAP_TLV_REQUEST_ACTION_TLV 19
|
||||
#define EAP_TLV_PKCS7_TLV 20
|
||||
|
||||
#define EAP_TLV_RESULT_SUCCESS 1
|
||||
#define EAP_TLV_RESULT_FAILURE 2
|
||||
|
||||
#define EAP_TLV_TYPE_MANDATORY 0x8000
|
||||
#define EAP_TLV_TYPE_MASK 0x3fff
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma pack(push, 1)
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
struct eap_tlv_hdr {
|
||||
be16 tlv_type;
|
||||
be16 length;
|
||||
} STRUCT_PACKED;
|
||||
|
||||
struct eap_tlv_nak_tlv {
|
||||
be16 tlv_type;
|
||||
be16 length;
|
||||
be32 vendor_id;
|
||||
be16 nak_type;
|
||||
} STRUCT_PACKED;
|
||||
|
||||
struct eap_tlv_result_tlv {
|
||||
be16 tlv_type;
|
||||
be16 length;
|
||||
be16 status;
|
||||
} STRUCT_PACKED;
|
||||
|
||||
/* RFC 4851, Section 4.2.7 - Intermediate-Result TLV */
|
||||
struct eap_tlv_intermediate_result_tlv {
|
||||
be16 tlv_type;
|
||||
be16 length;
|
||||
be16 status;
|
||||
/* Followed by optional TLVs */
|
||||
} STRUCT_PACKED;
|
||||
|
||||
/* RFC 4851, Section 4.2.8 - Crypto-Binding TLV */
|
||||
struct eap_tlv_crypto_binding_tlv {
|
||||
be16 tlv_type;
|
||||
be16 length;
|
||||
u8 reserved;
|
||||
u8 version;
|
||||
u8 received_version;
|
||||
u8 subtype;
|
||||
u8 nonce[32];
|
||||
u8 compound_mac[20];
|
||||
} STRUCT_PACKED;
|
||||
|
||||
struct eap_tlv_pac_ack_tlv {
|
||||
be16 tlv_type;
|
||||
be16 length;
|
||||
be16 pac_type;
|
||||
be16 pac_len;
|
||||
be16 result;
|
||||
} STRUCT_PACKED;
|
||||
|
||||
/* RFC 4851, Section 4.2.9 - Request-Action TLV */
|
||||
struct eap_tlv_request_action_tlv {
|
||||
be16 tlv_type;
|
||||
be16 length;
|
||||
be16 action;
|
||||
} STRUCT_PACKED;
|
||||
|
||||
/* RFC 5422, Section 4.2.6 - PAC-Type TLV */
|
||||
struct eap_tlv_pac_type_tlv {
|
||||
be16 tlv_type; /* PAC_TYPE_PAC_TYPE */
|
||||
be16 length;
|
||||
be16 pac_type;
|
||||
} STRUCT_PACKED;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma pack(pop)
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST 0
|
||||
#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE 1
|
||||
|
||||
#define EAP_TLV_ACTION_PROCESS_TLV 1
|
||||
#define EAP_TLV_ACTION_NEGOTIATE_EAP 2
|
||||
|
||||
#endif /* EAP_TLV_COMMON_H */
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* EAP server/peer: EAP-TTLS (RFC 5281)
|
||||
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef EAP_TTLS_H
|
||||
#define EAP_TTLS_H
|
||||
|
||||
struct ttls_avp {
|
||||
be32 avp_code;
|
||||
be32 avp_length; /* 8-bit flags, 24-bit length;
|
||||
* length includes AVP header */
|
||||
/* optional 32-bit Vendor-ID */
|
||||
/* Data */
|
||||
};
|
||||
|
||||
struct ttls_avp_vendor {
|
||||
be32 avp_code;
|
||||
be32 avp_length; /* 8-bit flags, 24-bit length;
|
||||
* length includes AVP header */
|
||||
be32 vendor_id;
|
||||
/* Data */
|
||||
};
|
||||
|
||||
#define AVP_FLAGS_VENDOR 0x80
|
||||
#define AVP_FLAGS_MANDATORY 0x40
|
||||
|
||||
#define AVP_PAD(start, pos) \
|
||||
do { \
|
||||
int __pad; \
|
||||
__pad = (4 - (((pos) - (start)) & 3)) & 3; \
|
||||
os_memset((pos), 0, __pad); \
|
||||
pos += __pad; \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* RFC 2865 */
|
||||
#define RADIUS_ATTR_USER_NAME 1
|
||||
#define RADIUS_ATTR_USER_PASSWORD 2
|
||||
#define RADIUS_ATTR_CHAP_PASSWORD 3
|
||||
#define RADIUS_ATTR_REPLY_MESSAGE 18
|
||||
#define RADIUS_ATTR_CHAP_CHALLENGE 60
|
||||
#define RADIUS_ATTR_EAP_MESSAGE 79
|
||||
|
||||
/* RFC 2548 */
|
||||
#define RADIUS_VENDOR_ID_MICROSOFT 311
|
||||
#define RADIUS_ATTR_MS_CHAP_RESPONSE 1
|
||||
#define RADIUS_ATTR_MS_CHAP_ERROR 2
|
||||
#define RADIUS_ATTR_MS_CHAP_NT_ENC_PW 6
|
||||
#define RADIUS_ATTR_MS_CHAP_CHALLENGE 11
|
||||
#define RADIUS_ATTR_MS_CHAP2_RESPONSE 25
|
||||
#define RADIUS_ATTR_MS_CHAP2_SUCCESS 26
|
||||
#define RADIUS_ATTR_MS_CHAP2_CPW 27
|
||||
|
||||
#define EAP_TTLS_MSCHAPV2_CHALLENGE_LEN 16
|
||||
#define EAP_TTLS_MSCHAPV2_RESPONSE_LEN 50
|
||||
#define EAP_TTLS_MSCHAP_CHALLENGE_LEN 8
|
||||
#define EAP_TTLS_MSCHAP_RESPONSE_LEN 50
|
||||
#define EAP_TTLS_CHAP_CHALLENGE_LEN 16
|
||||
#define EAP_TTLS_CHAP_PASSWORD_LEN 16
|
||||
|
||||
#endif /* EAP_TTLS_H */
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* MSCHAPV2
|
||||
*/
|
||||
|
||||
|
||||
#ifndef MSCHAPV2_H
|
||||
#define MSCHAPV2_H
|
||||
|
||||
#define MSCHAPV2_CHAL_LEN 16
|
||||
#define MSCHAPV2_NT_RESPONSE_LEN 24
|
||||
#define MSCHAPV2_AUTH_RESPONSE_LEN 20
|
||||
#define MSCHAPV2_MASTER_KEY_LEN 16
|
||||
|
||||
const u8 * mschapv2_remove_domain(const u8 *username, size_t *len);
|
||||
int mschapv2_derive_response(const u8 *username, size_t username_len,
|
||||
const u8 *password, size_t password_len,
|
||||
int pwhash,
|
||||
const u8 *auth_challenge,
|
||||
const u8 *peer_challenge,
|
||||
u8 *nt_response, u8 *auth_response,
|
||||
u8 *master_key);
|
||||
int mschapv2_verify_auth_response(const u8 *auth_response,
|
||||
const u8 *buf, size_t buf_len);
|
||||
#endif /* MSCHAPV2_H */
|
|
@ -13,7 +13,6 @@
|
|||
* If CONFIG_INTERNAL_LIBTOMMATH is defined, bignum.c includes this
|
||||
* libtommath.c file instead of using the external LibTomMath library.
|
||||
*/
|
||||
#include "c_types.h"
|
||||
#include "os.h"
|
||||
#include "stdarg.h"
|
||||
|
||||
|
@ -193,7 +192,7 @@ static int mp_mul_d (mp_int * a, mp_digit b, mp_int * c);
|
|||
|
||||
|
||||
/* reverse an array, used for radix code */
|
||||
static void ICACHE_FLASH_ATTR
|
||||
static void
|
||||
bn_reverse (unsigned char *s, int len)
|
||||
{
|
||||
int ix, iy;
|
||||
|
@ -212,7 +211,7 @@ bn_reverse (unsigned char *s, int len)
|
|||
|
||||
|
||||
/* low level addition, based on HAC pp.594, Algorithm 14.7 */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
s_mp_add (mp_int * a, mp_int * b, mp_int * c)
|
||||
{
|
||||
mp_int *x;
|
||||
|
@ -301,7 +300,7 @@ s_mp_add (mp_int * a, mp_int * b, mp_int * c)
|
|||
|
||||
|
||||
/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
s_mp_sub (mp_int * a, mp_int * b, mp_int * c)
|
||||
{
|
||||
int olduse, res, min, max;
|
||||
|
@ -369,7 +368,7 @@ s_mp_sub (mp_int * a, mp_int * b, mp_int * c)
|
|||
|
||||
|
||||
/* init a new mp_int */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_init (mp_int * a)
|
||||
{
|
||||
int i;
|
||||
|
@ -396,7 +395,7 @@ mp_init (mp_int * a)
|
|||
|
||||
|
||||
/* clear one (frees) */
|
||||
static void ICACHE_FLASH_ATTR
|
||||
static void
|
||||
mp_clear (mp_int * a)
|
||||
{
|
||||
int i;
|
||||
|
@ -420,7 +419,7 @@ mp_clear (mp_int * a)
|
|||
|
||||
|
||||
/* high level addition (handles signs) */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_add (mp_int * a, mp_int * b, mp_int * c)
|
||||
{
|
||||
int sa, sb, res;
|
||||
|
@ -453,7 +452,7 @@ mp_add (mp_int * a, mp_int * b, mp_int * c)
|
|||
|
||||
|
||||
/* high level subtraction (handles signs) */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_sub (mp_int * a, mp_int * b, mp_int * c)
|
||||
{
|
||||
int sa, sb, res;
|
||||
|
@ -491,7 +490,7 @@ mp_sub (mp_int * a, mp_int * b, mp_int * c)
|
|||
|
||||
|
||||
/* high level multiplication (handles sign) */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_mul (mp_int * a, mp_int * b, mp_int * c)
|
||||
{
|
||||
int res, neg;
|
||||
|
@ -539,7 +538,7 @@ mp_mul (mp_int * a, mp_int * b, mp_int * c)
|
|||
|
||||
|
||||
/* d = a * b (mod c) */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
|
||||
{
|
||||
int res;
|
||||
|
@ -560,7 +559,7 @@ mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
|
|||
|
||||
|
||||
/* c = a mod b, 0 <= c < b */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_mod (mp_int * a, mp_int * b, mp_int * c)
|
||||
{
|
||||
mp_int t;
|
||||
|
@ -592,10 +591,12 @@ mp_mod (mp_int * a, mp_int * b, mp_int * c)
|
|||
* embedded in the normal function but that wasted a lot of stack space
|
||||
* for nothing (since 99% of the time the Montgomery code would be called)
|
||||
*/
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y)
|
||||
{
|
||||
int dr;
|
||||
#if defined(BN_MP_DR_IS_MODULUS_C)||defined(BN_MP_REDUCE_IS_2K_C)||defined(BN_MP_EXPTMOD_FAST_C)
|
||||
int dr = 0;
|
||||
#endif
|
||||
|
||||
/* modulus P must be positive */
|
||||
if (P->sign == MP_NEG) {
|
||||
|
@ -652,9 +653,6 @@ mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y)
|
|||
#ifdef BN_MP_DR_IS_MODULUS_C
|
||||
/* is it a DR modulus? */
|
||||
dr = mp_dr_is_modulus(P);
|
||||
#else
|
||||
/* default to no */
|
||||
dr = 0;
|
||||
#endif
|
||||
|
||||
#ifdef BN_MP_REDUCE_IS_2K_C
|
||||
|
@ -685,7 +683,7 @@ mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y)
|
|||
|
||||
|
||||
/* compare two ints (signed)*/
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_cmp (mp_int * a, mp_int * b)
|
||||
{
|
||||
/* compare based on sign */
|
||||
|
@ -708,7 +706,7 @@ mp_cmp (mp_int * a, mp_int * b)
|
|||
|
||||
|
||||
/* compare a digit */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_cmp_d(mp_int * a, mp_digit b)
|
||||
{
|
||||
/* compare based on sign */
|
||||
|
@ -734,7 +732,7 @@ mp_cmp_d(mp_int * a, mp_digit b)
|
|||
|
||||
#ifndef LTM_NO_NEG_EXP
|
||||
/* hac 14.61, pp608 */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_invmod (mp_int * a, mp_int * b, mp_int * c)
|
||||
{
|
||||
/* b cannot be negative */
|
||||
|
@ -764,7 +762,7 @@ mp_invmod (mp_int * a, mp_int * b, mp_int * c)
|
|||
|
||||
|
||||
/* get the size for an unsigned equivalent */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_unsigned_bin_size (mp_int * a)
|
||||
{
|
||||
int size = mp_count_bits (a);
|
||||
|
@ -774,7 +772,7 @@ mp_unsigned_bin_size (mp_int * a)
|
|||
|
||||
#ifndef LTM_NO_NEG_EXP
|
||||
/* hac 14.61, pp608 */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c)
|
||||
{
|
||||
mp_int x, y, u, v, A, B, C, D;
|
||||
|
@ -931,7 +929,7 @@ LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL);
|
|||
|
||||
|
||||
/* compare maginitude of two ints (unsigned) */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_cmp_mag (mp_int * a, mp_int * b)
|
||||
{
|
||||
int n;
|
||||
|
@ -967,7 +965,7 @@ mp_cmp_mag (mp_int * a, mp_int * b)
|
|||
|
||||
|
||||
/* reads a unsigned char array, assumes the msb is stored first [big endian] */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c)
|
||||
{
|
||||
int res;
|
||||
|
@ -1003,7 +1001,7 @@ mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c)
|
|||
|
||||
|
||||
/* store in unsigned [big endian] format */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_to_unsigned_bin (mp_int * a, unsigned char *b)
|
||||
{
|
||||
int x, res;
|
||||
|
@ -1032,7 +1030,7 @@ mp_to_unsigned_bin (mp_int * a, unsigned char *b)
|
|||
|
||||
|
||||
/* shift right by a certain bit count (store quotient in c, optional remainder in d) */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d)
|
||||
{
|
||||
mp_digit D, r, rr;
|
||||
|
@ -1109,7 +1107,7 @@ mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d)
|
|||
}
|
||||
|
||||
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_init_copy (mp_int * a, mp_int * b)
|
||||
{
|
||||
int res;
|
||||
|
@ -1122,7 +1120,7 @@ mp_init_copy (mp_int * a, mp_int * b)
|
|||
|
||||
|
||||
/* set to zero */
|
||||
static void ICACHE_FLASH_ATTR
|
||||
static void
|
||||
mp_zero (mp_int * a)
|
||||
{
|
||||
int n;
|
||||
|
@ -1139,7 +1137,7 @@ mp_zero (mp_int * a)
|
|||
|
||||
|
||||
/* copy, b = a */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_copy (mp_int * a, mp_int * b)
|
||||
{
|
||||
int res, n;
|
||||
|
@ -1187,7 +1185,7 @@ mp_copy (mp_int * a, mp_int * b)
|
|||
|
||||
|
||||
/* shift right a certain amount of digits */
|
||||
static void ICACHE_FLASH_ATTR
|
||||
static void
|
||||
mp_rshd (mp_int * a, int b)
|
||||
{
|
||||
int x;
|
||||
|
@ -1242,7 +1240,7 @@ mp_rshd (mp_int * a, int b)
|
|||
/* swap the elements of two integers, for cases where you can't simply swap the
|
||||
* mp_int pointers around
|
||||
*/
|
||||
static void ICACHE_FLASH_ATTR
|
||||
static void
|
||||
mp_exch (mp_int * a, mp_int * b)
|
||||
{
|
||||
mp_int t;
|
||||
|
@ -1260,7 +1258,7 @@ mp_exch (mp_int * a, mp_int * b)
|
|||
* Typically very fast. Also fixes the sign if there
|
||||
* are no more leading digits
|
||||
*/
|
||||
static void ICACHE_FLASH_ATTR
|
||||
static void
|
||||
mp_clamp (mp_int * a)
|
||||
{
|
||||
/* decrease used while the most significant digit is
|
||||
|
@ -1278,7 +1276,7 @@ mp_clamp (mp_int * a)
|
|||
|
||||
|
||||
/* grow as required */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_grow (mp_int * a, int size)
|
||||
{
|
||||
int i;
|
||||
|
@ -1320,7 +1318,7 @@ mp_grow (mp_int * a, int size)
|
|||
*
|
||||
* Simple function copies the input and fixes the sign to positive
|
||||
*/
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_abs (mp_int * a, mp_int * b)
|
||||
{
|
||||
int res;
|
||||
|
@ -1341,7 +1339,7 @@ mp_abs (mp_int * a, mp_int * b)
|
|||
|
||||
|
||||
/* set to a digit */
|
||||
static void ICACHE_FLASH_ATTR
|
||||
static void
|
||||
mp_set (mp_int * a, mp_digit b)
|
||||
{
|
||||
mp_zero (a);
|
||||
|
@ -1352,7 +1350,7 @@ mp_set (mp_int * a, mp_digit b)
|
|||
|
||||
#ifndef LTM_NO_NEG_EXP
|
||||
/* b = a/2 */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_div_2(mp_int * a, mp_int * b)
|
||||
{
|
||||
int x, res, oldused;
|
||||
|
@ -1402,7 +1400,7 @@ mp_div_2(mp_int * a, mp_int * b)
|
|||
|
||||
|
||||
/* shift left by a certain bit count */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_mul_2d (mp_int * a, int b, mp_int * c)
|
||||
{
|
||||
mp_digit d;
|
||||
|
@ -1468,7 +1466,7 @@ mp_mul_2d (mp_int * a, int b, mp_int * c)
|
|||
|
||||
|
||||
#ifdef BN_MP_INIT_MULTI_C
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_init_multi(mp_int *mp, ...)
|
||||
{
|
||||
mp_err res = MP_OKAY; /* Assume ok until proven otherwise */
|
||||
|
@ -1508,7 +1506,7 @@ mp_init_multi(mp_int *mp, ...)
|
|||
|
||||
|
||||
#ifdef BN_MP_CLEAR_MULTI_C
|
||||
static void ICACHE_FLASH_ATTR
|
||||
static void
|
||||
mp_clear_multi(mp_int *mp, ...)
|
||||
{
|
||||
mp_int* next_mp = mp;
|
||||
|
@ -1524,7 +1522,7 @@ mp_clear_multi(mp_int *mp, ...)
|
|||
|
||||
|
||||
/* shift left a certain amount of digits */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_lshd (mp_int * a, int b)
|
||||
{
|
||||
int x, res;
|
||||
|
@ -1572,7 +1570,7 @@ mp_lshd (mp_int * a, int b)
|
|||
|
||||
|
||||
/* returns the number of bits in an int */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_count_bits (mp_int * a)
|
||||
{
|
||||
int r;
|
||||
|
@ -1597,7 +1595,7 @@ mp_count_bits (mp_int * a)
|
|||
|
||||
|
||||
/* calc a value mod 2**b */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_mod_2d (mp_int * a, int b, mp_int * c)
|
||||
{
|
||||
int x, res;
|
||||
|
@ -1634,7 +1632,7 @@ mp_mod_2d (mp_int * a, int b, mp_int * c)
|
|||
#ifdef BN_MP_DIV_SMALL
|
||||
|
||||
/* slower bit-bang division... also smaller */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d)
|
||||
{
|
||||
mp_int ta, tb, tq, q;
|
||||
|
@ -1717,7 +1715,7 @@ LBL_ERR:
|
|||
* The overall algorithm is as described as
|
||||
* 14.20 from HAC but fixed to treat these cases.
|
||||
*/
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
|
||||
{
|
||||
mp_int q, x, y, t1, t2;
|
||||
|
@ -1910,7 +1908,7 @@ LBL_Q:mp_clear (&q);
|
|||
#define TAB_SIZE 256
|
||||
#endif
|
||||
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode)
|
||||
{
|
||||
mp_int M[TAB_SIZE], res, mu;
|
||||
|
@ -2139,7 +2137,7 @@ LBL_M:
|
|||
|
||||
|
||||
/* computes b = a*a */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_sqr (mp_int * a, mp_int * b)
|
||||
{
|
||||
int res;
|
||||
|
@ -2181,7 +2179,7 @@ if (a->used >= KARATSUBA_SQR_CUTOFF) {
|
|||
This differs from reduce_2k since "d" can be larger
|
||||
than a single digit.
|
||||
*/
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d)
|
||||
{
|
||||
mp_int q;
|
||||
|
@ -2220,7 +2218,7 @@ ERR:
|
|||
|
||||
|
||||
/* determines the setup value */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_reduce_2k_setup_l(mp_int *a, mp_int *d)
|
||||
{
|
||||
int res;
|
||||
|
@ -2249,7 +2247,7 @@ ERR:
|
|||
* Simple algorithm which zeroes the int, grows it then just sets one bit
|
||||
* as required.
|
||||
*/
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_2expt (mp_int * a, int b)
|
||||
{
|
||||
int res;
|
||||
|
@ -2275,7 +2273,7 @@ mp_2expt (mp_int * a, int b)
|
|||
/* pre-calculate the value required for Barrett reduction
|
||||
* For a given modulus "b" it calulates the value required in "a"
|
||||
*/
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_reduce_setup (mp_int * a, mp_int * b)
|
||||
{
|
||||
int res;
|
||||
|
@ -2291,7 +2289,7 @@ mp_reduce_setup (mp_int * a, mp_int * b)
|
|||
* precomputed via mp_reduce_setup.
|
||||
* From HAC pp.604 Algorithm 14.42
|
||||
*/
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_reduce (mp_int * x, mp_int * m, mp_int * mu)
|
||||
{
|
||||
mp_int q;
|
||||
|
@ -2375,7 +2373,7 @@ CLEANUP:
|
|||
* HAC pp. 595, Algorithm 14.12 Modified so you can control how
|
||||
* many digits of output are created.
|
||||
*/
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
|
||||
{
|
||||
mp_int t;
|
||||
|
@ -2458,7 +2456,7 @@ s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
|
|||
* Based on Algorithm 14.12 on pp.595 of HAC.
|
||||
*
|
||||
*/
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
|
||||
{
|
||||
int olduse, res, pa, ix, iz;
|
||||
|
@ -2531,7 +2529,7 @@ fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
|
|||
|
||||
|
||||
/* init an mp_init for a given size */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_init_size (mp_int * a, int size)
|
||||
{
|
||||
int x;
|
||||
|
@ -2560,7 +2558,7 @@ mp_init_size (mp_int * a, int size)
|
|||
|
||||
|
||||
/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
s_mp_sqr (mp_int * a, mp_int * b)
|
||||
{
|
||||
mp_int t;
|
||||
|
@ -2627,7 +2625,7 @@ s_mp_sqr (mp_int * a, mp_int * b)
|
|||
/* multiplies |a| * |b| and does not compute the lower digs digits
|
||||
* [meant to get the higher part of the product]
|
||||
*/
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
|
||||
{
|
||||
mp_int t;
|
||||
|
@ -2687,7 +2685,7 @@ s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
|
|||
|
||||
#ifdef BN_MP_MONTGOMERY_SETUP_C
|
||||
/* setups the montgomery reduction stuff */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_montgomery_setup (mp_int * n, mp_digit * rho)
|
||||
{
|
||||
mp_digit x, b;
|
||||
|
@ -2735,7 +2733,7 @@ mp_montgomery_setup (mp_int * n, mp_digit * rho)
|
|||
*
|
||||
* Based on Algorithm 14.32 on pp.601 of HAC.
|
||||
*/
|
||||
int ICACHE_FLASH_ATTR
|
||||
int
|
||||
fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho)
|
||||
{
|
||||
int ix, res, olduse;
|
||||
|
@ -2883,7 +2881,7 @@ fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho)
|
|||
|
||||
#ifdef BN_MP_MUL_2_C
|
||||
/* b = a*2 */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_mul_2(mp_int * a, mp_int * b)
|
||||
{
|
||||
int x, res, oldused;
|
||||
|
@ -2953,7 +2951,7 @@ mp_mul_2(mp_int * a, mp_int * b)
|
|||
* The method is slightly modified to shift B unconditionally up to just under
|
||||
* the leading bit of b. This saves a lot of multiple precision shifting.
|
||||
*/
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_montgomery_calc_normalization (mp_int * a, mp_int * b)
|
||||
{
|
||||
int x, bits, res;
|
||||
|
@ -2997,7 +2995,7 @@ mp_montgomery_calc_normalization (mp_int * a, mp_int * b)
|
|||
* Uses Montgomery or Diminished Radix reduction [whichever appropriate]
|
||||
*/
|
||||
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode)
|
||||
{
|
||||
mp_int M[TAB_SIZE], res;
|
||||
|
@ -3296,7 +3294,7 @@ LBL_M:
|
|||
After that loop you do the squares and add them in.
|
||||
*/
|
||||
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
fast_s_mp_sqr (mp_int * a, mp_int * b)
|
||||
{
|
||||
int olduse, res, pa, ix, iz;
|
||||
|
@ -3384,7 +3382,7 @@ fast_s_mp_sqr (mp_int * a, mp_int * b)
|
|||
|
||||
#ifdef BN_MP_MUL_D_C
|
||||
/* multiply by a digit */
|
||||
static int ICACHE_FLASH_ATTR
|
||||
static int
|
||||
mp_mul_d (mp_int * a, mp_digit b, mp_int * c)
|
||||
{
|
||||
mp_digit u, *tmpa, *tmpc;
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
#ifndef BASE64_H
|
||||
#define BASE64_H
|
||||
|
||||
unsigned char * _base64_encode(const unsigned char *src, size_t len,
|
||||
unsigned char * base64_encode(const unsigned char *src, size_t len,
|
||||
size_t *out_len);
|
||||
unsigned char * _base64_decode(const unsigned char *src, size_t len,
|
||||
unsigned char * base64_decode(const unsigned char *src, size_t len,
|
||||
size_t *out_len);
|
||||
|
||||
#endif /* BASE64_H */
|
||||
|
|
|
@ -23,10 +23,10 @@ void ext_password_free(struct wpabuf *pw);
|
|||
|
||||
#else /* CONFIG_EXT_PASSWORD */
|
||||
|
||||
#define ext_password_init(b, p) ((void *) 1)
|
||||
#define ext_password_deinit(d) do { } while (0)
|
||||
#define ext_password_get(d, n) (NULL)
|
||||
#define ext_password_free(p) do { } while (0)
|
||||
#define ext_password_init(b, p)
|
||||
#define ext_password_deinit(d)
|
||||
#define ext_password_get(d, n)
|
||||
#define ext_password_free(p)
|
||||
|
||||
#endif /* CONFIG_EXT_PASSWORD */
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Universally Unique IDentifier (UUID)
|
||||
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef UUID_H
|
||||
#define UUID_H
|
||||
|
||||
#define UUID_LEN 16
|
||||
|
||||
int uuid_str2bin(const char *str, u8 *bin);
|
||||
int uuid_bin2str(const u8 *bin, char *str, size_t max_len);
|
||||
int is_nil_uuid(const u8 *uuid);
|
||||
|
||||
#endif /* UUID_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Wi-Fi Protected Setup - attribute parsing
|
||||
* Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef WPS_ATTR_PARSE_H
|
||||
#define WPS_ATTR_PARSE_H
|
||||
|
||||
#include "wps/wps.h"
|
||||
|
||||
struct wps_parse_attr {
|
||||
/* fixed length fields */
|
||||
const u8 *version; /* 1 octet */
|
||||
const u8 *version2; /* 1 octet */
|
||||
const u8 *msg_type; /* 1 octet */
|
||||
const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */
|
||||
const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */
|
||||
const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */
|
||||
const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */
|
||||
const u8 *auth_type_flags; /* 2 octets */
|
||||
const u8 *encr_type_flags; /* 2 octets */
|
||||
const u8 *conn_type_flags; /* 1 octet */
|
||||
const u8 *config_methods; /* 2 octets */
|
||||
const u8 *sel_reg_config_methods; /* 2 octets */
|
||||
const u8 *primary_dev_type; /* 8 octets */
|
||||
const u8 *rf_bands; /* 1 octet */
|
||||
const u8 *assoc_state; /* 2 octets */
|
||||
const u8 *config_error; /* 2 octets */
|
||||
const u8 *dev_password_id; /* 2 octets */
|
||||
const u8 *os_version; /* 4 octets */
|
||||
const u8 *wps_state; /* 1 octet */
|
||||
const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */
|
||||
const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */
|
||||
const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */
|
||||
const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */
|
||||
const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */
|
||||
const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
|
||||
const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
|
||||
const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
|
||||
const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
|
||||
const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */
|
||||
const u8 *auth_type; /* 2 octets */
|
||||
const u8 *encr_type; /* 2 octets */
|
||||
const u8 *network_idx; /* 1 octet */
|
||||
const u8 *network_key_idx; /* 1 octet */
|
||||
const u8 *mac_addr; /* ETH_ALEN (6) octets */
|
||||
const u8 *key_prov_auto; /* 1 octet (Bool) */
|
||||
const u8 *dot1x_enabled; /* 1 octet (Bool) */
|
||||
const u8 *selected_registrar; /* 1 octet (Bool) */
|
||||
const u8 *request_type; /* 1 octet */
|
||||
const u8 *response_type; /* 1 octet */
|
||||
const u8 *ap_setup_locked; /* 1 octet */
|
||||
const u8 *settings_delay_time; /* 1 octet */
|
||||
const u8 *network_key_shareable; /* 1 octet (Bool) */
|
||||
const u8 *request_to_enroll; /* 1 octet (Bool) */
|
||||
const u8 *ap_channel; /* 2 octets */
|
||||
|
||||
/* variable length fields */
|
||||
const u8 *manufacturer;
|
||||
size_t manufacturer_len;
|
||||
const u8 *model_name;
|
||||
size_t model_name_len;
|
||||
const u8 *model_number;
|
||||
size_t model_number_len;
|
||||
const u8 *serial_number;
|
||||
size_t serial_number_len;
|
||||
const u8 *dev_name;
|
||||
size_t dev_name_len;
|
||||
const u8 *public_key;
|
||||
size_t public_key_len;
|
||||
const u8 *encr_settings;
|
||||
size_t encr_settings_len;
|
||||
const u8 *ssid; /* <= 32 octets */
|
||||
size_t ssid_len;
|
||||
const u8 *network_key; /* <= 64 octets */
|
||||
size_t network_key_len;
|
||||
const u8 *eap_type; /* <= 8 octets */
|
||||
size_t eap_type_len;
|
||||
const u8 *eap_identity; /* <= 64 octets */
|
||||
size_t eap_identity_len;
|
||||
const u8 *authorized_macs; /* <= 30 octets */
|
||||
size_t authorized_macs_len;
|
||||
const u8 *sec_dev_type_list; /* <= 128 octets */
|
||||
size_t sec_dev_type_list_len;
|
||||
const u8 *oob_dev_password; /* 38..54 octets */
|
||||
size_t oob_dev_password_len;
|
||||
|
||||
/* attributes that can occur multiple times */
|
||||
#define MAX_CRED_COUNT 10
|
||||
const u8 *cred[MAX_CRED_COUNT];
|
||||
size_t cred_len[MAX_CRED_COUNT];
|
||||
size_t num_cred;
|
||||
|
||||
#define MAX_REQ_DEV_TYPE_COUNT 10
|
||||
const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
|
||||
size_t num_req_dev_type;
|
||||
|
||||
const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
|
||||
size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
|
||||
size_t num_vendor_ext;
|
||||
};
|
||||
|
||||
int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
|
||||
|
||||
#endif /* WPS_ATTR_PARSE_H */
|
|
@ -0,0 +1,342 @@
|
|||
/*
|
||||
* Wi-Fi Protected Setup - message definitions
|
||||
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef WPS_DEFS_H
|
||||
#define WPS_DEFS_H
|
||||
|
||||
#ifdef CONFIG_WPS_TESTING
|
||||
|
||||
extern int wps_version_number;
|
||||
extern int wps_testing_dummy_cred;
|
||||
#define WPS_VERSION wps_version_number
|
||||
|
||||
#else /* CONFIG_WPS_TESTING */
|
||||
|
||||
#ifdef CONFIG_WPS2
|
||||
#define WPS_VERSION 0x20
|
||||
#else /* CONFIG_WPS2 */
|
||||
#define WPS_VERSION 0x10
|
||||
#endif /* CONFIG_WPS2 */
|
||||
|
||||
#endif /* CONFIG_WPS_TESTING */
|
||||
|
||||
#define CONFIG_WPS_STRICT
|
||||
|
||||
/* Diffie-Hellman 1536-bit MODP Group; RFC 3526, Group 5 */
|
||||
#define WPS_DH_GROUP 5
|
||||
|
||||
#define WPS_UUID_LEN 16
|
||||
#define WPS_NONCE_LEN 16
|
||||
#define WPS_AUTHENTICATOR_LEN 8
|
||||
#define WPS_AUTHKEY_LEN 32
|
||||
#define WPS_KEYWRAPKEY_LEN 16
|
||||
#define WPS_EMSK_LEN 32
|
||||
#define WPS_PSK_LEN 16
|
||||
#define WPS_SECRET_NONCE_LEN 16
|
||||
#define WPS_HASH_LEN 32
|
||||
#define WPS_KWA_LEN 8
|
||||
#define WPS_MGMTAUTHKEY_LEN 32
|
||||
#define WPS_MGMTENCKEY_LEN 16
|
||||
#define WPS_MGMT_KEY_ID_LEN 16
|
||||
#define WPS_OOB_DEVICE_PASSWORD_MIN_LEN 16
|
||||
#define WPS_OOB_DEVICE_PASSWORD_LEN 32
|
||||
#define WPS_OOB_PUBKEY_HASH_LEN 20
|
||||
|
||||
/* Attribute Types */
|
||||
enum wps_attribute {
|
||||
ATTR_AP_CHANNEL = 0x1001,
|
||||
ATTR_ASSOC_STATE = 0x1002,
|
||||
ATTR_AUTH_TYPE = 0x1003,
|
||||
ATTR_AUTH_TYPE_FLAGS = 0x1004,
|
||||
ATTR_AUTHENTICATOR = 0x1005,
|
||||
ATTR_CONFIG_METHODS = 0x1008,
|
||||
ATTR_CONFIG_ERROR = 0x1009,
|
||||
ATTR_CONFIRM_URL4 = 0x100a,
|
||||
ATTR_CONFIRM_URL6 = 0x100b,
|
||||
ATTR_CONN_TYPE = 0x100c,
|
||||
ATTR_CONN_TYPE_FLAGS = 0x100d,
|
||||
ATTR_CRED = 0x100e,
|
||||
ATTR_ENCR_TYPE = 0x100f,
|
||||
ATTR_ENCR_TYPE_FLAGS = 0x1010,
|
||||
ATTR_DEV_NAME = 0x1011,
|
||||
ATTR_DEV_PASSWORD_ID = 0x1012,
|
||||
ATTR_E_HASH1 = 0x1014,
|
||||
ATTR_E_HASH2 = 0x1015,
|
||||
ATTR_E_SNONCE1 = 0x1016,
|
||||
ATTR_E_SNONCE2 = 0x1017,
|
||||
ATTR_ENCR_SETTINGS = 0x1018,
|
||||
ATTR_ENROLLEE_NONCE = 0x101a,
|
||||
ATTR_FEATURE_ID = 0x101b,
|
||||
ATTR_IDENTITY = 0x101c,
|
||||
ATTR_IDENTITY_PROOF = 0x101d,
|
||||
ATTR_KEY_WRAP_AUTH = 0x101e,
|
||||
ATTR_KEY_ID = 0x101f,
|
||||
ATTR_MAC_ADDR = 0x1020,
|
||||
ATTR_MANUFACTURER = 0x1021,
|
||||
ATTR_MSG_TYPE = 0x1022,
|
||||
ATTR_MODEL_NAME = 0x1023,
|
||||
ATTR_MODEL_NUMBER = 0x1024,
|
||||
ATTR_NETWORK_INDEX = 0x1026,
|
||||
ATTR_NETWORK_KEY = 0x1027,
|
||||
ATTR_NETWORK_KEY_INDEX = 0x1028,
|
||||
ATTR_NEW_DEVICE_NAME = 0x1029,
|
||||
ATTR_NEW_PASSWORD = 0x102a,
|
||||
ATTR_OOB_DEVICE_PASSWORD = 0x102c,
|
||||
ATTR_OS_VERSION = 0x102d,
|
||||
ATTR_POWER_LEVEL = 0x102f,
|
||||
ATTR_PSK_CURRENT = 0x1030,
|
||||
ATTR_PSK_MAX = 0x1031,
|
||||
ATTR_PUBLIC_KEY = 0x1032,
|
||||
ATTR_RADIO_ENABLE = 0x1033,
|
||||
ATTR_REBOOT = 0x1034,
|
||||
ATTR_REGISTRAR_CURRENT = 0x1035,
|
||||
ATTR_REGISTRAR_ESTABLISHED = 0x1036,
|
||||
ATTR_REGISTRAR_LIST = 0x1037,
|
||||
ATTR_REGISTRAR_MAX = 0x1038,
|
||||
ATTR_REGISTRAR_NONCE = 0x1039,
|
||||
ATTR_REQUEST_TYPE = 0x103a,
|
||||
ATTR_RESPONSE_TYPE = 0x103b,
|
||||
ATTR_RF_BANDS = 0x103c,
|
||||
ATTR_R_HASH1 = 0x103d,
|
||||
ATTR_R_HASH2 = 0x103e,
|
||||
ATTR_R_SNONCE1 = 0x103f,
|
||||
ATTR_R_SNONCE2 = 0x1040,
|
||||
ATTR_SELECTED_REGISTRAR = 0x1041,
|
||||
ATTR_SERIAL_NUMBER = 0x1042,
|
||||
ATTR_WPS_STATE = 0x1044,
|
||||
ATTR_SSID = 0x1045,
|
||||
ATTR_TOTAL_NETWORKS = 0x1046,
|
||||
ATTR_UUID_E = 0x1047,
|
||||
ATTR_UUID_R = 0x1048,
|
||||
ATTR_VENDOR_EXT = 0x1049,
|
||||
ATTR_VERSION = 0x104a,
|
||||
ATTR_X509_CERT_REQ = 0x104b,
|
||||
ATTR_X509_CERT = 0x104c,
|
||||
ATTR_EAP_IDENTITY = 0x104d,
|
||||
ATTR_MSG_COUNTER = 0x104e,
|
||||
ATTR_PUBKEY_HASH = 0x104f,
|
||||
ATTR_REKEY_KEY = 0x1050,
|
||||
ATTR_KEY_LIFETIME = 0x1051,
|
||||
ATTR_PERMITTED_CFG_METHODS = 0x1052,
|
||||
ATTR_SELECTED_REGISTRAR_CONFIG_METHODS = 0x1053,
|
||||
ATTR_PRIMARY_DEV_TYPE = 0x1054,
|
||||
ATTR_SECONDARY_DEV_TYPE_LIST = 0x1055,
|
||||
ATTR_PORTABLE_DEV = 0x1056,
|
||||
ATTR_AP_SETUP_LOCKED = 0x1057,
|
||||
ATTR_APPLICATION_EXT = 0x1058,
|
||||
ATTR_EAP_TYPE = 0x1059,
|
||||
ATTR_IV = 0x1060,
|
||||
ATTR_KEY_PROVIDED_AUTO = 0x1061,
|
||||
ATTR_802_1X_ENABLED = 0x1062,
|
||||
ATTR_APPSESSIONKEY = 0x1063,
|
||||
ATTR_WEPTRANSMITKEY = 0x1064,
|
||||
ATTR_REQUESTED_DEV_TYPE = 0x106a,
|
||||
ATTR_EXTENSIBILITY_TEST = 0x10fa /* _NOT_ defined in the spec */
|
||||
};
|
||||
|
||||
#define WPS_VENDOR_ID_WFA 14122
|
||||
|
||||
/* WFA Vendor Extension subelements */
|
||||
enum {
|
||||
WFA_ELEM_VERSION2 = 0x00,
|
||||
WFA_ELEM_AUTHORIZEDMACS = 0x01,
|
||||
WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02,
|
||||
WFA_ELEM_REQUEST_TO_ENROLL = 0x03,
|
||||
WFA_ELEM_SETTINGS_DELAY_TIME = 0x04
|
||||
};
|
||||
|
||||
/* Device Password ID */
|
||||
enum wps_dev_password_id {
|
||||
DEV_PW_DEFAULT = 0x0000,
|
||||
DEV_PW_USER_SPECIFIED = 0x0001,
|
||||
DEV_PW_MACHINE_SPECIFIED = 0x0002,
|
||||
DEV_PW_REKEY = 0x0003,
|
||||
DEV_PW_PUSHBUTTON = 0x0004,
|
||||
DEV_PW_REGISTRAR_SPECIFIED = 0x0005
|
||||
};
|
||||
|
||||
/* WPS message flag */
|
||||
enum wps_msg_flag {
|
||||
WPS_MSG_FLAG_MORE = 0x01,
|
||||
WPS_MSG_FLAG_LEN = 0x02
|
||||
};
|
||||
|
||||
/* Message Type */
|
||||
enum wps_msg_type {
|
||||
WPS_Beacon = 0x01,
|
||||
WPS_ProbeRequest = 0x02,
|
||||
WPS_ProbeResponse = 0x03,
|
||||
WPS_M1 = 0x04,
|
||||
WPS_M2 = 0x05,
|
||||
WPS_M2D = 0x06,
|
||||
WPS_M3 = 0x07,
|
||||
WPS_M4 = 0x08,
|
||||
WPS_M5 = 0x09,
|
||||
WPS_M6 = 0x0a,
|
||||
WPS_M7 = 0x0b,
|
||||
WPS_M8 = 0x0c,
|
||||
WPS_WSC_ACK = 0x0d,
|
||||
WPS_WSC_NACK = 0x0e,
|
||||
WPS_WSC_DONE = 0x0f
|
||||
};
|
||||
|
||||
/* Authentication Type Flags */
|
||||
#define WPS_WIFI_AUTH_OPEN 0x0001
|
||||
#define WPS_AUTH_WPAPSK 0x0002
|
||||
#define WPS_AUTH_SHARED 0x0004
|
||||
#define WPS_AUTH_WPA 0x0008
|
||||
#define WPS_AUTH_WPA2 0x0010
|
||||
#define WPS_AUTH_WPA2PSK 0x0020
|
||||
#define WPS_AUTH_TYPES (WPS_WIFI_AUTH_OPEN | WPS_AUTH_WPAPSK | WPS_AUTH_SHARED | \
|
||||
WPS_AUTH_WPA | WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)
|
||||
|
||||
/* Encryption Type Flags */
|
||||
#define WPS_ENCR_NONE 0x0001
|
||||
#define WPS_ENCR_WEP 0x0002
|
||||
#define WPS_ENCR_TKIP 0x0004
|
||||
#define WPS_ENCR_AES 0x0008
|
||||
#define WPS_ENCR_TYPES (WPS_ENCR_NONE | WPS_ENCR_WEP | WPS_ENCR_TKIP | \
|
||||
WPS_ENCR_AES)
|
||||
|
||||
/* Configuration Error */
|
||||
enum wps_config_error {
|
||||
WPS_CFG_NO_ERROR = 0,
|
||||
WPS_CFG_OOB_IFACE_READ_ERROR = 1,
|
||||
WPS_CFG_DECRYPTION_CRC_FAILURE = 2,
|
||||
WPS_CFG_24_CHAN_NOT_SUPPORTED = 3,
|
||||
WPS_CFG_50_CHAN_NOT_SUPPORTED = 4,
|
||||
WPS_CFG_SIGNAL_TOO_WEAK = 5,
|
||||
WPS_CFG_NETWORK_AUTH_FAILURE = 6,
|
||||
WPS_CFG_NETWORK_ASSOC_FAILURE = 7,
|
||||
WPS_CFG_NO_DHCP_RESPONSE = 8,
|
||||
WPS_CFG_FAILED_DHCP_CONFIG = 9,
|
||||
WPS_CFG_IP_ADDR_CONFLICT = 10,
|
||||
WPS_CFG_NO_CONN_TO_REGISTRAR = 11,
|
||||
WPS_CFG_MULTIPLE_PBC_DETECTED = 12,
|
||||
WPS_CFG_ROGUE_SUSPECTED = 13,
|
||||
WPS_CFG_DEVICE_BUSY = 14,
|
||||
WPS_CFG_SETUP_LOCKED = 15,
|
||||
WPS_CFG_MSG_TIMEOUT = 16,
|
||||
WPS_CFG_REG_SESS_TIMEOUT = 17,
|
||||
WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18
|
||||
};
|
||||
|
||||
/* Vendor specific Error Indication for WPS event messages */
|
||||
enum wps_error_indication {
|
||||
WPS_EI_NO_ERROR,
|
||||
WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED,
|
||||
WPS_EI_SECURITY_WEP_PROHIBITED,
|
||||
NUM_WPS_EI_VALUES
|
||||
};
|
||||
|
||||
/* RF Bands */
|
||||
#define WPS_RF_24GHZ 0x01
|
||||
#define WPS_RF_50GHZ 0x02
|
||||
|
||||
/* Config Methods */
|
||||
#define WPS_CONFIG_USBA 0x0001
|
||||
#define WPS_CONFIG_ETHERNET 0x0002
|
||||
#define WPS_CONFIG_LABEL 0x0004
|
||||
#define WPS_CONFIG_DISPLAY 0x0008
|
||||
#define WPS_CONFIG_EXT_NFC_TOKEN 0x0010
|
||||
#define WPS_CONFIG_INT_NFC_TOKEN 0x0020
|
||||
#define WPS_CONFIG_NFC_INTERFACE 0x0040
|
||||
#define WPS_CONFIG_PUSHBUTTON 0x0080
|
||||
#define WPS_CONFIG_KEYPAD 0x0100
|
||||
#ifdef CONFIG_WPS2
|
||||
#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280
|
||||
#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480
|
||||
#define WPS_CONFIG_VIRT_DISPLAY 0x2008
|
||||
#define WPS_CONFIG_PHY_DISPLAY 0x4008
|
||||
#endif /* CONFIG_WPS2 */
|
||||
|
||||
/* Connection Type Flags */
|
||||
#define WPS_CONN_ESS 0x01
|
||||
#define WPS_CONN_IBSS 0x02
|
||||
|
||||
/* Wi-Fi Protected Setup State */
|
||||
enum wps_state {
|
||||
WPS_STATE_NOT_CONFIGURED = 1,
|
||||
WPS_STATE_CONFIGURED = 2
|
||||
};
|
||||
|
||||
/* Association State */
|
||||
enum wps_assoc_state {
|
||||
WPS_ASSOC_NOT_ASSOC = 0,
|
||||
WPS_ASSOC_CONN_SUCCESS = 1,
|
||||
WPS_ASSOC_CFG_FAILURE = 2,
|
||||
WPS_ASSOC_FAILURE = 3,
|
||||
WPS_ASSOC_IP_FAILURE = 4
|
||||
};
|
||||
|
||||
|
||||
#define WPS_DEV_OUI_WFA 0x0050f204
|
||||
|
||||
enum wps_dev_categ {
|
||||
WPS_DEV_COMPUTER = 1,
|
||||
WPS_DEV_INPUT = 2,
|
||||
WPS_DEV_PRINTER = 3,
|
||||
WPS_DEV_CAMERA = 4,
|
||||
WPS_DEV_STORAGE = 5,
|
||||
WPS_DEV_NETWORK_INFRA = 6,
|
||||
WPS_DEV_DISPLAY = 7,
|
||||
WPS_DEV_MULTIMEDIA = 8,
|
||||
WPS_DEV_GAMING = 9,
|
||||
WPS_DEV_PHONE = 10
|
||||
};
|
||||
|
||||
enum wps_dev_subcateg {
|
||||
WPS_DEV_COMPUTER_PC = 1,
|
||||
WPS_DEV_COMPUTER_SERVER = 2,
|
||||
WPS_DEV_COMPUTER_MEDIA_CENTER = 3,
|
||||
WPS_DEV_PRINTER_PRINTER = 1,
|
||||
WPS_DEV_PRINTER_SCANNER = 2,
|
||||
WPS_DEV_CAMERA_DIGITAL_STILL_CAMERA = 1,
|
||||
WPS_DEV_STORAGE_NAS = 1,
|
||||
WPS_DEV_NETWORK_INFRA_AP = 1,
|
||||
WPS_DEV_NETWORK_INFRA_ROUTER = 2,
|
||||
WPS_DEV_NETWORK_INFRA_SWITCH = 3,
|
||||
WPS_DEV_DISPLAY_TV = 1,
|
||||
WPS_DEV_DISPLAY_PICTURE_FRAME = 2,
|
||||
WPS_DEV_DISPLAY_PROJECTOR = 3,
|
||||
WPS_DEV_MULTIMEDIA_DAR = 1,
|
||||
WPS_DEV_MULTIMEDIA_PVR = 2,
|
||||
WPS_DEV_MULTIMEDIA_MCX = 3,
|
||||
WPS_DEV_GAMING_XBOX = 1,
|
||||
WPS_DEV_GAMING_XBOX360 = 2,
|
||||
WPS_DEV_GAMING_PLAYSTATION = 3,
|
||||
WPS_DEV_PHONE_WINDOWS_MOBILE = 1
|
||||
};
|
||||
|
||||
|
||||
/* Request Type */
|
||||
enum wps_request_type {
|
||||
WPS_REQ_ENROLLEE_INFO = 0,
|
||||
WPS_REQ_ENROLLEE = 1,
|
||||
WPS_REQ_REGISTRAR = 2,
|
||||
WPS_REQ_WLAN_MANAGER_REGISTRAR = 3
|
||||
};
|
||||
|
||||
/* Response Type */
|
||||
enum wps_response_type {
|
||||
WPS_RESP_ENROLLEE_INFO = 0,
|
||||
WPS_RESP_ENROLLEE = 1,
|
||||
WPS_RESP_REGISTRAR = 2,
|
||||
WPS_RESP_AP = 3
|
||||
};
|
||||
|
||||
/* Walk Time for push button configuration (in seconds) */
|
||||
#define WPS_PBC_WALK_TIME 120
|
||||
|
||||
#define WPS_MAX_AUTHORIZED_MACS 5
|
||||
|
||||
#define WPS_IGNORE_SEL_REG_MAX_CNT 4
|
||||
|
||||
#define WPS_MAX_DIS_AP_NUM 10
|
||||
|
||||
#endif /* WPS_DEFS_H */
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Wi-Fi Protected Setup - device attributes
|
||||
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef WPS_DEV_ATTR_H
|
||||
#define WPS_DEV_ATTR_H
|
||||
|
||||
struct wps_parse_attr;
|
||||
|
||||
int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg);
|
||||
int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg);
|
||||
int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg);
|
||||
int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
|
||||
int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
|
||||
int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
|
||||
int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg);
|
||||
int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg);
|
||||
int wps_build_primary_dev_type(struct wps_device_data *dev,
|
||||
struct wpabuf *msg);
|
||||
int wps_build_secondary_dev_type(struct wps_device_data *dev,
|
||||
struct wpabuf *msg);
|
||||
int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
|
||||
int wps_process_device_attrs(struct wps_device_data *dev,
|
||||
struct wps_parse_attr *attr);
|
||||
int wps_process_os_version(struct wps_device_data *dev, const u8 *ver);
|
||||
int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands);
|
||||
void wps_device_data_dup(struct wps_device_data *dst,
|
||||
const struct wps_device_data *src);
|
||||
void wps_device_data_free(struct wps_device_data *dev);
|
||||
int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg);
|
||||
int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
|
||||
unsigned int num_req_dev_types,
|
||||
const u8 *req_dev_types);
|
||||
|
||||
#endif /* WPS_DEV_ATTR_H */
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* Wi-Fi Protected Setup - internal definitions
|
||||
* Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef WPS_I_H
|
||||
#define WPS_I_H
|
||||
|
||||
#include "wps.h"
|
||||
#include "wps_attr_parse.h"
|
||||
#include "esp_wifi_crypto_types.h"
|
||||
|
||||
#ifdef CONFIG_WPS_NFC
|
||||
struct wps_nfc_pw_token;
|
||||
#endif
|
||||
/**
|
||||
* struct wps_data - WPS registration protocol data
|
||||
*
|
||||
* This data is stored at the EAP-WSC server/peer method and it is kept for a
|
||||
* single registration protocol run.
|
||||
*/
|
||||
struct wps_data {
|
||||
/**
|
||||
* wps - Pointer to long term WPS context
|
||||
*/
|
||||
struct wps_context *wps;
|
||||
|
||||
/**
|
||||
* registrar - Whether this end is a Registrar
|
||||
*/
|
||||
int registrar;
|
||||
|
||||
/**
|
||||
* er - Whether the local end is an external registrar
|
||||
*/
|
||||
int er;
|
||||
|
||||
enum {
|
||||
/* Enrollee states */
|
||||
SEND_M1, RECV_M2, SEND_M3, RECV_M4, SEND_M5, RECV_M6, SEND_M7,
|
||||
RECV_M8, RECEIVED_M2D, WPS_MSG_DONE, RECV_ACK, WPS_FINISHED,
|
||||
SEND_WSC_NACK,
|
||||
|
||||
/* Registrar states */
|
||||
RECV_M1, SEND_M2, RECV_M3, SEND_M4, RECV_M5, SEND_M6,
|
||||
RECV_M7, SEND_M8, RECV_DONE, SEND_M2D, RECV_M2D_ACK
|
||||
} state;
|
||||
|
||||
u8 uuid_e[WPS_UUID_LEN];
|
||||
u8 uuid_r[WPS_UUID_LEN];
|
||||
u8 mac_addr_e[ETH_ALEN];
|
||||
u8 nonce_e[WPS_NONCE_LEN];
|
||||
u8 nonce_r[WPS_NONCE_LEN];
|
||||
u8 psk1[WPS_PSK_LEN];
|
||||
u8 psk2[WPS_PSK_LEN];
|
||||
u8 snonce[2 * WPS_SECRET_NONCE_LEN];
|
||||
u8 peer_hash1[WPS_HASH_LEN];
|
||||
u8 peer_hash2[WPS_HASH_LEN];
|
||||
|
||||
struct wpabuf *dh_privkey;
|
||||
struct wpabuf *dh_pubkey_e;
|
||||
struct wpabuf *dh_pubkey_r;
|
||||
u8 authkey[WPS_AUTHKEY_LEN];
|
||||
u8 keywrapkey[WPS_KEYWRAPKEY_LEN];
|
||||
u8 emsk[WPS_EMSK_LEN];
|
||||
|
||||
struct wpabuf *last_msg;
|
||||
|
||||
u8 *dev_password;
|
||||
size_t dev_password_len;
|
||||
u16 dev_pw_id;
|
||||
int pbc;
|
||||
|
||||
/**
|
||||
* request_type - Request Type attribute from (Re)AssocReq
|
||||
*/
|
||||
u8 request_type;
|
||||
|
||||
/**
|
||||
* encr_type - Available encryption types
|
||||
*/
|
||||
u16 encr_type;
|
||||
|
||||
/**
|
||||
* auth_type - Available authentication types
|
||||
*/
|
||||
u16 auth_type;
|
||||
|
||||
u8 *new_psk;
|
||||
size_t new_psk_len;
|
||||
|
||||
int wps_pin_revealed;
|
||||
struct wps_credential cred;
|
||||
|
||||
struct wps_device_data peer_dev;
|
||||
|
||||
/**
|
||||
* config_error - Configuration Error value to be used in NACK
|
||||
*/
|
||||
u16 config_error;
|
||||
u16 error_indication;
|
||||
|
||||
int ext_reg;
|
||||
int int_reg;
|
||||
|
||||
struct wps_credential *new_ap_settings;
|
||||
|
||||
void *dh_ctx;
|
||||
|
||||
void (*ap_settings_cb)(void *ctx, const struct wps_credential *cred);
|
||||
void *ap_settings_cb_ctx;
|
||||
|
||||
struct wps_credential *use_cred;
|
||||
|
||||
int use_psk_key;
|
||||
u8 p2p_dev_addr[ETH_ALEN]; /* P2P Device Address of the client or
|
||||
* 00:00:00:00:00:00 if not a P2p client */
|
||||
int pbc_in_m1;
|
||||
#ifdef CONFIG_WPS_NFC
|
||||
struct wps_nfc_pw_token *nfc_pw_token;
|
||||
#endif
|
||||
};
|
||||
|
||||
wps_crypto_funcs_t wps_crypto_funcs;
|
||||
|
||||
/* wps_common.c */
|
||||
void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
|
||||
const char *label, u8 *res, size_t res_len);
|
||||
int wps_derive_keys(struct wps_data *wps);
|
||||
void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
|
||||
size_t dev_passwd_len);
|
||||
struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
|
||||
size_t encr_len);
|
||||
void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
|
||||
u16 config_error, u16 error_indication);
|
||||
void wps_success_event(struct wps_context *wps);
|
||||
void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part);
|
||||
void wps_pbc_overlap_event(struct wps_context *wps);
|
||||
void wps_pbc_timeout_event(struct wps_context *wps);
|
||||
|
||||
struct wpabuf * wps_build_wsc_ack(struct wps_data *wps);
|
||||
struct wpabuf * wps_build_wsc_nack(struct wps_data *wps);
|
||||
|
||||
typedef enum wps_calc_key_mode {
|
||||
WPS_CALC_KEY_NORMAL = 0,
|
||||
WPS_CALC_KEY_NO_CALC,
|
||||
WPS_CALC_KEY_PRE_CALC,
|
||||
WPS_CALC_KEY_MAX,
|
||||
} wps_key_mode_t;
|
||||
|
||||
/* wps_attr_build.c */
|
||||
int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg, wps_key_mode_t mode);
|
||||
int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type);
|
||||
int wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type);
|
||||
int wps_build_config_methods(struct wpabuf *msg, u16 methods);
|
||||
int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid);
|
||||
int wps_build_dev_password_id(struct wpabuf *msg, u16 id);
|
||||
int wps_build_config_error(struct wpabuf *msg, u16 err);
|
||||
int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg);
|
||||
int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg);
|
||||
int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
|
||||
struct wpabuf *plain);
|
||||
int wps_build_version(struct wpabuf *msg);
|
||||
int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
|
||||
const u8 *auth_macs, size_t auth_macs_count);
|
||||
int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type);
|
||||
int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg);
|
||||
int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg);
|
||||
int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg);
|
||||
int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg);
|
||||
int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg);
|
||||
int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg);
|
||||
int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id,
|
||||
const struct wpabuf *pubkey, const u8 *dev_pw,
|
||||
size_t dev_pw_len);
|
||||
struct wpabuf * wps_ie_encapsulate(struct wpabuf *data);
|
||||
|
||||
/* wps_attr_process.c */
|
||||
int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
|
||||
const struct wpabuf *msg);
|
||||
int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
|
||||
const u8 *key_wrap_auth);
|
||||
int wps_process_cred(struct wps_parse_attr *attr,
|
||||
struct wps_credential *cred);
|
||||
int wps_process_ap_settings(struct wps_parse_attr *attr,
|
||||
struct wps_credential *cred);
|
||||
|
||||
/* wps_enrollee.c */
|
||||
struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
|
||||
enum wsc_op_code *op_code);
|
||||
enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
|
||||
enum wsc_op_code op_code,
|
||||
const struct wpabuf *msg);
|
||||
|
||||
/* wps_registrar.c */
|
||||
struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
|
||||
enum wsc_op_code *op_code);
|
||||
enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
|
||||
enum wsc_op_code op_code,
|
||||
const struct wpabuf *msg);
|
||||
int wps_build_cred(struct wps_data *wps, struct wpabuf *msg);
|
||||
int wps_device_store(struct wps_registrar *reg,
|
||||
struct wps_device_data *dev, const u8 *uuid);
|
||||
void wps_registrar_selected_registrar_changed(struct wps_registrar *reg);
|
||||
const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count);
|
||||
int wps_registrar_pbc_overlap(struct wps_registrar *reg,
|
||||
const u8 *addr, const u8 *uuid_e);
|
||||
#ifdef CONFIG_WPS_NFC
|
||||
|
||||
void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
|
||||
struct wps_nfc_pw_token *token);
|
||||
#endif
|
||||
|
||||
#endif /* WPS_I_H */
|
|
@ -18,6 +18,7 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "esp_err.h"
|
||||
#include "rom/ets_sys.h"
|
||||
|
||||
typedef long os_time_t;
|
||||
|
@ -201,6 +202,10 @@ char * os_readfile(const char *name, size_t *len);
|
|||
#define os_free(p) free((p))
|
||||
#endif
|
||||
|
||||
#ifndef os_bzero
|
||||
#define os_bzero(s, n) bzero(s, n)
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef os_strdup
|
||||
#ifdef _MSC_VER
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* CHAP-MD5
|
||||
*
|
||||
*/
|
||||
#ifdef CHAP_MD5
|
||||
|
||||
#include "wpa/includes.h"
|
||||
#include "wpa/common.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "wpa2/eap_peer/chap.h"
|
||||
|
||||
int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge,
|
||||
size_t challenge_len, u8 *response)
|
||||
{
|
||||
const u8 *addr[3];
|
||||
size_t len[3];
|
||||
|
||||
addr[0] = &id;
|
||||
len[0] = 1;
|
||||
addr[1] = secret;
|
||||
len[1] = secret_len;
|
||||
addr[2] = challenge;
|
||||
len[2] = challenge_len;
|
||||
return md5_vector(3, addr, len, response);
|
||||
}
|
||||
|
||||
#endif /* CHAP_MD5 */
|
|
@ -0,0 +1,731 @@
|
|||
/*
|
||||
* EAP peer state machines (RFC 4137)
|
||||
* Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*
|
||||
* This file implements the Peer State Machine as defined in RFC 4137. The used
|
||||
* states and state transitions match mostly with the RFC. However, there are
|
||||
* couple of additional transitions for working around small issues noticed
|
||||
* during testing. These exceptions are explained in comments within the
|
||||
* functions in this file. The method functions, m.func(), are similar to the
|
||||
* ones used in RFC 4137, but some small changes have used here to optimize
|
||||
* operations and to add functionality needed for fast re-authentication
|
||||
* (session resumption).
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
#include "wpa/includes.h"
|
||||
#include "wpa/common.h"
|
||||
#include "wpa/wpa_debug.h"
|
||||
#include "wpa/eapol_common.h"
|
||||
#include "wpa/ieee802_11_defs.h"
|
||||
#include "wpa/state_machine.h"
|
||||
#include "wpa/wpa.h"
|
||||
|
||||
#include "crypto/crypto.h"
|
||||
|
||||
#include "wpa2/utils/ext_password.h"
|
||||
#include "wpa2/tls/tls.h"
|
||||
#include "wpa2/eap_peer/eap_i.h"
|
||||
#include "wpa2/eap_peer/eap_config.h"
|
||||
#include "wpa2/eap_peer/eap.h"
|
||||
#include "wpa2/eap_peer/eap_tls.h"
|
||||
#ifdef EAP_PEER_METHOD
|
||||
#include "wpa2/eap_peer/eap_methods.h"
|
||||
#endif
|
||||
|
||||
|
||||
static bool gl_disable_time_check = true;
|
||||
void eap_peer_config_deinit(struct eap_sm *sm);
|
||||
void eap_peer_blob_deinit(struct eap_sm *sm);
|
||||
void eap_deinit_prev_method(struct eap_sm *sm, const char *txt);
|
||||
|
||||
extern bool ieee80211_unregister_wpa2_cb(void);
|
||||
|
||||
#ifdef EAP_PEER_METHOD
|
||||
static struct eap_method *eap_methods = NULL;
|
||||
|
||||
const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method)
|
||||
{
|
||||
struct eap_method *m;
|
||||
for (m = eap_methods; m; m = m->next) {
|
||||
if (m->vendor == vendor && m->method == method)
|
||||
return m;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct eap_method * eap_peer_get_methods(size_t *count)
|
||||
{
|
||||
int c = 0;
|
||||
struct eap_method *m;
|
||||
|
||||
for (m = eap_methods; m; m = m->next)
|
||||
c++;
|
||||
|
||||
*count = c;
|
||||
return eap_methods;
|
||||
}
|
||||
|
||||
EapType eap_peer_get_type(const char *name, int *vendor)
|
||||
{
|
||||
struct eap_method *m;
|
||||
for (m = eap_methods; m; m = m->next) {
|
||||
if (os_strcmp(m->name, name) == 0) {
|
||||
*vendor = m->vendor;
|
||||
return m->method;
|
||||
}
|
||||
}
|
||||
*vendor = EAP_VENDOR_IETF;
|
||||
return EAP_TYPE_NONE;
|
||||
}
|
||||
|
||||
static int
|
||||
eap_allowed_phase2_type(int vendor, int type)
|
||||
{
|
||||
if (vendor != EAP_VENDOR_IETF)
|
||||
return 0;
|
||||
return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS &&
|
||||
type != EAP_TYPE_FAST;
|
||||
}
|
||||
|
||||
u32 eap_get_phase2_type(const char *name, int *vendor)
|
||||
{
|
||||
int v;
|
||||
u8 type = eap_peer_get_type(name, &v);
|
||||
if (eap_allowed_phase2_type(v, type)) {
|
||||
*vendor = v;
|
||||
return type;
|
||||
}
|
||||
*vendor = EAP_VENDOR_IETF;
|
||||
return EAP_TYPE_NONE;
|
||||
}
|
||||
|
||||
struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
|
||||
size_t *count)
|
||||
{
|
||||
struct eap_method_type *buf;
|
||||
u32 method;
|
||||
int vendor;
|
||||
size_t mcount;
|
||||
const struct eap_method *methods, *m;
|
||||
|
||||
methods = eap_peer_get_methods(&mcount);
|
||||
if (methods == NULL)
|
||||
return NULL;
|
||||
*count = 0;
|
||||
buf = os_malloc(mcount * sizeof(struct eap_method_type));
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
|
||||
for (m = methods; m; m = m->next) {
|
||||
vendor = m->vendor;
|
||||
method = m->method;
|
||||
if (eap_allowed_phase2_type(vendor, method)) {
|
||||
if (vendor == EAP_VENDOR_IETF &&
|
||||
method == EAP_TYPE_TLS && config &&
|
||||
config->private_key2 == NULL)
|
||||
continue;
|
||||
buf[*count].vendor = vendor;
|
||||
buf[*count].method = method;
|
||||
(*count)++;
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
struct eap_method * eap_peer_method_alloc(int vendor, EapType method,
|
||||
const char *name)
|
||||
{
|
||||
struct eap_method *eap;
|
||||
eap = (struct eap_method *)os_zalloc(sizeof(*eap));
|
||||
if (eap == NULL)
|
||||
return NULL;
|
||||
eap->vendor = vendor;
|
||||
eap->method = method;
|
||||
eap->name = name;
|
||||
return eap;
|
||||
}
|
||||
|
||||
void eap_peer_method_free(struct eap_method *method)
|
||||
{
|
||||
os_free(method);
|
||||
}
|
||||
|
||||
int eap_peer_method_register(struct eap_method *method)
|
||||
{
|
||||
struct eap_method *m, *last = NULL;
|
||||
|
||||
if (method == NULL || method->name == NULL)
|
||||
return -1;
|
||||
for (m = eap_methods; m; m = m->next) {
|
||||
if (m->vendor == method->vendor &&
|
||||
m->method == method->method &&
|
||||
os_strcmp(m->name, method->name))
|
||||
return -2;
|
||||
last = m;
|
||||
}
|
||||
if (last)
|
||||
last->next = method;
|
||||
else
|
||||
eap_methods = method;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void eap_peer_unregister_methods(void)
|
||||
{
|
||||
struct eap_method *m;
|
||||
while (eap_methods) {
|
||||
m = eap_methods;
|
||||
eap_methods = eap_methods->next;
|
||||
|
||||
if (m->free)
|
||||
m->free(m);
|
||||
else
|
||||
eap_peer_method_free(m);
|
||||
}
|
||||
}
|
||||
|
||||
int eap_peer_register_methods(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
#ifdef EAP_MD5
|
||||
if (ret == 0)
|
||||
ret = eap_peer_md5_register();
|
||||
#endif
|
||||
|
||||
#ifdef EAP_TLS
|
||||
if (ret == 0)
|
||||
ret = eap_peer_tls_register();
|
||||
#endif
|
||||
|
||||
#ifdef EAP_MSCHAPv2
|
||||
if (ret == 0)
|
||||
ret = eap_peer_mschapv2_register();
|
||||
#endif
|
||||
|
||||
#ifdef EAP_PEAP
|
||||
if (ret == 0)
|
||||
ret = eap_peer_peap_register();
|
||||
#endif
|
||||
|
||||
#ifdef EAP_TTLS
|
||||
if (ret == 0)
|
||||
ret = eap_peer_ttls_register();
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void eap_deinit_prev_method(struct eap_sm *sm, const char *txt)
|
||||
{
|
||||
if (sm->m == NULL || sm->eap_method_priv == NULL)
|
||||
return;
|
||||
sm->m->deinit(sm, sm->eap_method_priv);
|
||||
sm->eap_method_priv = NULL;
|
||||
sm->m = NULL;
|
||||
}
|
||||
|
||||
struct wpabuf * eap_sm_build_identity_resp(struct eap_sm *sm, u8 id, int encrypted)
|
||||
{
|
||||
const u8 *identity;
|
||||
size_t identity_len;
|
||||
struct wpabuf *eap_buf = NULL;
|
||||
struct eap_peer_config *config = eap_get_config(sm);
|
||||
|
||||
if (config == NULL) {
|
||||
wpa_printf(MSG_ERROR, "EAP: Build Identity Resp-> configuration was not available\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sm->m && sm->m->get_identity) {
|
||||
identity = sm->m->get_identity(sm,
|
||||
sm->eap_method_priv,
|
||||
&identity_len);
|
||||
} else if (!encrypted && config->anonymous_identity) {
|
||||
identity = config->anonymous_identity;
|
||||
identity_len = config->anonymous_identity_len;
|
||||
} else {
|
||||
identity = config->identity;
|
||||
identity_len = config->identity_len;
|
||||
}
|
||||
|
||||
if (identity == NULL) {
|
||||
wpa_printf(MSG_ERROR, "EAP: Build Identity Resp-> identity was not available\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
eap_buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
|
||||
identity_len, EAP_CODE_RESPONSE, id);
|
||||
if (!eap_buf) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wpabuf_put_data(eap_buf, identity, identity_len);
|
||||
return eap_buf;
|
||||
}
|
||||
|
||||
struct wpabuf * eap_sm_build_nak(struct eap_sm *sm, EapType type, u8 id)
|
||||
{
|
||||
size_t count = 0;
|
||||
int found = 0;
|
||||
struct wpabuf *resp;
|
||||
const struct eap_method *methods, *m;
|
||||
|
||||
methods = eap_peer_get_methods(&count);
|
||||
if (methods == NULL)
|
||||
return NULL;
|
||||
|
||||
if (type == EAP_TYPE_EXPANDED) {
|
||||
/*Build Expanded NAK*/
|
||||
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EXPANDED,
|
||||
8 + 8 * (count + 1), EAP_CODE_RESPONSE, id);
|
||||
if (resp == NULL)
|
||||
return NULL;
|
||||
wpabuf_put_be24(resp, EAP_VENDOR_IETF);
|
||||
wpabuf_put_be32(resp, EAP_TYPE_NAK);
|
||||
} else {
|
||||
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK,
|
||||
sizeof(struct eap_hdr) + 1 + count + 1,
|
||||
EAP_CODE_RESPONSE, id);
|
||||
if (resp == NULL)
|
||||
return NULL;
|
||||
wpabuf_put(resp, 0);
|
||||
}
|
||||
|
||||
for (m = methods; m; m = m->next) {
|
||||
if (type == EAP_TYPE_EXPANDED) {
|
||||
wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
|
||||
wpabuf_put_be24(resp, m->vendor);
|
||||
wpabuf_put_be32(resp, m->method);
|
||||
} else
|
||||
wpabuf_put_u8(resp, EAP_TYPE_NONE);
|
||||
found++;
|
||||
}
|
||||
if (!found) {
|
||||
if (type == EAP_TYPE_EXPANDED) {
|
||||
wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
|
||||
wpabuf_put_be24(resp, EAP_VENDOR_IETF);
|
||||
wpabuf_put_be32(resp, EAP_TYPE_NONE);
|
||||
} else
|
||||
wpabuf_put_u8(resp, EAP_TYPE_NONE);
|
||||
}
|
||||
eap_update_len(resp);
|
||||
return resp;
|
||||
}
|
||||
#endif
|
||||
|
||||
int eap_peer_config_init(
|
||||
struct eap_sm *sm, u8 *private_key_passwd,
|
||||
int private_key_passwd_len)
|
||||
{
|
||||
if (!sm)
|
||||
return -1;
|
||||
|
||||
sm->config.anonymous_identity = NULL;
|
||||
sm->config.identity = NULL;
|
||||
sm->config.password = NULL;
|
||||
sm->config.new_password = NULL;
|
||||
|
||||
sm->config.private_key_passwd = private_key_passwd;
|
||||
sm->config.client_cert = (u8 *)sm->blob[0].name;
|
||||
sm->config.private_key = (u8 *)sm->blob[1].name;
|
||||
sm->config.ca_cert = (u8 *)sm->blob[2].name;
|
||||
|
||||
sm->config.ca_path = NULL;
|
||||
|
||||
sm->config.fragment_size = 1400; /* fragment size */
|
||||
|
||||
/* anonymous identity */
|
||||
if (g_wpa_anonymous_identity && g_wpa_anonymous_identity_len > 0) {
|
||||
sm->config.anonymous_identity_len = g_wpa_anonymous_identity_len;
|
||||
sm->config.anonymous_identity = (u8 *)os_zalloc(sm->config.anonymous_identity_len);
|
||||
if (sm->config.anonymous_identity == NULL)
|
||||
return -2;
|
||||
os_memcpy(sm->config.anonymous_identity, g_wpa_anonymous_identity, g_wpa_anonymous_identity_len);
|
||||
}
|
||||
|
||||
/* Configre identity */
|
||||
if (g_wpa_username && g_wpa_username_len > 0) {
|
||||
sm->config.identity_len = g_wpa_username_len;
|
||||
sm->config.identity = (u8 *)os_zalloc(sm->config.identity_len);
|
||||
if (sm->config.identity == NULL) {
|
||||
return -2;
|
||||
}
|
||||
os_memcpy(sm->config.identity, g_wpa_username, g_wpa_username_len);
|
||||
}
|
||||
|
||||
if (g_wpa_password && g_wpa_password_len) {
|
||||
sm->config.password_len = g_wpa_password_len;
|
||||
sm->config.password = (u8 *)os_zalloc(sm->config.password_len);
|
||||
if (sm->config.password == NULL)
|
||||
return -2;
|
||||
os_memcpy(sm->config.password, g_wpa_password, sm->config.password_len);
|
||||
}
|
||||
|
||||
if (g_wpa_new_password && g_wpa_new_password_len) {
|
||||
sm->config.new_password_len = g_wpa_new_password_len;
|
||||
sm->config.new_password = (u8 *)os_zalloc(sm->config.new_password_len);
|
||||
if (sm->config.new_password == NULL)
|
||||
return -2;
|
||||
os_memcpy(sm->config.new_password, g_wpa_new_password,
|
||||
sm->config.new_password_len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void eap_peer_config_deinit(struct eap_sm *sm)
|
||||
{
|
||||
if (!sm)
|
||||
return;
|
||||
|
||||
os_free(sm->config.anonymous_identity);
|
||||
os_free(sm->config.identity);
|
||||
os_free(sm->config.password);
|
||||
os_free(sm->config.new_password);
|
||||
os_bzero(&sm->config, sizeof(struct eap_peer_config));
|
||||
}
|
||||
|
||||
int eap_peer_blob_init(struct eap_sm *sm)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
if (!sm)
|
||||
return -1;
|
||||
|
||||
if (g_wpa_client_cert && g_wpa_client_cert_len) {
|
||||
sm->blob[0].name = (char *)os_zalloc(BLOB_NAME_LEN+1);
|
||||
if (sm->blob[0].name == NULL) {
|
||||
ret = -2;
|
||||
goto _out;
|
||||
}
|
||||
os_strncpy(sm->blob[0].name, CLIENT_CERT_NAME, BLOB_NAME_LEN);
|
||||
sm->blob[0].len = g_wpa_client_cert_len;
|
||||
sm->blob[0].data = g_wpa_client_cert;
|
||||
}
|
||||
|
||||
if (g_wpa_private_key && g_wpa_private_key_len) {
|
||||
sm->blob[1].name = (char *)os_zalloc(BLOB_NAME_LEN+1);
|
||||
if (sm->blob[1].name == NULL) {
|
||||
ret = -2;
|
||||
goto _out;
|
||||
}
|
||||
os_strncpy(sm->blob[1].name, PRIVATE_KEY_NAME, BLOB_NAME_LEN);
|
||||
sm->blob[1].len = g_wpa_private_key_len;
|
||||
sm->blob[1].data = g_wpa_private_key;
|
||||
}
|
||||
|
||||
if (g_wpa_ca_cert && g_wpa_ca_cert_len) {
|
||||
sm->blob[2].name = (char *)os_zalloc(BLOB_NAME_LEN+1);
|
||||
if (sm->blob[2].name == NULL) {
|
||||
ret = -2;
|
||||
goto _out;
|
||||
}
|
||||
os_strncpy(sm->blob[2].name, CA_CERT_NAME, BLOB_NAME_LEN);
|
||||
sm->blob[2].len = g_wpa_ca_cert_len;
|
||||
sm->blob[2].data = g_wpa_ca_cert;
|
||||
}
|
||||
|
||||
return 0;
|
||||
_out:
|
||||
for (i = 0; i < BLOB_NUM; i++) {
|
||||
if (sm->blob[i].name) {
|
||||
os_free(sm->blob[i].name);
|
||||
sm->blob[i].name = NULL;
|
||||
}
|
||||
}
|
||||
os_bzero(&sm->blob[0], sizeof(struct wpa_config_blob)*BLOB_NUM);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void eap_peer_blob_deinit(struct eap_sm *sm)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < BLOB_NUM; i++) {
|
||||
if (sm->blob[i].name) {
|
||||
os_free(sm->blob[i].name);
|
||||
sm->blob[i].name = NULL;
|
||||
}
|
||||
}
|
||||
os_bzero(&sm->blob[0], sizeof(struct wpa_config_blob)*BLOB_NUM);
|
||||
|
||||
sm->config.client_cert = NULL;
|
||||
sm->config.private_key = NULL;
|
||||
sm->config.ca_cert = NULL;
|
||||
}
|
||||
|
||||
void eap_sm_abort(struct eap_sm *sm)
|
||||
{
|
||||
wpabuf_free(sm->lastRespData);
|
||||
sm->lastRespData = NULL;
|
||||
//os_free(sm->eapKeyData);
|
||||
//sm->eapKeyData = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* eap_get_config - Get current network configuration
|
||||
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
|
||||
* Returns: Pointer to the current network configuration or %NULL if not found
|
||||
*
|
||||
* EAP peer methods should avoid using this function if they can use other
|
||||
* access functions, like eap_get_config_identity() and
|
||||
* eap_get_config_password(), that do not require direct access to
|
||||
* struct eap_peer_config.
|
||||
*/
|
||||
struct eap_peer_config * eap_get_config(struct eap_sm *sm)
|
||||
{
|
||||
return &sm->config;
|
||||
}
|
||||
|
||||
const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len)
|
||||
{
|
||||
struct eap_peer_config *config = eap_get_config(sm);
|
||||
if (config == NULL)
|
||||
return NULL;
|
||||
*len = config->identity_len;
|
||||
return config->identity;
|
||||
}
|
||||
|
||||
const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len)
|
||||
{
|
||||
struct eap_peer_config *config = eap_get_config(sm);
|
||||
if (config == NULL)
|
||||
return NULL;
|
||||
*len = config->password_len;
|
||||
return config->password;
|
||||
}
|
||||
|
||||
const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash)
|
||||
{
|
||||
struct eap_peer_config *config = eap_get_config(sm);
|
||||
if (config == NULL)
|
||||
return NULL;
|
||||
|
||||
*len = config->password_len;
|
||||
if (hash)
|
||||
*hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH);
|
||||
return config->password;
|
||||
}
|
||||
|
||||
const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len)
|
||||
{
|
||||
struct eap_peer_config *config = eap_get_config(sm);
|
||||
if (config == NULL)
|
||||
return NULL;
|
||||
*len = config->new_password_len;
|
||||
return config->new_password;
|
||||
}
|
||||
/**
|
||||
* eap_get_config_blob - Get a named configuration blob
|
||||
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
|
||||
* @name: Name of the blob
|
||||
* Returns: Pointer to blob data or %NULL if not found
|
||||
*/
|
||||
const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm,
|
||||
const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!sm)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < BLOB_NUM; i++) {
|
||||
if (sm->blob[i].name == NULL)
|
||||
continue;
|
||||
if (os_strncmp(name, sm->blob[i].name, BLOB_NAME_LEN) == 0) {
|
||||
return &sm->blob[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t esp_wifi_sta_wpa2_ent_set_cert_key(const unsigned char *client_cert, int client_cert_len, const unsigned char *private_key, int private_key_len, const unsigned char *private_key_passwd, int private_key_passwd_len)
|
||||
{
|
||||
if (client_cert && client_cert_len > 0) {
|
||||
g_wpa_client_cert = client_cert;
|
||||
g_wpa_client_cert_len = client_cert_len;
|
||||
}
|
||||
if (private_key && private_key_len > 0) {
|
||||
g_wpa_private_key = private_key;
|
||||
g_wpa_private_key_len = private_key_len;
|
||||
}
|
||||
if (private_key_passwd && private_key_passwd_len > 0) {
|
||||
g_wpa_private_key_passwd = private_key_passwd;
|
||||
g_wpa_private_key_passwd_len = private_key_passwd_len;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_wifi_sta_wpa2_ent_clear_cert_key(void)
|
||||
{
|
||||
ieee80211_unregister_wpa2_cb();
|
||||
|
||||
g_wpa_client_cert = NULL;
|
||||
g_wpa_client_cert_len = 0;
|
||||
g_wpa_private_key = NULL;
|
||||
g_wpa_private_key_len = 0;
|
||||
g_wpa_private_key_passwd = NULL;
|
||||
g_wpa_private_key_passwd_len = 0;
|
||||
}
|
||||
|
||||
esp_err_t esp_wifi_sta_wpa2_ent_set_ca_cert(const unsigned char *ca_cert, int ca_cert_len)
|
||||
{
|
||||
if (ca_cert && ca_cert_len > 0) {
|
||||
g_wpa_ca_cert = ca_cert;
|
||||
g_wpa_ca_cert_len = ca_cert_len;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_wifi_sta_wpa2_ent_clear_ca_cert(void)
|
||||
{
|
||||
g_wpa_ca_cert = NULL;
|
||||
g_wpa_ca_cert_len = 0;
|
||||
}
|
||||
|
||||
#define ANONYMOUS_ID_LEN_MAX 128
|
||||
esp_err_t esp_wifi_sta_wpa2_ent_set_identity(const unsigned char *identity, int len)
|
||||
{
|
||||
if (len <= 0 || len > ANONYMOUS_ID_LEN_MAX) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (g_wpa_anonymous_identity) {
|
||||
os_free(g_wpa_anonymous_identity);
|
||||
g_wpa_anonymous_identity = NULL;
|
||||
}
|
||||
|
||||
g_wpa_anonymous_identity = (u8 *)os_zalloc(len);
|
||||
if (g_wpa_anonymous_identity == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
os_memcpy(g_wpa_anonymous_identity, identity, len);
|
||||
g_wpa_anonymous_identity_len = len;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_wifi_sta_wpa2_ent_clear_identity(void)
|
||||
{
|
||||
if (g_wpa_anonymous_identity)
|
||||
os_free(g_wpa_anonymous_identity);
|
||||
|
||||
g_wpa_anonymous_identity = NULL;
|
||||
g_wpa_anonymous_identity_len = 0;
|
||||
}
|
||||
|
||||
#define USERNAME_LEN_MAX 128
|
||||
esp_err_t esp_wifi_sta_wpa2_ent_set_username(const unsigned char *username, int len)
|
||||
{
|
||||
if (len <= 0 || len > USERNAME_LEN_MAX)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
|
||||
if (g_wpa_username) {
|
||||
os_free(g_wpa_username);
|
||||
g_wpa_username = NULL;
|
||||
}
|
||||
|
||||
g_wpa_username = (u8 *)os_zalloc(len);
|
||||
if (g_wpa_username == NULL)
|
||||
return ESP_ERR_NO_MEM;
|
||||
|
||||
os_memcpy(g_wpa_username, username, len);
|
||||
g_wpa_username_len = len;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_wifi_sta_wpa2_ent_clear_username(void)
|
||||
{
|
||||
if (g_wpa_username)
|
||||
os_free(g_wpa_username);
|
||||
|
||||
g_wpa_username = NULL;
|
||||
g_wpa_username_len = 0;
|
||||
}
|
||||
|
||||
esp_err_t esp_wifi_sta_wpa2_ent_set_password(const unsigned char *password, int len)
|
||||
{
|
||||
if (len <= 0)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
|
||||
if (g_wpa_password) {
|
||||
os_free(g_wpa_password);
|
||||
g_wpa_password = NULL;
|
||||
}
|
||||
|
||||
g_wpa_password = (u8 *)os_zalloc(len);
|
||||
if (g_wpa_password == NULL)
|
||||
return ESP_ERR_NO_MEM;
|
||||
|
||||
os_memcpy(g_wpa_password, password, len);
|
||||
g_wpa_password_len = len;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_wifi_sta_wpa2_ent_clear_password(void)
|
||||
{
|
||||
if (g_wpa_password)
|
||||
os_free(g_wpa_password);
|
||||
g_wpa_password = NULL;
|
||||
g_wpa_password_len = 0;
|
||||
}
|
||||
|
||||
esp_err_t esp_wifi_sta_wpa2_ent_set_new_password(const unsigned char *new_password, int len)
|
||||
{
|
||||
if (len <= 0)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
|
||||
if (g_wpa_new_password) {
|
||||
os_free(g_wpa_new_password);
|
||||
g_wpa_new_password = NULL;
|
||||
}
|
||||
|
||||
g_wpa_new_password = (u8 *)os_zalloc(len);
|
||||
if (g_wpa_new_password == NULL)
|
||||
return ESP_ERR_NO_MEM;
|
||||
|
||||
os_memcpy(g_wpa_new_password, new_password, len);
|
||||
g_wpa_password_len = len;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_wifi_sta_wpa2_ent_clear_new_password(void)
|
||||
{
|
||||
if (g_wpa_new_password)
|
||||
os_free(g_wpa_new_password);
|
||||
g_wpa_new_password = NULL;
|
||||
g_wpa_new_password_len = 0;
|
||||
}
|
||||
|
||||
esp_err_t esp_wifi_sta_wpa2_ent_set_disable_time_check(bool disable)
|
||||
{
|
||||
gl_disable_time_check = disable;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool wifi_sta_get_enterprise_disable_time_check(void)
|
||||
{
|
||||
return gl_disable_time_check;
|
||||
}
|
||||
|
||||
esp_err_t esp_wifi_sta_wpa2_ent_get_disable_time_check(bool *disable)
|
||||
{
|
||||
*disable = wifi_sta_get_enterprise_disable_time_check();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* EAP common peer/server definitions
|
||||
* Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "wpa2/eap_peer/eap_defs.h"
|
||||
#include "wpa2/eap_peer/eap_common.h"
|
||||
|
||||
/**
|
||||
* eap_hdr_len_valid - Validate EAP header length field
|
||||
* @msg: EAP frame (starting with EAP header)
|
||||
* @min_payload: Minimum payload length needed
|
||||
* Returns: 1 for valid header, 0 for invalid
|
||||
*
|
||||
* This is a helper function that does minimal validation of EAP messages. The
|
||||
* length field is verified to be large enough to include the header and not
|
||||
* too large to go beyond the end of the buffer.
|
||||
*/
|
||||
int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload)
|
||||
{
|
||||
const struct eap_hdr *hdr;
|
||||
size_t len;
|
||||
|
||||
if (msg == NULL)
|
||||
return 0;
|
||||
|
||||
hdr = wpabuf_head(msg);
|
||||
|
||||
if (wpabuf_len(msg) < sizeof(*hdr)) {
|
||||
wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = be_to_host16(hdr->length);
|
||||
if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) {
|
||||
wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_hdr_validate - Validate EAP header
|
||||
* @vendor: Expected EAP Vendor-Id (0 = IETF)
|
||||
* @eap_type: Expected EAP type number
|
||||
* @msg: EAP frame (starting with EAP header)
|
||||
* @plen: Pointer to variable to contain the returned payload length
|
||||
* Returns: Pointer to EAP payload (after type field), or %NULL on failure
|
||||
*
|
||||
* This is a helper function for EAP method implementations. This is usually
|
||||
* called in the beginning of struct eap_method::process() function to verify
|
||||
* that the received EAP request packet has a valid header. This function is
|
||||
* able to process both legacy and expanded EAP headers and in most cases, the
|
||||
* caller can just use the returned payload pointer (into *plen) for processing
|
||||
* the payload regardless of whether the packet used the expanded EAP header or
|
||||
* not.
|
||||
*/
|
||||
const u8 * eap_hdr_validate(int vendor, EapType eap_type,
|
||||
const struct wpabuf *msg, size_t *plen)
|
||||
{
|
||||
const struct eap_hdr *hdr;
|
||||
const u8 *pos;
|
||||
size_t len;
|
||||
|
||||
if (!eap_hdr_len_valid(msg, 1))
|
||||
return NULL;
|
||||
|
||||
hdr = wpabuf_head(msg);
|
||||
len = be_to_host16(hdr->length);
|
||||
pos = (const u8 *) (hdr + 1);
|
||||
|
||||
if (*pos == EAP_TYPE_EXPANDED) {
|
||||
int exp_vendor;
|
||||
u32 exp_type;
|
||||
if (len < sizeof(*hdr) + 8) {
|
||||
wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP "
|
||||
"length");
|
||||
return NULL;
|
||||
}
|
||||
pos++;
|
||||
exp_vendor = WPA_GET_BE24(pos);
|
||||
pos += 3;
|
||||
exp_type = WPA_GET_BE32(pos);
|
||||
pos += 4;
|
||||
if (exp_vendor != vendor || exp_type != (u32) eap_type) {
|
||||
wpa_printf(MSG_INFO, "EAP: Invalid expanded frame "
|
||||
"type");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*plen = len - sizeof(*hdr) - 8;
|
||||
return pos;
|
||||
} else {
|
||||
if (vendor != EAP_VENDOR_IETF || *pos != eap_type) {
|
||||
wpa_printf(MSG_INFO, "EAP: Invalid frame type");
|
||||
return NULL;
|
||||
}
|
||||
*plen = len - sizeof(*hdr) - 1;
|
||||
return pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_msg_alloc - Allocate a buffer for an EAP message
|
||||
* @vendor: Vendor-Id (0 = IETF)
|
||||
* @type: EAP type
|
||||
* @payload_len: Payload length in bytes (data after Type)
|
||||
* @code: Message Code (EAP_CODE_*)
|
||||
* @identifier: Identifier
|
||||
* Returns: Pointer to the allocated message buffer or %NULL on error
|
||||
*
|
||||
* This function can be used to allocate a buffer for an EAP message and fill
|
||||
* in the EAP header. This function is automatically using expanded EAP header
|
||||
* if the selected Vendor-Id is not IETF. In other words, most EAP methods do
|
||||
* not need to separately select which header type to use when using this
|
||||
* function to allocate the message buffers. The returned buffer has room for
|
||||
* payload_len bytes and has the EAP header and Type field already filled in.
|
||||
*/
|
||||
struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
|
||||
u8 code, u8 identifier)
|
||||
{
|
||||
struct wpabuf *buf;
|
||||
struct eap_hdr *hdr;
|
||||
size_t len;
|
||||
|
||||
len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) +
|
||||
payload_len;
|
||||
buf = wpabuf_alloc(len);
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
|
||||
hdr = wpabuf_put(buf, sizeof(*hdr));
|
||||
hdr->code = code;
|
||||
hdr->identifier = identifier;
|
||||
hdr->length = host_to_be16(len);
|
||||
|
||||
if (vendor == EAP_VENDOR_IETF) {
|
||||
wpabuf_put_u8(buf, type);
|
||||
} else {
|
||||
wpabuf_put_u8(buf, EAP_TYPE_EXPANDED);
|
||||
wpabuf_put_be24(buf, vendor);
|
||||
wpabuf_put_be32(buf, type);
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_update_len - Update EAP header length
|
||||
* @msg: EAP message from eap_msg_alloc
|
||||
*
|
||||
* This function updates the length field in the EAP header to match with the
|
||||
* current length for the buffer. This allows eap_msg_alloc() to be used to
|
||||
* allocate a larger buffer than the exact message length (e.g., if exact
|
||||
* message length is not yet known).
|
||||
*/
|
||||
void eap_update_len(struct wpabuf *msg)
|
||||
{
|
||||
struct eap_hdr *hdr;
|
||||
hdr = wpabuf_mhead(msg);
|
||||
if (wpabuf_len(msg) < sizeof(*hdr))
|
||||
return;
|
||||
hdr->length = host_to_be16(wpabuf_len(msg));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_get_id - Get EAP Identifier from wpabuf
|
||||
* @msg: Buffer starting with an EAP header
|
||||
* Returns: The Identifier field from the EAP header
|
||||
*/
|
||||
u8 eap_get_id(const struct wpabuf *msg)
|
||||
{
|
||||
const struct eap_hdr *eap;
|
||||
|
||||
if (wpabuf_len(msg) < sizeof(*eap))
|
||||
return 0;
|
||||
|
||||
eap = wpabuf_head(msg);
|
||||
return eap->identifier;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_get_id - Get EAP Type from wpabuf
|
||||
* @msg: Buffer starting with an EAP header
|
||||
* Returns: The EAP Type after the EAP header
|
||||
*/
|
||||
EapType eap_get_type(const struct wpabuf *msg)
|
||||
{
|
||||
if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1)
|
||||
return EAP_TYPE_NONE;
|
||||
|
||||
return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)];
|
||||
}
|
|
@ -0,0 +1,671 @@
|
|||
/*
|
||||
* EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
|
||||
* Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
|
||||
#ifdef EAP_MSCHAPv2
|
||||
|
||||
#include "wpa/wpa.h"
|
||||
#include "wpa/includes.h"
|
||||
#include "wpa/common.h"
|
||||
#include "crypto/random.h"
|
||||
#include "crypto/ms_funcs.h"
|
||||
#include "wpa2/tls/tls.h"
|
||||
#include "wpa2/eap_peer/eap_i.h"
|
||||
#include "wpa2/eap_peer/eap_defs.h"
|
||||
#include "wpa2/eap_peer/eap_tls_common.h"
|
||||
#include "wpa2/eap_peer/eap_config.h"
|
||||
#include "wpa2/eap_peer/mschapv2.h"
|
||||
#include "wpa2/eap_peer/eap_methods.h"
|
||||
|
||||
#define MSCHAPV2_OP_CHALLENGE 1
|
||||
#define MSCHAPV2_OP_RESPONSE 2
|
||||
#define MSCHAPV2_OP_SUCCESS 3
|
||||
#define MSCHAPV2_OP_FAILURE 4
|
||||
#define MSCHAPV2_OP_CHANGE_PASSWORD 7
|
||||
|
||||
#define PASSWD_CHANGE_CHAL_LEN 16
|
||||
#define MSCHAPV2_KEY_LEN 16
|
||||
|
||||
#define ERROR_RESTRICTED_LOGON_HOURS 646
|
||||
#define ERROR_ACCT_DISABLED 647
|
||||
#define ERROR_PASSWD_EXPIRED 648
|
||||
#define ERROR_NO_DIALIN_PERMISSION 649
|
||||
#define ERROR_AUTHENTICATION_FAILURE 691
|
||||
#define ERROR_CHANGING_PASSWORD 709
|
||||
|
||||
struct eap_mschapv2_hdr {
|
||||
u8 op_code;
|
||||
u8 mschapv2_id;
|
||||
u8 ms_length[2];
|
||||
} __packed;
|
||||
|
||||
struct ms_response {
|
||||
u8 peer_challenge[MSCHAPV2_CHAL_LEN];
|
||||
u8 reserved[8];
|
||||
u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
|
||||
u8 flags;
|
||||
} __packed;
|
||||
|
||||
struct ms_change_password {
|
||||
u8 encr_password[516];
|
||||
u8 encr_hash[16];
|
||||
u8 peer_challenge[MSCHAPV2_CHAL_LEN];
|
||||
u8 reserved[8];
|
||||
u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
|
||||
u8 flags[2];
|
||||
} __packed;
|
||||
|
||||
struct eap_mschapv2_data {
|
||||
u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
|
||||
int auth_response_valid;
|
||||
|
||||
int prev_error;
|
||||
u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN];
|
||||
int passwd_change_challenge_valid;
|
||||
int passwd_change_version;
|
||||
|
||||
u8 *peer_challenge;
|
||||
u8 *auth_challenge;
|
||||
|
||||
int phase2;
|
||||
u8 master_key[MSCHAPV2_MASTER_KEY_LEN];
|
||||
int master_key_valid;
|
||||
int success;
|
||||
|
||||
struct wpabuf *prev_challenge;
|
||||
};
|
||||
|
||||
static void
|
||||
eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
|
||||
{
|
||||
struct eap_mschapv2_data *data = priv;
|
||||
|
||||
os_free(data->peer_challenge);
|
||||
os_free(data->auth_challenge);
|
||||
wpabuf_free(data->prev_challenge);
|
||||
os_free(data);
|
||||
}
|
||||
|
||||
static void *
|
||||
eap_mschapv2_init(struct eap_sm *sm)
|
||||
{
|
||||
struct eap_mschapv2_data *data;
|
||||
data = (struct eap_mschapv2_data *)os_zalloc(sizeof(*data));
|
||||
if (data == NULL)
|
||||
return NULL;
|
||||
|
||||
data->phase2 = sm->init_phase2;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static struct wpabuf *
|
||||
eap_mschapv2_challenge_reply(
|
||||
struct eap_sm *sm, struct eap_mschapv2_data *data,
|
||||
u8 id, u8 mschapv2_id, const u8 *auth_challenge)
|
||||
{
|
||||
struct wpabuf *resp;
|
||||
struct eap_mschapv2_hdr *ms;
|
||||
u8 *peer_challenge;
|
||||
int ms_len;
|
||||
struct ms_response *r;
|
||||
size_t identity_len, password_len;
|
||||
const u8 *identity, *password;
|
||||
int pwhash;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generate Challenge Response\n");
|
||||
|
||||
identity = eap_get_config_identity(sm, &identity_len);
|
||||
password = eap_get_config_password2(sm, &password_len, &pwhash);
|
||||
if (identity == NULL || password == NULL)
|
||||
return NULL;
|
||||
|
||||
ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len;
|
||||
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
|
||||
ms_len, EAP_CODE_RESPONSE, id);
|
||||
if (resp == NULL)
|
||||
return NULL;
|
||||
|
||||
ms = wpabuf_put(resp, sizeof(*ms));
|
||||
ms->op_code = MSCHAPV2_OP_RESPONSE;
|
||||
ms->mschapv2_id = mschapv2_id;
|
||||
if (data->prev_error)
|
||||
ms->mschapv2_id++;
|
||||
WPA_PUT_BE16(ms->ms_length, ms_len);
|
||||
wpabuf_put_u8(resp, sizeof(*r));
|
||||
|
||||
/* Response */
|
||||
r = wpabuf_put(resp, sizeof(*r));
|
||||
peer_challenge = r->peer_challenge;
|
||||
if (data->peer_challenge) {
|
||||
peer_challenge = data->peer_challenge;
|
||||
os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
|
||||
} else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) {
|
||||
wpabuf_free(resp);
|
||||
return NULL;
|
||||
}
|
||||
os_memset(r->reserved, 0, 8);
|
||||
if (data->auth_challenge)
|
||||
auth_challenge = data->auth_challenge;
|
||||
if (mschapv2_derive_response(identity, identity_len, password,
|
||||
password_len, pwhash, auth_challenge,
|
||||
peer_challenge, r->nt_response,
|
||||
data->auth_response, data->master_key)) {
|
||||
wpabuf_free(resp);
|
||||
return NULL;
|
||||
}
|
||||
data->auth_response_valid = 1;
|
||||
data->master_key_valid = 1;
|
||||
|
||||
r->flags = 0;
|
||||
|
||||
wpabuf_put_data(resp, identity, identity_len);
|
||||
return resp;
|
||||
}
|
||||
|
||||
static struct wpabuf *
|
||||
eap_mschapv2_challenge(
|
||||
struct eap_sm *sm, struct eap_mschapv2_data *data,
|
||||
struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req,
|
||||
size_t req_len, u8 id)
|
||||
{
|
||||
size_t len, challenge_len;
|
||||
const u8 *pos, *challenge;
|
||||
|
||||
if (eap_get_config_identity(sm, &len) == NULL ||
|
||||
eap_get_config_password(sm, &len) == NULL)
|
||||
return NULL;
|
||||
|
||||
if (req_len < sizeof(*req) + 1) {
|
||||
ret->ignore = true;
|
||||
return NULL;
|
||||
}
|
||||
pos = (const u8 *)(req + 1);
|
||||
challenge_len = *pos++;
|
||||
len = req_len - sizeof(*req) - 1;
|
||||
if (challenge_len != MSCHAPV2_CHAL_LEN) {
|
||||
ret->ignore = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (len < challenge_len) {
|
||||
ret->ignore = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (data->passwd_change_challenge_valid)
|
||||
challenge = data->passwd_change_challenge;
|
||||
else
|
||||
challenge = pos;
|
||||
pos += challenge_len;
|
||||
len -= challenge_len;
|
||||
|
||||
ret->ignore = false;
|
||||
ret->methodState = METHOD_MAY_CONT;
|
||||
ret->decision = DECISION_FAIL;
|
||||
ret->allowNotifications = true;
|
||||
|
||||
return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id,
|
||||
challenge);
|
||||
}
|
||||
|
||||
static void
|
||||
eap_mschapv2_password_changed(struct eap_sm *sm,
|
||||
struct eap_mschapv2_data *data)
|
||||
{
|
||||
struct eap_peer_config *config = eap_get_config(sm);
|
||||
if (config && config->new_password) {
|
||||
data->prev_error = 0;
|
||||
os_free(config->password);
|
||||
if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
|
||||
} else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) {
|
||||
config->password = os_malloc(16);
|
||||
config->password_len = 16;
|
||||
if (config->password) {
|
||||
nt_password_hash(config->new_password,
|
||||
config->new_password_len,
|
||||
config->password);
|
||||
}
|
||||
os_free(config->new_password);
|
||||
} else {
|
||||
config->password = config->new_password;
|
||||
config->password_len = config->new_password_len;
|
||||
}
|
||||
config->new_password = NULL;
|
||||
config->new_password_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static struct wpabuf *
|
||||
eap_mschapv2_success(struct eap_sm *sm,
|
||||
struct eap_mschapv2_data *data,
|
||||
struct eap_method_ret *ret,
|
||||
const struct eap_mschapv2_hdr *req,
|
||||
size_t req_len, u8 id)
|
||||
{
|
||||
struct wpabuf *resp;
|
||||
const u8 *pos;
|
||||
size_t len;
|
||||
|
||||
len = req_len - sizeof(*req);
|
||||
pos = (const u8 *)(req + 1);
|
||||
if (!data->auth_response_valid ||
|
||||
mschapv2_verify_auth_response(data->auth_response, pos, len)) {
|
||||
ret->methodState = METHOD_NONE;
|
||||
ret->decision = DECISION_FAIL;
|
||||
return NULL;
|
||||
}
|
||||
pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
|
||||
len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
|
||||
while (len > 0 && *pos == ' ') {
|
||||
pos++;
|
||||
len--;
|
||||
}
|
||||
|
||||
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
|
||||
EAP_CODE_RESPONSE, id);
|
||||
if (resp == NULL) {
|
||||
ret->ignore = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS);
|
||||
ret->methodState = METHOD_DONE;
|
||||
ret->decision = DECISION_UNCOND_SUCC;
|
||||
ret->allowNotifications = false;
|
||||
data->success = 1;
|
||||
|
||||
if (data->prev_error == ERROR_PASSWD_EXPIRED)
|
||||
eap_mschapv2_password_changed(sm, data);
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
static int
|
||||
eap_mschapv2_failure_txt(struct eap_sm *sm,
|
||||
struct eap_mschapv2_data *data, char *txt)
|
||||
{
|
||||
char *pos;
|
||||
//char *msg = "";
|
||||
int retry = 1;
|
||||
struct eap_peer_config *config = eap_get_config(sm);
|
||||
|
||||
pos = txt;
|
||||
|
||||
if (pos && os_strncmp(pos, "E=", 2) == 0) {
|
||||
pos += 2;
|
||||
data->prev_error = atoi(pos);
|
||||
pos = (char *)os_strchr(pos, ' ');
|
||||
if (pos)
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (pos && os_strncmp(pos, "R=", 2) == 0) {
|
||||
pos += 2;
|
||||
retry = atoi(pos);
|
||||
pos = (char *)os_strchr(pos, ' ');
|
||||
if (pos)
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (pos && os_strncmp(pos, "C=", 2) == 0) {
|
||||
int hex_len;
|
||||
pos += 2;
|
||||
hex_len = (char *)os_strchr(pos, ' ') - (char *)pos;
|
||||
if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
|
||||
if (hexstr2bin(pos, data->passwd_change_challenge,
|
||||
PASSWD_CHANGE_CHAL_LEN)) {
|
||||
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: invalid failure challenge\n");
|
||||
} else {
|
||||
data->passwd_change_challenge_valid = 1;
|
||||
}
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: required challenge field "
|
||||
"was not present in failure message\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (pos && os_strncmp(pos, "V=", 2) == 0) {
|
||||
pos += 2;
|
||||
data->passwd_change_version = atoi(pos);
|
||||
pos = (char *)os_strchr(pos, ' ');
|
||||
if (pos)
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (pos && os_strncmp(pos, "M=", 2) == 0) {
|
||||
pos += 2;
|
||||
//msg = pos;
|
||||
}
|
||||
#if 0
|
||||
wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error %d)",
|
||||
msg, retry == 1? "" : "not ", data->prev_error);
|
||||
#endif
|
||||
if (data->prev_error == ERROR_PASSWD_EXPIRED &&
|
||||
data->passwd_change_version == 3 && config) {
|
||||
if (config->new_password == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Password expired - "
|
||||
"password change reqired\n");
|
||||
//eap_sm_request_new_password(sm);
|
||||
}
|
||||
} else if (retry == 1 && config) {
|
||||
if (!config->mschapv2_retry)
|
||||
//eap_sm_request_identity(sm);
|
||||
//eap_sm_request_password(sm);
|
||||
config->mschapv2_retry = 1;
|
||||
} else if (config) {
|
||||
config->mschapv2_retry = 0;
|
||||
}
|
||||
|
||||
return retry == 1;
|
||||
}
|
||||
|
||||
static struct wpabuf *
|
||||
eap_mschapv2_change_password(
|
||||
struct eap_sm *sm, struct eap_mschapv2_data *data,
|
||||
struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id)
|
||||
{
|
||||
struct wpabuf *resp;
|
||||
int ms_len;
|
||||
const u8 *username, *password, *new_password;
|
||||
size_t username_len, password_len, new_password_len;
|
||||
struct eap_mschapv2_hdr *ms;
|
||||
struct ms_change_password *cp;
|
||||
u8 password_hash[16], password_hash_hash[16];
|
||||
int pwhash;
|
||||
|
||||
username = eap_get_config_identity(sm, &username_len);
|
||||
password = eap_get_config_password2(sm, &password_len, &pwhash);
|
||||
new_password = eap_get_config_new_password(sm, &new_password_len);
|
||||
if (username == NULL || password == NULL || new_password == NULL)
|
||||
return NULL;
|
||||
|
||||
username = mschapv2_remove_domain(username, &username_len);
|
||||
|
||||
ret->ignore = false;
|
||||
ret->methodState = METHOD_MAY_CONT;
|
||||
ret->decision = DECISION_COND_SUCC;
|
||||
ret->allowNotifications = TRUE;
|
||||
|
||||
ms_len = sizeof(*ms) + sizeof(*cp);
|
||||
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
|
||||
EAP_CODE_RESPONSE, id);
|
||||
if (resp == NULL)
|
||||
return NULL;
|
||||
ms = wpabuf_put(resp, sizeof(*ms));
|
||||
ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD;
|
||||
ms->mschapv2_id = req->mschapv2_id + 1;
|
||||
WPA_PUT_BE16(ms->ms_length, ms_len);
|
||||
cp = wpabuf_put(resp, sizeof(*cp));
|
||||
|
||||
if (pwhash) {
|
||||
if (encrypt_pw_block_with_password_hash(
|
||||
new_password, new_password_len,
|
||||
password, cp->encr_password))
|
||||
goto fail;
|
||||
} else {
|
||||
if (new_password_encrypted_with_old_nt_password_hash(
|
||||
new_password, new_password_len,
|
||||
password, password_len, cp->encr_password))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pwhash) {
|
||||
u8 new_password_hash[16];
|
||||
nt_password_hash(new_password, new_password_len,
|
||||
new_password_hash);
|
||||
nt_password_hash_encrypted_with_block(password,
|
||||
new_password_hash,
|
||||
cp->encr_hash);
|
||||
} else {
|
||||
old_nt_password_hash_encrypted_with_new_nt_password_hash(
|
||||
new_password, new_password_len,
|
||||
password, password_len, cp->encr_hash);
|
||||
}
|
||||
|
||||
if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
|
||||
goto fail;
|
||||
|
||||
os_memset(cp->reserved, 0, 8);
|
||||
|
||||
generate_nt_response(data->passwd_change_challenge, cp->peer_challenge,
|
||||
username, username_len, new_password,
|
||||
new_password_len, cp->nt_response);
|
||||
|
||||
generate_authenticator_response(new_password, new_password_len,
|
||||
cp->peer_challenge,
|
||||
data->passwd_change_challenge,
|
||||
username, username_len,
|
||||
cp->nt_response, data->auth_response);
|
||||
data->auth_response_valid = 1;
|
||||
|
||||
nt_password_hash(new_password, new_password_len, password_hash);
|
||||
hash_nt_password_hash(password_hash, password_hash_hash);
|
||||
get_master_key(password_hash_hash, cp->nt_response, data->master_key);
|
||||
data->master_key_valid = 1;
|
||||
|
||||
os_memset(cp->flags, 0, 2);
|
||||
|
||||
return resp;
|
||||
|
||||
fail:
|
||||
wpabuf_free(resp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct wpabuf *
|
||||
eap_mschapv2_failure(struct eap_sm *sm,
|
||||
struct eap_mschapv2_data *data,
|
||||
struct eap_method_ret *ret,
|
||||
const struct eap_mschapv2_hdr *req,
|
||||
size_t req_len, u8 id)
|
||||
{
|
||||
struct wpabuf *resp;
|
||||
const u8 *msdata = (const u8 *)(req + 1);
|
||||
char *buf;
|
||||
size_t len = req_len - sizeof(*req);
|
||||
int retry = 0;
|
||||
|
||||
buf = (char *)dup_binstr(msdata, len);
|
||||
if (buf) {
|
||||
retry = eap_mschapv2_failure_txt(sm, data, buf);
|
||||
os_free(buf);
|
||||
}
|
||||
|
||||
ret->ignore = false;
|
||||
ret->methodState = METHOD_DONE;
|
||||
ret->decision = DECISION_FAIL;
|
||||
ret->allowNotifications = false;
|
||||
|
||||
if (data->prev_error == ERROR_PASSWD_EXPIRED &&
|
||||
data->passwd_change_version == 3) {
|
||||
struct eap_peer_config *config = eap_get_config(sm);
|
||||
if (config && config->new_password)
|
||||
return eap_mschapv2_change_password(sm, data, ret,
|
||||
req, id);
|
||||
//if (config && config->pending_req_new_password)
|
||||
// return NULL;
|
||||
} else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
|
||||
EAP_CODE_RESPONSE, id);
|
||||
if (resp == NULL)
|
||||
return NULL;
|
||||
|
||||
wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE);
|
||||
return resp;
|
||||
}
|
||||
|
||||
static int
|
||||
eap_mschapv2_check_config(struct eap_sm *sm)
|
||||
{
|
||||
struct eap_peer_config *config = eap_get_config(sm);
|
||||
|
||||
if (config == NULL)
|
||||
return -1;
|
||||
|
||||
if (config->identity == NULL ||
|
||||
config->identity_len == 0) {
|
||||
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: idetity not configured\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (config->password == NULL ||
|
||||
config->password_len == 0) {
|
||||
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Password not configured\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
|
||||
const struct eap_mschapv2_hdr *ms)
|
||||
{
|
||||
size_t ms_len = WPA_GET_BE16(ms->ms_length);
|
||||
|
||||
if (ms_len == len)
|
||||
return 0;
|
||||
|
||||
if (sm->workaround) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Workaround, ignore Invalid"
|
||||
" header len=%lu ms_len=%lu\n",
|
||||
(unsigned long)len, (unsigned long)ms_len);
|
||||
return 0;
|
||||
}
|
||||
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Invalid header len=%lu ms_len=%lu\n",
|
||||
(unsigned long)len, (unsigned long)ms_len);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
|
||||
const struct wpabuf *reqData)
|
||||
{
|
||||
wpabuf_free(data->prev_challenge);
|
||||
data->prev_challenge = wpabuf_dup(reqData);
|
||||
}
|
||||
|
||||
static struct wpabuf *
|
||||
eap_mschapv2_process(struct eap_sm *sm, void *priv,
|
||||
struct eap_method_ret *ret,
|
||||
const struct wpabuf *reqData)
|
||||
{
|
||||
u8 id;
|
||||
size_t len;
|
||||
const u8 *pos;
|
||||
int using_prev_challenge = 0;
|
||||
const struct eap_mschapv2_hdr *ms;
|
||||
struct eap_mschapv2_data *data = priv;
|
||||
struct eap_peer_config *config = eap_get_config(sm);
|
||||
|
||||
if (eap_mschapv2_check_config(sm)) {
|
||||
ret->ignore = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (config->mschapv2_retry && data->prev_challenge &&
|
||||
data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
|
||||
reqData = data->prev_challenge;
|
||||
using_prev_challenge = 1;
|
||||
config->mschapv2_retry = 0;
|
||||
}
|
||||
|
||||
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
|
||||
reqData, &len);
|
||||
if (pos == NULL || len < sizeof(*ms) + 1) {
|
||||
ret->ignore = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ms = (const struct eap_mschapv2_hdr *)pos;
|
||||
if (eap_mschapv2_check_mslen(sm, len, ms)) {
|
||||
ret->ignore = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
id = eap_get_id(reqData);
|
||||
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d\n",
|
||||
id, ms->mschapv2_id);
|
||||
switch (ms->op_code) {
|
||||
case MSCHAPV2_OP_CHALLENGE:
|
||||
if (!using_prev_challenge)
|
||||
eap_mschapv2_copy_challenge(data, reqData);
|
||||
return eap_mschapv2_challenge(sm, data, ret, ms, len, id);
|
||||
case MSCHAPV2_OP_SUCCESS:
|
||||
return eap_mschapv2_success(sm, data, ret, ms, len, id);
|
||||
case MSCHAPV2_OP_FAILURE:
|
||||
return eap_mschapv2_failure(sm, data, ret, ms, len, id);
|
||||
default:
|
||||
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Unknow op code %d -ignored\n",
|
||||
ms->op_code);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
|
||||
{
|
||||
struct eap_mschapv2_data *data = priv;
|
||||
return data->success && data->master_key_valid;
|
||||
}
|
||||
|
||||
static u8 *
|
||||
eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
|
||||
{
|
||||
struct eap_mschapv2_data *data = priv;
|
||||
u8 *key;
|
||||
int key_len;
|
||||
|
||||
if (!data->master_key_valid || !data->success)
|
||||
return NULL;
|
||||
|
||||
key_len = 2 * MSCHAPV2_KEY_LEN;
|
||||
|
||||
key = os_malloc(key_len);
|
||||
|
||||
/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key,
|
||||
* peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
|
||||
get_asymetric_start_key(data->master_key, key,
|
||||
MSCHAPV2_KEY_LEN, 1, 0);
|
||||
get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
|
||||
MSCHAPV2_KEY_LEN, 0, 0);
|
||||
|
||||
*len = key_len;
|
||||
return key;
|
||||
}
|
||||
|
||||
int
|
||||
eap_peer_mschapv2_register(void)
|
||||
{
|
||||
struct eap_method *eap;
|
||||
int ret;
|
||||
|
||||
eap = eap_peer_method_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
|
||||
"MSCHAPV2");
|
||||
|
||||
if (eap == NULL)
|
||||
return -1;
|
||||
|
||||
eap->init = eap_mschapv2_init;
|
||||
eap->deinit = eap_mschapv2_deinit;
|
||||
eap->process = eap_mschapv2_process;
|
||||
eap->isKeyAvailable = eap_mschapv2_isKeyAvailable;
|
||||
eap->getKey = eap_mschapv2_getKey;
|
||||
|
||||
ret = eap_peer_method_register(eap);
|
||||
if (ret)
|
||||
eap_peer_method_free(eap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* EAP_MSCHAPv2 */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* EAP-PEAP common routines
|
||||
* Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifdef EAP_PEAP
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "crypto/sha1.h"
|
||||
#include "wpa2/eap_peer/eap_peap_common.h"
|
||||
|
||||
int
|
||||
peap_prfplus(int version, const u8 *key, size_t key_len,
|
||||
const char *label, const u8 *seed, size_t seed_len,
|
||||
u8 *buf, size_t buf_len)
|
||||
{
|
||||
unsigned char counter = 0;
|
||||
size_t pos, plen;
|
||||
u8 hash[SHA1_MAC_LEN];
|
||||
size_t label_len = os_strlen(label);
|
||||
u8 extra[2];
|
||||
const unsigned char *addr[5];
|
||||
size_t len[5];
|
||||
|
||||
addr[0] = hash;
|
||||
len[0] = 0;
|
||||
addr[1] = (unsigned char *) label;
|
||||
len[1] = label_len;
|
||||
addr[2] = seed;
|
||||
len[2] = seed_len;
|
||||
|
||||
if (version == 0) {
|
||||
/*
|
||||
* PRF+(K, S, LEN) = T1 | T2 | ... | Tn
|
||||
* T1 = HMAC-SHA1(K, S | 0x01 | 0x00 | 0x00)
|
||||
* T2 = HMAC-SHA1(K, T1 | S | 0x02 | 0x00 | 0x00)
|
||||
* ...
|
||||
* Tn = HMAC-SHA1(K, Tn-1 | S | n | 0x00 | 0x00)
|
||||
*/
|
||||
|
||||
extra[0] = 0;
|
||||
extra[1] = 0;
|
||||
|
||||
addr[3] = &counter;
|
||||
len[3] = 1;
|
||||
addr[4] = extra;
|
||||
len[4] = 2;
|
||||
} else {
|
||||
/*
|
||||
* PRF (K,S,LEN) = T1 | T2 | T3 | T4 | ... where:
|
||||
* T1 = HMAC-SHA1(K, S | LEN | 0x01)
|
||||
* T2 = HMAC-SHA1 (K, T1 | S | LEN | 0x02)
|
||||
* T3 = HMAC-SHA1 (K, T2 | S | LEN | 0x03)
|
||||
* T4 = HMAC-SHA1 (K, T3 | S | LEN | 0x04)
|
||||
* ...
|
||||
*/
|
||||
|
||||
extra[0] = buf_len & 0xff;
|
||||
|
||||
addr[3] = extra;
|
||||
len[3] = 1;
|
||||
addr[4] = &counter;
|
||||
len[4] = 1;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
while (pos < buf_len) {
|
||||
counter++;
|
||||
plen = buf_len - pos;
|
||||
if (hmac_sha1_vector(key, key_len, 5, addr, len, hash) < 0)
|
||||
return -1;
|
||||
if (plen >= SHA1_MAC_LEN) {
|
||||
os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);
|
||||
pos += SHA1_MAC_LEN;
|
||||
} else {
|
||||
os_memcpy(&buf[pos], hash, plen);
|
||||
break;
|
||||
}
|
||||
len[0] = SHA1_MAC_LEN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* EAP_PEAP */
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* EAP peer method: EAP-TLS (RFC 2716)
|
||||
* Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
#ifdef EAP_TLS
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "wpa2/tls/tls.h"
|
||||
#include "wpa2/eap_peer/eap_i.h"
|
||||
#include "wpa2/eap_peer/eap_defs.h"
|
||||
#include "wpa2/eap_peer/eap_tls_common.h"
|
||||
#include "wpa2/eap_peer/eap_config.h"
|
||||
#include "wpa2/eap_peer/eap_methods.h"
|
||||
|
||||
struct eap_tls_data {
|
||||
struct eap_ssl_data ssl;
|
||||
u8 *key_data;
|
||||
u8 *session_id;
|
||||
size_t id_len;
|
||||
void *ssl_ctx;
|
||||
u8 eap_type;
|
||||
};
|
||||
|
||||
|
||||
|
||||
static void eap_tls_deinit(struct eap_sm *sm, void *priv)
|
||||
{
|
||||
struct eap_tls_data *data = priv;
|
||||
if (data == NULL)
|
||||
return;
|
||||
eap_peer_tls_ssl_deinit(sm, &data->ssl);
|
||||
os_free(data->key_data);
|
||||
os_free(data->session_id);
|
||||
os_free(data);
|
||||
}
|
||||
|
||||
|
||||
static void * eap_tls_init(struct eap_sm *sm)
|
||||
{
|
||||
struct eap_tls_data *data;
|
||||
struct eap_peer_config *config = eap_get_config(sm);
|
||||
if (config == NULL ||
|
||||
config->private_key == 0) {
|
||||
wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = (struct eap_tls_data *)os_zalloc(sizeof(*data));
|
||||
if (data == NULL)
|
||||
return NULL;
|
||||
|
||||
data->ssl_ctx = sm->ssl_ctx;
|
||||
|
||||
if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TLS)) {
|
||||
wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
|
||||
eap_tls_deinit(sm, data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data->eap_type = EAP_TYPE_TLS;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static struct wpabuf * eap_tls_failure(struct eap_sm *sm,
|
||||
struct eap_tls_data *data,
|
||||
struct eap_method_ret *ret, int res,
|
||||
struct wpabuf *resp, u8 id)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed");
|
||||
|
||||
ret->methodState = METHOD_DONE;
|
||||
ret->decision = DECISION_FAIL;
|
||||
|
||||
if (res == -1) {
|
||||
struct eap_peer_config *config = eap_get_config(sm);
|
||||
if (config) {
|
||||
/*
|
||||
* The TLS handshake failed. So better forget the old
|
||||
* PIN. It may be wrong, we cannot be sure but trying
|
||||
* the wrong one again might block it on the card--so
|
||||
* better ask the user again.
|
||||
*/
|
||||
os_free(config->pin);
|
||||
config->pin = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (resp) {
|
||||
/*
|
||||
* This is likely an alert message, so send it instead of just
|
||||
* ACKing the error.
|
||||
*/
|
||||
return resp;
|
||||
}
|
||||
|
||||
return eap_peer_tls_build_ack(id, data->eap_type, 0);
|
||||
}
|
||||
|
||||
|
||||
static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data,
|
||||
struct eap_method_ret *ret)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
|
||||
|
||||
ret->methodState = METHOD_DONE;
|
||||
ret->decision = DECISION_UNCOND_SUCC;
|
||||
|
||||
os_free(data->key_data);
|
||||
data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
|
||||
"client EAP encryption",
|
||||
EAP_TLS_KEY_LEN +
|
||||
EAP_EMSK_LEN);
|
||||
if (data->key_data) {
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived key",
|
||||
data->key_data, EAP_TLS_KEY_LEN);
|
||||
wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived EMSK",
|
||||
data->key_data + EAP_TLS_KEY_LEN,
|
||||
EAP_EMSK_LEN);
|
||||
} else {
|
||||
wpa_printf(MSG_INFO, "EAP-TLS: Failed to derive key");
|
||||
}
|
||||
|
||||
os_free(data->session_id);
|
||||
data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl,
|
||||
EAP_TYPE_TLS,
|
||||
&data->id_len);
|
||||
if (data->session_id) {
|
||||
wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived Session-Id",
|
||||
data->session_id, data->id_len);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "EAP-TLS: Failed to derive Session-Id");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv,
|
||||
struct eap_method_ret *ret,
|
||||
const struct wpabuf *reqData)
|
||||
{
|
||||
size_t left;
|
||||
int res;
|
||||
struct wpabuf *resp;
|
||||
u8 flags, id;
|
||||
const u8 *pos;
|
||||
struct eap_tls_data *data = priv;
|
||||
|
||||
pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret,
|
||||
reqData, &left, &flags);
|
||||
if (pos == NULL)
|
||||
return NULL;
|
||||
id = eap_get_id(reqData);
|
||||
|
||||
if (flags & EAP_TLS_FLAGS_START) {
|
||||
wpa_printf(MSG_DEBUG, "EAP-TLS: Start");
|
||||
left = 0; /* make sure that this frame is empty, even though it
|
||||
* should always be, anyway */
|
||||
}
|
||||
|
||||
resp = NULL;
|
||||
res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0,
|
||||
id, pos, left, &resp);
|
||||
|
||||
if (res < 0) {
|
||||
return eap_tls_failure(sm, data, ret, res, resp, id);
|
||||
}
|
||||
|
||||
if (tls_connection_established(data->ssl_ctx, data->ssl.conn))
|
||||
eap_tls_success(sm, data, ret);
|
||||
|
||||
if (res == 1) {
|
||||
wpabuf_free(resp);
|
||||
return eap_peer_tls_build_ack(id, data->eap_type, 0);
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
static bool eap_tls_isKeyAvailable(struct eap_sm *sm, void *priv)
|
||||
{
|
||||
struct eap_tls_data *data = priv;
|
||||
|
||||
return data->key_data != NULL;
|
||||
}
|
||||
static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
|
||||
{
|
||||
struct eap_tls_data *data = priv;
|
||||
u8 *key;
|
||||
|
||||
if (data->key_data == NULL)
|
||||
return NULL;
|
||||
|
||||
key = os_malloc(EAP_TLS_KEY_LEN);
|
||||
if (key == NULL)
|
||||
return NULL;
|
||||
|
||||
*len = EAP_TLS_KEY_LEN;
|
||||
os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
int eap_peer_tls_register(void)
|
||||
{
|
||||
struct eap_method *eap;
|
||||
int ret;
|
||||
|
||||
eap = eap_peer_method_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLS,
|
||||
"TLS");
|
||||
|
||||
if (eap == NULL)
|
||||
return -1;
|
||||
|
||||
eap->init = eap_tls_init;
|
||||
eap->deinit = eap_tls_deinit;
|
||||
eap->process = eap_tls_process;
|
||||
eap->isKeyAvailable = eap_tls_isKeyAvailable;
|
||||
eap->getKey = eap_tls_getKey;
|
||||
|
||||
ret = eap_peer_method_register(eap);
|
||||
if (ret)
|
||||
eap_peer_method_free(eap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#endif /* EAP_TLS */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* MSCHAPV2
|
||||
*/
|
||||
|
||||
#ifdef EAP_MSCHAPv2
|
||||
|
||||
#include "wpa/includes.h"
|
||||
#include "wpa/common.h"
|
||||
#include "crypto/ms_funcs.h"
|
||||
#include "wpa2/eap_peer/mschapv2.h"
|
||||
|
||||
const u8 * mschapv2_remove_domain(const u8 *username, size_t *len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
/*
|
||||
* MSCHAPV2 does not include optional domain name in the
|
||||
* challenge-response calculation, so remove domain prefix
|
||||
* (if present)
|
||||
*/
|
||||
for (i = 0; i < *len; i++) {
|
||||
if (username[i] == '\\') {
|
||||
*len -= i + 1;
|
||||
return username + i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return username;
|
||||
}
|
||||
|
||||
int mschapv2_derive_response(const u8 *identity, size_t identity_len,
|
||||
const u8 *password, size_t password_len,
|
||||
int pwhash,
|
||||
const u8 *auth_challenge,
|
||||
const u8 *peer_challenge,
|
||||
u8 *nt_response, u8 *auth_response,
|
||||
u8 *master_key)
|
||||
{
|
||||
const u8 *username;
|
||||
size_t username_len;
|
||||
u8 password_hash[16], password_hash_hash[16];
|
||||
|
||||
username_len = identity_len;
|
||||
username = mschapv2_remove_domain(identity, &username_len);
|
||||
|
||||
if (pwhash) {
|
||||
if (generate_nt_response_pwhash(auth_challenge, peer_challenge,
|
||||
username, username_len,
|
||||
password, nt_response) ||
|
||||
generate_authenticator_response_pwhash(
|
||||
password, peer_challenge, auth_challenge,
|
||||
username, username_len, nt_response,
|
||||
auth_response))
|
||||
return -1;
|
||||
} else {
|
||||
if (generate_nt_response(auth_challenge, peer_challenge,
|
||||
username, username_len,
|
||||
password, password_len,
|
||||
nt_response) ||
|
||||
generate_authenticator_response(password, password_len,
|
||||
peer_challenge,
|
||||
auth_challenge,
|
||||
username, username_len,
|
||||
nt_response,
|
||||
auth_response))
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pwhash) {
|
||||
if (hash_nt_password_hash(password, password_hash_hash))
|
||||
return -1;
|
||||
} else {
|
||||
if (nt_password_hash(password, password_len, password_hash) ||
|
||||
hash_nt_password_hash(password_hash, password_hash_hash))
|
||||
return -1;
|
||||
}
|
||||
if (get_master_key(password_hash_hash, nt_response, master_key))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mschapv2_verify_auth_response(const u8 *auth_response,
|
||||
const u8 *buf, size_t buf_len)
|
||||
{
|
||||
u8 recv_response[MSCHAPV2_AUTH_RESPONSE_LEN];
|
||||
if (buf_len < 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN ||
|
||||
buf[0] != 'S' || buf[1] != '=' ||
|
||||
hexstr2bin((char *)(buf + 2), recv_response,
|
||||
MSCHAPV2_AUTH_RESPONSE_LEN) ||
|
||||
os_memcmp(auth_response, recv_response,
|
||||
MSCHAPV2_AUTH_RESPONSE_LEN) != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* EAP_MSCHAPv2 */
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* ASN.1 DER parsing
|
||||
* Copyright (c) 2006, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "wpa2/tls/asn1.h"
|
||||
|
||||
int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr)
|
||||
{
|
||||
const u8 *pos, *end;
|
||||
u8 tmp;
|
||||
|
||||
os_memset(hdr, 0, sizeof(*hdr));
|
||||
pos = buf;
|
||||
end = buf + len;
|
||||
|
||||
hdr->identifier = *pos++;
|
||||
hdr->class = hdr->identifier >> 6;
|
||||
hdr->constructed = !!(hdr->identifier & (1 << 5));
|
||||
|
||||
if ((hdr->identifier & 0x1f) == 0x1f) {
|
||||
hdr->tag = 0;
|
||||
do {
|
||||
if (pos >= end) {
|
||||
wpa_printf(MSG_DEBUG, "ASN.1: Identifier "
|
||||
"underflow");
|
||||
return -1;
|
||||
}
|
||||
tmp = *pos++;
|
||||
wpa_printf(MSG_DEBUG, "ASN.1: Extended tag data: "
|
||||
"0x%02x", tmp);
|
||||
hdr->tag = (hdr->tag << 7) | (tmp & 0x7f);
|
||||
} while (tmp & 0x80);
|
||||
} else
|
||||
hdr->tag = hdr->identifier & 0x1f;
|
||||
|
||||
tmp = *pos++;
|
||||
if (tmp & 0x80) {
|
||||
if (tmp == 0xff) {
|
||||
wpa_printf(MSG_DEBUG, "ASN.1: Reserved length "
|
||||
"value 0xff used");
|
||||
return -1;
|
||||
}
|
||||
tmp &= 0x7f; /* number of subsequent octets */
|
||||
hdr->length = 0;
|
||||
if (tmp > 4) {
|
||||
wpa_printf(MSG_DEBUG, "ASN.1: Too long length field");
|
||||
return -1;
|
||||
}
|
||||
while (tmp--) {
|
||||
if (pos >= end) {
|
||||
wpa_printf(MSG_DEBUG, "ASN.1: Length "
|
||||
"underflow");
|
||||
return -1;
|
||||
}
|
||||
hdr->length = (hdr->length << 8) | *pos++;
|
||||
}
|
||||
} else {
|
||||
/* Short form - length 0..127 in one octet */
|
||||
hdr->length = tmp;
|
||||
}
|
||||
|
||||
if (end < pos || hdr->length > (unsigned int) (end - pos)) {
|
||||
wpa_printf(MSG_DEBUG, "ASN.1: Contents underflow");
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdr->payload = pos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid)
|
||||
{
|
||||
const u8 *pos, *end;
|
||||
unsigned long val;
|
||||
u8 tmp;
|
||||
|
||||
os_memset(oid, 0, sizeof(*oid));
|
||||
|
||||
pos = buf;
|
||||
end = buf + len;
|
||||
|
||||
while (pos < end) {
|
||||
val = 0;
|
||||
|
||||
do {
|
||||
if (pos >= end)
|
||||
return -1;
|
||||
tmp = *pos++;
|
||||
val = (val << 7) | (tmp & 0x7f);
|
||||
} while (tmp & 0x80);
|
||||
|
||||
if (oid->len >= ASN1_MAX_OID_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "ASN.1: Too long OID value");
|
||||
return -1;
|
||||
}
|
||||
if (oid->len == 0) {
|
||||
/*
|
||||
* The first octet encodes the first two object
|
||||
* identifier components in (X*40) + Y formula.
|
||||
* X = 0..2.
|
||||
*/
|
||||
oid->oid[0] = val / 40;
|
||||
if (oid->oid[0] > 2)
|
||||
oid->oid[0] = 2;
|
||||
oid->oid[1] = val - oid->oid[0] * 40;
|
||||
oid->len = 2;
|
||||
} else
|
||||
oid->oid[oid->len++] = val;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
|
||||
const u8 **next)
|
||||
{
|
||||
struct asn1_hdr hdr;
|
||||
|
||||
if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0)
|
||||
return -1;
|
||||
|
||||
if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) {
|
||||
wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d "
|
||||
"tag 0x%x", hdr.class, hdr.tag);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*next = hdr.payload + hdr.length;
|
||||
|
||||
return asn1_parse_oid(hdr.payload, hdr.length, oid);
|
||||
}
|
||||
|
||||
|
||||
void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len)
|
||||
{
|
||||
char *pos = buf;
|
||||
size_t i;
|
||||
int ret;
|
||||
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
buf[0] = '\0';
|
||||
|
||||
for (i = 0; i < oid->len; i++) {
|
||||
//ret = os_snprintf(pos, buf + len - pos,
|
||||
ret = sprintf(pos,
|
||||
"%s%lu",
|
||||
i == 0 ? "" : ".", oid->oid[i]);
|
||||
if (ret < 0 || ret >= buf + len - pos)
|
||||
break;
|
||||
pos += ret;
|
||||
}
|
||||
buf[len - 1] = '\0';
|
||||
}
|
||||
|
||||
|
||||
static u8 rotate_bits(u8 octet)
|
||||
{
|
||||
int i;
|
||||
u8 res;
|
||||
|
||||
res = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
res <<= 1;
|
||||
if (octet & 1)
|
||||
res |= 1;
|
||||
octet >>= 1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len)
|
||||
{
|
||||
unsigned long val = 0;
|
||||
const u8 *pos = buf;
|
||||
|
||||
/* BER requires that unused bits are zero, so we can ignore the number
|
||||
* of unused bits */
|
||||
pos++;
|
||||
|
||||
if (len >= 2)
|
||||
val |= rotate_bits(*pos++);
|
||||
if (len >= 3)
|
||||
val |= ((unsigned long) rotate_bits(*pos++)) << 8;
|
||||
if (len >= 4)
|
||||
val |= ((unsigned long) rotate_bits(*pos++)) << 16;
|
||||
if (len >= 5)
|
||||
val |= ((unsigned long) rotate_bits(*pos++)) << 24;
|
||||
if (len >= 6)
|
||||
wpa_printf(MSG_DEBUG, "X509: %s - some bits ignored "
|
||||
"(BIT STRING length %lu)",
|
||||
__func__, (unsigned long) len);
|
||||
|
||||
return val;
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* Big number math
|
||||
* Copyright (c) 2006, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Alternatively, this software may be distributed under the terms of BSD
|
||||
* license.
|
||||
*
|
||||
* See README and COPYING for more details.
|
||||
*/
|
||||
|
||||
#include "crypto/includes.h"
|
||||
#include "crypto/common.h"
|
||||
#include "wpa/wpabuf.h"
|
||||
#include "wpa/wpa_debug.h"
|
||||
#include "wpa2/tls/bignum.h"
|
||||
|
||||
#define CONFIG_INTERNAL_LIBTOMMATH
|
||||
#ifdef CONFIG_INTERNAL_LIBTOMMATH
|
||||
#include "wpa2/tls/libtommath.h"
|
||||
#else /* CONFIG_INTERNAL_LIBTOMMATH */
|
||||
#include <tommath.h>
|
||||
#endif /* CONFIG_INTERNAL_LIBTOMMATH */
|
||||
|
||||
|
||||
/*
|
||||
* The current version is just a wrapper for LibTomMath library, so
|
||||
* struct bignum is just typecast to mp_int.
|
||||
*/
|
||||
|
||||
/**
|
||||
* bignum_init - Allocate memory for bignum
|
||||
* Returns: Pointer to allocated bignum or %NULL on failure
|
||||
*/
|
||||
struct bignum *
|
||||
bignum_init(void)
|
||||
{
|
||||
struct bignum *n = (struct bignum *)os_zalloc(sizeof(mp_int));
|
||||
if (n == NULL)
|
||||
return NULL;
|
||||
if (mp_init((mp_int *) n) != MP_OKAY) {
|
||||
os_free(n);
|
||||
n = NULL;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* bignum_deinit - Free bignum
|
||||
* @n: Bignum from bignum_init()
|
||||
*/
|
||||
void
|
||||
bignum_deinit(struct bignum *n)
|
||||
{
|
||||
if (n) {
|
||||
mp_clear((mp_int *) n);
|
||||
os_free(n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* bignum_get_unsigned_bin - Get length of bignum as an unsigned binary buffer
|
||||
* @n: Bignum from bignum_init()
|
||||
* Returns: Length of n if written to a binary buffer
|
||||
*/
|
||||
size_t
|
||||
bignum_get_unsigned_bin_len(struct bignum *n)
|
||||
{
|
||||
return mp_unsigned_bin_size((mp_int *) n);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* bignum_get_unsigned_bin - Set binary buffer to unsigned bignum
|
||||
* @n: Bignum from bignum_init()
|
||||
* @buf: Buffer for the binary number
|
||||
* @len: Length of the buffer, can be %NULL if buffer is known to be long
|
||||
* enough. Set to used buffer length on success if not %NULL.
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int
|
||||
bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len)
|
||||
{
|
||||
size_t need = mp_unsigned_bin_size((mp_int *) n);
|
||||
if (len && need > *len) {
|
||||
*len = need;
|
||||
return -1;
|
||||
}
|
||||
if (mp_to_unsigned_bin((mp_int *) n, buf) != MP_OKAY) {
|
||||
wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
|
||||
return -1;
|
||||
}
|
||||
if (len)
|
||||
*len = need;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* bignum_set_unsigned_bin - Set bignum based on unsigned binary buffer
|
||||
* @n: Bignum from bignum_init(); to be set to the given value
|
||||
* @buf: Buffer with unsigned binary value
|
||||
* @len: Length of buf in octets
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int
|
||||
bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len)
|
||||
{
|
||||
if (mp_read_unsigned_bin((mp_int *) n, (u8 *) buf, len) != MP_OKAY) {
|
||||
wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* bignum_cmp - Signed comparison
|
||||
* @a: Bignum from bignum_init()
|
||||
* @b: Bignum from bignum_init()
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int
|
||||
bignum_cmp(const struct bignum *a, const struct bignum *b)
|
||||
{
|
||||
return mp_cmp((mp_int *) a, (mp_int *) b);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* bignum_cmd_d - Compare bignum to standard integer
|
||||
* @a: Bignum from bignum_init()
|
||||
* @b: Small integer
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int
|
||||
bignum_cmp_d(const struct bignum *a, unsigned long b)
|
||||
{
|
||||
return mp_cmp_d((mp_int *) a, b);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* bignum_add - c = a + b
|
||||
* @a: Bignum from bignum_init()
|
||||
* @b: Bignum from bignum_init()
|
||||
* @c: Bignum from bignum_init(); used to store the result of a + b
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int
|
||||
bignum_add(const struct bignum *a, const struct bignum *b,
|
||||
struct bignum *c)
|
||||
{
|
||||
if (mp_add((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
|
||||
wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* bignum_sub - c = a - b
|
||||
* @a: Bignum from bignum_init()
|
||||
* @b: Bignum from bignum_init()
|
||||
* @c: Bignum from bignum_init(); used to store the result of a - b
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int
|
||||
bignum_sub(const struct bignum *a, const struct bignum *b,
|
||||
struct bignum *c)
|
||||
{
|
||||
if (mp_sub((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
|
||||
wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* bignum_mul - c = a * b
|
||||
* @a: Bignum from bignum_init()
|
||||
* @b: Bignum from bignum_init()
|
||||
* @c: Bignum from bignum_init(); used to store the result of a * b
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int
|
||||
bignum_mul(const struct bignum *a, const struct bignum *b,
|
||||
struct bignum *c)
|
||||
{
|
||||
if (mp_mul((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
|
||||
wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* bignum_mulmod - d = a * b (mod c)
|
||||
* @a: Bignum from bignum_init()
|
||||
* @b: Bignum from bignum_init()
|
||||
* @c: Bignum from bignum_init(); modulus
|
||||
* @d: Bignum from bignum_init(); used to store the result of a * b (mod c)
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int
|
||||
bignum_mulmod(const struct bignum *a, const struct bignum *b,
|
||||
const struct bignum *c, struct bignum *d)
|
||||
{
|
||||
if (mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d)
|
||||
!= MP_OKAY) {
|
||||
wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* bignum_exptmod - Modular exponentiation: d = a^b (mod c)
|
||||
* @a: Bignum from bignum_init(); base
|
||||
* @b: Bignum from bignum_init(); exponent
|
||||
* @c: Bignum from bignum_init(); modulus
|
||||
* @d: Bignum from bignum_init(); used to store the result of a^b (mod c)
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int
|
||||
bignum_exptmod(const struct bignum *a, const struct bignum *b,
|
||||
const struct bignum *c, struct bignum *d)
|
||||
{
|
||||
if (mp_exptmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d)
|
||||
!= MP_OKAY) {
|
||||
wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* PKCS #1 (RSA Encryption)
|
||||
* Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "wpa2/tls/rsa.h"
|
||||
#include "wpa2/tls/pkcs1.h"
|
||||
|
||||
|
||||
static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen,
|
||||
const u8 *in, size_t inlen,
|
||||
u8 *out, size_t *outlen)
|
||||
{
|
||||
size_t ps_len;
|
||||
u8 *pos;
|
||||
|
||||
/*
|
||||
* PKCS #1 v1.5, 8.1:
|
||||
*
|
||||
* EB = 00 || BT || PS || 00 || D
|
||||
* BT = 00 or 01 for private-key operation; 02 for public-key operation
|
||||
* PS = k-3-||D||; at least eight octets
|
||||
* (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero)
|
||||
* k = length of modulus in octets (modlen)
|
||||
*/
|
||||
|
||||
if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer "
|
||||
"lengths (modlen=%lu outlen=%lu inlen=%lu)",
|
||||
__func__, (unsigned long) modlen,
|
||||
(unsigned long) *outlen,
|
||||
(unsigned long) inlen);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pos = out;
|
||||
*pos++ = 0x00;
|
||||
*pos++ = block_type; /* BT */
|
||||
ps_len = modlen - inlen - 3;
|
||||
switch (block_type) {
|
||||
case 0:
|
||||
os_memset(pos, 0x00, ps_len);
|
||||
pos += ps_len;
|
||||
break;
|
||||
case 1:
|
||||
os_memset(pos, 0xff, ps_len);
|
||||
pos += ps_len;
|
||||
break;
|
||||
case 2:
|
||||
if (os_get_random(pos, ps_len) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get "
|
||||
"random data for PS", __func__);
|
||||
return -1;
|
||||
}
|
||||
while (ps_len--) {
|
||||
if (*pos == 0x00)
|
||||
*pos = 0x01;
|
||||
pos++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type "
|
||||
"%d", __func__, block_type);
|
||||
return -1;
|
||||
}
|
||||
*pos++ = 0x00;
|
||||
os_memcpy(pos, in, inlen); /* D */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key,
|
||||
int use_private, const u8 *in, size_t inlen,
|
||||
u8 *out, size_t *outlen)
|
||||
{
|
||||
size_t modlen;
|
||||
|
||||
modlen = crypto_rsa_get_modulus_len(key);
|
||||
|
||||
if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen,
|
||||
out, outlen) < 0)
|
||||
return -1;
|
||||
|
||||
return crypto_rsa_exptmod(out, modlen, out, outlen, key, use_private);
|
||||
}
|
||||
|
||||
|
||||
int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key,
|
||||
const u8 *in, size_t inlen,
|
||||
u8 *out, size_t *outlen)
|
||||
{
|
||||
int res;
|
||||
u8 *pos, *end;
|
||||
|
||||
res = crypto_rsa_exptmod(in, inlen, out, outlen, key, 1);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
if (*outlen < 2 || out[0] != 0 || out[1] != 2)
|
||||
return -1;
|
||||
|
||||
/* Skip PS (pseudorandom non-zero octets) */
|
||||
pos = out + 2;
|
||||
end = out + *outlen;
|
||||
while (*pos && pos < end)
|
||||
pos++;
|
||||
if (pos == end)
|
||||
return -1;
|
||||
pos++;
|
||||
|
||||
*outlen -= pos - out;
|
||||
|
||||
/* Strip PKCS #1 header */
|
||||
os_memmove(out, pos, *outlen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int pkcs1_decrypt_public_key(struct crypto_rsa_key *key,
|
||||
const u8 *crypt, size_t crypt_len,
|
||||
u8 *plain, size_t *plain_len)
|
||||
{
|
||||
size_t len;
|
||||
u8 *pos;
|
||||
|
||||
len = *plain_len;
|
||||
if (crypto_rsa_exptmod(crypt, crypt_len, plain, &len, key, 0) < 0)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* PKCS #1 v1.5, 8.1:
|
||||
*
|
||||
* EB = 00 || BT || PS || 00 || D
|
||||
* BT = 00 or 01
|
||||
* PS = k-3-||D|| times (00 if BT=00) or (FF if BT=01)
|
||||
* k = length of modulus in octets
|
||||
*/
|
||||
|
||||
if (len < 3 + 8 + 16 /* min hash len */ ||
|
||||
plain[0] != 0x00 || (plain[1] != 0x00 && plain[1] != 0x01)) {
|
||||
wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
|
||||
"structure");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pos = plain + 3;
|
||||
if (plain[1] == 0x00) {
|
||||
/* BT = 00 */
|
||||
if (plain[2] != 0x00) {
|
||||
wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
|
||||
"PS (BT=00)");
|
||||
return -1;
|
||||
}
|
||||
while (pos + 1 < plain + len && *pos == 0x00 && pos[1] == 0x00)
|
||||
pos++;
|
||||
} else {
|
||||
/* BT = 01 */
|
||||
if (plain[2] != 0xff) {
|
||||
wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
|
||||
"PS (BT=01)");
|
||||
return -1;
|
||||
}
|
||||
while (pos < plain + len && *pos == 0xff)
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (pos - plain - 2 < 8) {
|
||||
/* PKCS #1 v1.5, 8.1: At least eight octets long PS */
|
||||
wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature "
|
||||
"padding");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) {
|
||||
wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
|
||||
"structure (2)");
|
||||
return -1;
|
||||
}
|
||||
pos++;
|
||||
len -= pos - plain;
|
||||
|
||||
/* Strip PKCS #1 header */
|
||||
os_memmove(plain, pos, len);
|
||||
*plain_len = len;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* PKCS #5 (Password-based Encryption)
|
||||
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "crypto/md5.h"
|
||||
#include "wpa2/tls/asn1.h"
|
||||
#include "wpa2/tls/pkcs5.h"
|
||||
|
||||
#include "wpa2/eap_peer/eap_i.h"
|
||||
|
||||
struct pkcs5_params {
|
||||
enum pkcs5_alg {
|
||||
PKCS5_ALG_UNKNOWN,
|
||||
PKCS5_ALG_MD5_DES_CBC
|
||||
} alg;
|
||||
u8 salt[8];
|
||||
size_t salt_len;
|
||||
unsigned int iter_count;
|
||||
};
|
||||
|
||||
|
||||
static enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid)
|
||||
{
|
||||
if (oid->len == 7 &&
|
||||
oid->oid[0] == 1 /* iso */ &&
|
||||
oid->oid[1] == 2 /* member-body */ &&
|
||||
oid->oid[2] == 840 /* us */ &&
|
||||
oid->oid[3] == 113549 /* rsadsi */ &&
|
||||
oid->oid[4] == 1 /* pkcs */ &&
|
||||
oid->oid[5] == 5 /* pkcs-5 */ &&
|
||||
oid->oid[6] == 3 /* pbeWithMD5AndDES-CBC */)
|
||||
return PKCS5_ALG_MD5_DES_CBC;
|
||||
|
||||
return PKCS5_ALG_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len,
|
||||
struct pkcs5_params *params)
|
||||
{
|
||||
struct asn1_hdr hdr;
|
||||
const u8 *enc_alg_end, *pos, *end;
|
||||
struct asn1_oid oid;
|
||||
char obuf[80];
|
||||
|
||||
/* AlgorithmIdentifier */
|
||||
|
||||
enc_alg_end = enc_alg + enc_alg_len;
|
||||
|
||||
os_memset(params, 0, sizeof(*params));
|
||||
|
||||
if (asn1_get_oid(enc_alg, enc_alg_end - enc_alg, &oid, &pos)) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #5: Failed to parse OID "
|
||||
"(algorithm)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
asn1_oid_to_str(&oid, obuf, sizeof(obuf));
|
||||
wpa_printf(MSG_DEBUG, "PKCS #5: encryption algorithm %s", obuf);
|
||||
params->alg = pkcs5_get_alg(&oid);
|
||||
if (params->alg == PKCS5_ALG_UNKNOWN) {
|
||||
wpa_printf(MSG_INFO, "PKCS #5: unsupported encryption "
|
||||
"algorithm %s", obuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* PKCS#5, Section 8
|
||||
* PBEParameter ::= SEQUENCE {
|
||||
* salt OCTET STRING SIZE(8),
|
||||
* iterationCount INTEGER }
|
||||
*/
|
||||
|
||||
if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 ||
|
||||
hdr.class != ASN1_CLASS_UNIVERSAL ||
|
||||
hdr.tag != ASN1_TAG_SEQUENCE) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #5: Expected SEQUENCE "
|
||||
"(PBEParameter) - found class %d tag 0x%x",
|
||||
hdr.class, hdr.tag);
|
||||
return -1;
|
||||
}
|
||||
pos = hdr.payload;
|
||||
end = hdr.payload + hdr.length;
|
||||
|
||||
/* salt OCTET STRING SIZE(8) */
|
||||
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
|
||||
hdr.class != ASN1_CLASS_UNIVERSAL ||
|
||||
hdr.tag != ASN1_TAG_OCTETSTRING ||
|
||||
hdr.length != 8) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #5: Expected OCTETSTRING SIZE(8) "
|
||||
"(salt) - found class %d tag 0x%x size %d",
|
||||
hdr.class, hdr.tag, hdr.length);
|
||||
return -1;
|
||||
}
|
||||
pos = hdr.payload + hdr.length;
|
||||
os_memcpy(params->salt, hdr.payload, hdr.length);
|
||||
params->salt_len = hdr.length;
|
||||
wpa_hexdump(MSG_DEBUG, "PKCS #5: salt",
|
||||
params->salt, params->salt_len);
|
||||
|
||||
/* iterationCount INTEGER */
|
||||
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
|
||||
hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #5: Expected INTEGER - found "
|
||||
"class %d tag 0x%x", hdr.class, hdr.tag);
|
||||
return -1;
|
||||
}
|
||||
if (hdr.length == 1)
|
||||
params->iter_count = *hdr.payload;
|
||||
else if (hdr.length == 2)
|
||||
params->iter_count = WPA_GET_BE16(hdr.payload);
|
||||
else if (hdr.length == 4)
|
||||
params->iter_count = WPA_GET_BE32(hdr.payload);
|
||||
else {
|
||||
wpa_hexdump(MSG_DEBUG, "PKCS #5: Unsupported INTEGER value "
|
||||
" (iterationCount)",
|
||||
hdr.payload, hdr.length);
|
||||
return -1;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "PKCS #5: iterationCount=0x%x",
|
||||
params->iter_count);
|
||||
if (params->iter_count == 0 || params->iter_count > 0xffff) {
|
||||
wpa_printf(MSG_INFO, "PKCS #5: Unsupported "
|
||||
"iterationCount=0x%x", params->iter_count);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params,
|
||||
const char *passwd)
|
||||
{
|
||||
unsigned int i;
|
||||
u8 hash[MD5_MAC_LEN];
|
||||
const u8 *addr[2];
|
||||
size_t len[2];
|
||||
|
||||
if (params->alg != PKCS5_ALG_MD5_DES_CBC) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
addr[0] = (const u8 *) passwd;
|
||||
len[0] = os_strlen(passwd);
|
||||
addr[1] = params->salt;
|
||||
len[1] = params->salt_len;
|
||||
if (md5_vector(2, addr, len, hash) < 0)
|
||||
return NULL;
|
||||
addr[0] = hash;
|
||||
len[0] = MD5_MAC_LEN;
|
||||
for (i = 1; i < params->iter_count; i++) {
|
||||
if (md5_vector(1, addr, len, hash) < 0)
|
||||
return NULL;
|
||||
}
|
||||
/* TODO: DES key parity bits(?) */
|
||||
wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES key", hash, 8);
|
||||
wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES IV", hash + 8, 8);
|
||||
|
||||
if (wpa2_crypto_funcs.crypto_cipher_init) {
|
||||
return wpa2_crypto_funcs.crypto_cipher_init(CRYPTO_CIPHER_ALG_DES, hash + 8, hash, 8);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_cipher_init function! \r\n", __FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len,
|
||||
const u8 *enc_data, size_t enc_data_len,
|
||||
const char *passwd, size_t *data_len)
|
||||
{
|
||||
struct crypto_cipher *ctx = NULL;
|
||||
u8 *eb, pad;
|
||||
struct pkcs5_params params;
|
||||
unsigned int i;
|
||||
|
||||
if (pkcs5_get_params(enc_alg, enc_alg_len, ¶ms) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #5: Unsupported parameters");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx = pkcs5_crypto_init(¶ms, passwd);
|
||||
|
||||
if (ctx == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #5: Failed to initialize crypto");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* PKCS #5, Section 7 - Decryption process */
|
||||
if (enc_data_len < 16 || enc_data_len % 8) {
|
||||
wpa_printf(MSG_INFO, "PKCS #5: invalid length of ciphertext "
|
||||
"%d", (int) enc_data_len);
|
||||
if (wpa2_crypto_funcs.crypto_cipher_deinit) {
|
||||
wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
eb = os_malloc(enc_data_len);
|
||||
if (eb == NULL) {
|
||||
if (wpa2_crypto_funcs.crypto_cipher_deinit) {
|
||||
wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (wpa2_crypto_funcs.crypto_cipher_decrypt) {
|
||||
if ((int)wpa2_crypto_funcs.crypto_cipher_decrypt(ctx, enc_data, eb, enc_data_len) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #5: Failed to decrypt EB");
|
||||
wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
|
||||
os_free(eb);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "Fail to register crypto cipher decrypt function.\r\n");
|
||||
wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
|
||||
os_free(eb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (wpa2_crypto_funcs.crypto_cipher_deinit) {
|
||||
wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pad = eb[enc_data_len - 1];
|
||||
if (pad > 8) {
|
||||
wpa_printf(MSG_INFO, "PKCS #5: Invalid PS octet 0x%x", pad);
|
||||
os_free(eb);
|
||||
return NULL;
|
||||
}
|
||||
for (i = enc_data_len - pad; i < enc_data_len; i++) {
|
||||
if (eb[i] != pad) {
|
||||
wpa_hexdump(MSG_INFO, "PKCS #5: Invalid PS",
|
||||
eb + enc_data_len - pad, pad);
|
||||
os_free(eb);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
wpa_hexdump_key(MSG_MSGDUMP, "PKCS #5: message M (encrypted key)",
|
||||
eb, enc_data_len - pad);
|
||||
|
||||
*data_len = enc_data_len - pad;
|
||||
return eb;
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* PKCS #8 (Private-key information syntax)
|
||||
* Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "wpa2/tls/asn1.h"
|
||||
#include "wpa2/tls/bignum.h"
|
||||
#include "wpa2/tls/rsa.h"
|
||||
#include "wpa2/tls/pkcs5.h"
|
||||
#include "wpa2/tls/pkcs8.h"
|
||||
|
||||
struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len)
|
||||
{
|
||||
struct asn1_hdr hdr;
|
||||
const u8 *pos, *end;
|
||||
struct bignum *zero;
|
||||
struct asn1_oid oid;
|
||||
char obuf[80];
|
||||
|
||||
/* PKCS #8, Chapter 6 */
|
||||
|
||||
/* PrivateKeyInfo ::= SEQUENCE */
|
||||
if (asn1_get_next(buf, len, &hdr) < 0 ||
|
||||
hdr.class != ASN1_CLASS_UNIVERSAL ||
|
||||
hdr.tag != ASN1_TAG_SEQUENCE) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 "
|
||||
"header (SEQUENCE); assume PKCS #8 not used");
|
||||
return NULL;
|
||||
}
|
||||
pos = hdr.payload;
|
||||
end = pos + hdr.length;
|
||||
|
||||
/* version Version (Version ::= INTEGER) */
|
||||
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
|
||||
hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #8: Expected INTEGER - found "
|
||||
"class %d tag 0x%x; assume PKCS #8 not used",
|
||||
hdr.class, hdr.tag);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
zero = bignum_init();
|
||||
if (zero == NULL)
|
||||
return NULL;
|
||||
|
||||
if (bignum_set_unsigned_bin(zero, hdr.payload, hdr.length) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse INTEGER");
|
||||
bignum_deinit(zero);
|
||||
return NULL;
|
||||
}
|
||||
pos = hdr.payload + hdr.length;
|
||||
|
||||
if (bignum_cmp_d(zero, 0) != 0) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #8: Expected zero INTEGER in the "
|
||||
"beginning of private key; not found; assume "
|
||||
"PKCS #8 not used");
|
||||
bignum_deinit(zero);
|
||||
return NULL;
|
||||
}
|
||||
bignum_deinit(zero);
|
||||
|
||||
/* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier
|
||||
* (PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier) */
|
||||
if (asn1_get_next(pos, len, &hdr) < 0 ||
|
||||
hdr.class != ASN1_CLASS_UNIVERSAL ||
|
||||
hdr.tag != ASN1_TAG_SEQUENCE) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE "
|
||||
"(AlgorithmIdentifier) - found class %d tag 0x%x; "
|
||||
"assume PKCS #8 not used",
|
||||
hdr.class, hdr.tag);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (asn1_get_oid(hdr.payload, hdr.length, &oid, &pos)) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse OID "
|
||||
"(algorithm); assume PKCS #8 not used");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
asn1_oid_to_str(&oid, obuf, sizeof(obuf));
|
||||
wpa_printf(MSG_DEBUG, "PKCS #8: algorithm=%s", obuf);
|
||||
|
||||
if (oid.len != 7 ||
|
||||
oid.oid[0] != 1 /* iso */ ||
|
||||
oid.oid[1] != 2 /* member-body */ ||
|
||||
oid.oid[2] != 840 /* us */ ||
|
||||
oid.oid[3] != 113549 /* rsadsi */ ||
|
||||
oid.oid[4] != 1 /* pkcs */ ||
|
||||
oid.oid[5] != 1 /* pkcs-1 */ ||
|
||||
oid.oid[6] != 1 /* rsaEncryption */) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #8: Unsupported private key "
|
||||
"algorithm %s", obuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pos = hdr.payload + hdr.length;
|
||||
|
||||
/* privateKey PrivateKey (PrivateKey ::= OCTET STRING) */
|
||||
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
|
||||
hdr.class != ASN1_CLASS_UNIVERSAL ||
|
||||
hdr.tag != ASN1_TAG_OCTETSTRING) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING "
|
||||
"(privateKey) - found class %d tag 0x%x",
|
||||
hdr.class, hdr.tag);
|
||||
return NULL;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "PKCS #8: Try to parse RSAPrivateKey");
|
||||
|
||||
return (struct crypto_private_key *)
|
||||
crypto_rsa_import_private_key(hdr.payload, hdr.length);
|
||||
}
|
||||
|
||||
|
||||
struct crypto_private_key *
|
||||
pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd)
|
||||
{
|
||||
struct asn1_hdr hdr;
|
||||
const u8 *pos, *end, *enc_alg;
|
||||
size_t enc_alg_len;
|
||||
u8 *data;
|
||||
size_t data_len;
|
||||
|
||||
if (passwd == NULL)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* PKCS #8, Chapter 7
|
||||
* EncryptedPrivateKeyInfo ::= SEQUENCE {
|
||||
* encryptionAlgorithm EncryptionAlgorithmIdentifier,
|
||||
* encryptedData EncryptedData }
|
||||
* EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
|
||||
* EncryptedData ::= OCTET STRING
|
||||
*/
|
||||
|
||||
if (asn1_get_next(buf, len, &hdr) < 0 ||
|
||||
hdr.class != ASN1_CLASS_UNIVERSAL ||
|
||||
hdr.tag != ASN1_TAG_SEQUENCE) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 "
|
||||
"header (SEQUENCE); assume encrypted PKCS #8 not "
|
||||
"used");
|
||||
return NULL;
|
||||
}
|
||||
pos = hdr.payload;
|
||||
end = pos + hdr.length;
|
||||
|
||||
/* encryptionAlgorithm EncryptionAlgorithmIdentifier */
|
||||
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
|
||||
hdr.class != ASN1_CLASS_UNIVERSAL ||
|
||||
hdr.tag != ASN1_TAG_SEQUENCE) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE "
|
||||
"(AlgorithmIdentifier) - found class %d tag 0x%x; "
|
||||
"assume encrypted PKCS #8 not used",
|
||||
hdr.class, hdr.tag);
|
||||
return NULL;
|
||||
}
|
||||
enc_alg = hdr.payload;
|
||||
enc_alg_len = hdr.length;
|
||||
pos = hdr.payload + hdr.length;
|
||||
|
||||
/* encryptedData EncryptedData */
|
||||
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
|
||||
hdr.class != ASN1_CLASS_UNIVERSAL ||
|
||||
hdr.tag != ASN1_TAG_OCTETSTRING) {
|
||||
wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING "
|
||||
"(encryptedData) - found class %d tag 0x%x",
|
||||
hdr.class, hdr.tag);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length,
|
||||
passwd, &data_len);
|
||||
if (data) {
|
||||
struct crypto_private_key *key;
|
||||
key = pkcs8_key_import(data, data_len);
|
||||
os_free(data);
|
||||
return key;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,353 @@
|
|||
/*
|
||||
* RSA
|
||||
* Copyright (c) 2006, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "wpa2/tls/asn1.h"
|
||||
#include "wpa2/tls/bignum.h"
|
||||
#include "wpa2/tls/rsa.h"
|
||||
#include "soc/dport_reg.h"
|
||||
|
||||
struct crypto_rsa_key {
|
||||
int private_key; /* whether private key is set */
|
||||
struct bignum *n; /* modulus (p * q) */
|
||||
struct bignum *e; /* public exponent */
|
||||
/* The following parameters are available only if private_key is set */
|
||||
struct bignum *d; /* private exponent */
|
||||
struct bignum *p; /* prime p (factor of n) */
|
||||
struct bignum *q; /* prime q (factor of n) */
|
||||
struct bignum *dmp1; /* d mod (p - 1); CRT exponent */
|
||||
struct bignum *dmq1; /* d mod (q - 1); CRT exponent */
|
||||
struct bignum *iqmp; /* 1 / q mod p; CRT coefficient */
|
||||
};
|
||||
|
||||
|
||||
static const u8 * crypto_rsa_parse_integer(const u8 *pos, const u8 *end,
|
||||
struct bignum *num)
|
||||
{
|
||||
struct asn1_hdr hdr;
|
||||
|
||||
if (pos == NULL)
|
||||
return NULL;
|
||||
|
||||
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
|
||||
hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
|
||||
wpa_printf(MSG_DEBUG, "RSA: Expected INTEGER - found class %d "
|
||||
"tag 0x%x", hdr.class, hdr.tag);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (bignum_set_unsigned_bin(num, hdr.payload, hdr.length) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "RSA: Failed to parse INTEGER");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return hdr.payload + hdr.length;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* crypto_rsa_import_public_key - Import an RSA public key
|
||||
* @buf: Key buffer (DER encoded RSA public key)
|
||||
* @len: Key buffer length in bytes
|
||||
* Returns: Pointer to the public key or %NULL on failure
|
||||
*/
|
||||
struct crypto_rsa_key *
|
||||
crypto_rsa_import_public_key(const u8 *buf, size_t len)
|
||||
{
|
||||
struct crypto_rsa_key *key;
|
||||
struct asn1_hdr hdr;
|
||||
const u8 *pos, *end;
|
||||
|
||||
key = (struct crypto_rsa_key *)os_zalloc(sizeof(*key));
|
||||
if (key == NULL)
|
||||
return NULL;
|
||||
|
||||
key->n = bignum_init();
|
||||
key->e = bignum_init();
|
||||
if (key->n == NULL || key->e == NULL) {
|
||||
crypto_rsa_free(key);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* PKCS #1, 7.1:
|
||||
* RSAPublicKey ::= SEQUENCE {
|
||||
* modulus INTEGER, -- n
|
||||
* publicExponent INTEGER -- e
|
||||
* }
|
||||
*/
|
||||
|
||||
if (asn1_get_next(buf, len, &hdr) < 0 ||
|
||||
hdr.class != ASN1_CLASS_UNIVERSAL ||
|
||||
hdr.tag != ASN1_TAG_SEQUENCE) {
|
||||
wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE "
|
||||
"(public key) - found class %d tag 0x%x",
|
||||
hdr.class, hdr.tag);
|
||||
goto error;
|
||||
}
|
||||
pos = hdr.payload;
|
||||
end = pos + hdr.length;
|
||||
|
||||
pos = crypto_rsa_parse_integer(pos, end, key->n);
|
||||
pos = crypto_rsa_parse_integer(pos, end, key->e);
|
||||
|
||||
if (pos == NULL)
|
||||
goto error;
|
||||
|
||||
if (pos != end) {
|
||||
wpa_hexdump(MSG_DEBUG,
|
||||
"RSA: Extra data in public key SEQUENCE",
|
||||
pos, end - pos);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return key;
|
||||
|
||||
error:
|
||||
crypto_rsa_free(key);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* crypto_rsa_import_private_key - Import an RSA private key
|
||||
* @buf: Key buffer (DER encoded RSA private key)
|
||||
* @len: Key buffer length in bytes
|
||||
* Returns: Pointer to the private key or %NULL on failure
|
||||
*/
|
||||
struct crypto_rsa_key *
|
||||
crypto_rsa_import_private_key(const u8 *buf, size_t len)
|
||||
{
|
||||
struct crypto_rsa_key *key;
|
||||
struct bignum *zero;
|
||||
struct asn1_hdr hdr;
|
||||
const u8 *pos, *end;
|
||||
|
||||
key = (struct crypto_rsa_key *)os_zalloc(sizeof(*key));
|
||||
if (key == NULL)
|
||||
return NULL;
|
||||
|
||||
key->private_key = 1;
|
||||
|
||||
key->n = bignum_init();
|
||||
key->e = bignum_init();
|
||||
key->d = bignum_init();
|
||||
key->p = bignum_init();
|
||||
key->q = bignum_init();
|
||||
key->dmp1 = bignum_init();
|
||||
key->dmq1 = bignum_init();
|
||||
key->iqmp = bignum_init();
|
||||
|
||||
if (key->n == NULL || key->e == NULL || key->d == NULL ||
|
||||
key->p == NULL || key->q == NULL || key->dmp1 == NULL ||
|
||||
key->dmq1 == NULL || key->iqmp == NULL) {
|
||||
crypto_rsa_free(key);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* PKCS #1, 7.2:
|
||||
* RSAPrivateKey ::= SEQUENCE {
|
||||
* version Version,
|
||||
* modulus INTEGER, -- n
|
||||
* publicExponent INTEGER, -- e
|
||||
* privateExponent INTEGER, -- d
|
||||
* prime1 INTEGER, -- p
|
||||
* prime2 INTEGER, -- q
|
||||
* exponent1 INTEGER, -- d mod (p-1)
|
||||
* exponent2 INTEGER, -- d mod (q-1)
|
||||
* coefficient INTEGER -- (inverse of q) mod p
|
||||
* }
|
||||
*
|
||||
* Version ::= INTEGER -- shall be 0 for this version of the standard
|
||||
*/
|
||||
if (asn1_get_next(buf, len, &hdr) < 0 ||
|
||||
hdr.class != ASN1_CLASS_UNIVERSAL ||
|
||||
hdr.tag != ASN1_TAG_SEQUENCE) {
|
||||
wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE "
|
||||
"(public key) - found class %d tag 0x%x",
|
||||
hdr.class, hdr.tag);
|
||||
goto error;
|
||||
}
|
||||
pos = hdr.payload;
|
||||
end = pos + hdr.length;
|
||||
|
||||
zero = bignum_init();
|
||||
if (zero == NULL)
|
||||
goto error;
|
||||
pos = crypto_rsa_parse_integer(pos, end, zero);
|
||||
if (pos == NULL || bignum_cmp_d(zero, 0) != 0) {
|
||||
wpa_printf(MSG_DEBUG, "RSA: Expected zero INTEGER in the "
|
||||
"beginning of private key; not found");
|
||||
bignum_deinit(zero);
|
||||
goto error;
|
||||
}
|
||||
bignum_deinit(zero);
|
||||
|
||||
pos = crypto_rsa_parse_integer(pos, end, key->n);
|
||||
pos = crypto_rsa_parse_integer(pos, end, key->e);
|
||||
pos = crypto_rsa_parse_integer(pos, end, key->d);
|
||||
pos = crypto_rsa_parse_integer(pos, end, key->p);
|
||||
pos = crypto_rsa_parse_integer(pos, end, key->q);
|
||||
pos = crypto_rsa_parse_integer(pos, end, key->dmp1);
|
||||
pos = crypto_rsa_parse_integer(pos, end, key->dmq1);
|
||||
pos = crypto_rsa_parse_integer(pos, end, key->iqmp);
|
||||
|
||||
if (pos == NULL)
|
||||
goto error;
|
||||
|
||||
if (pos != end) {
|
||||
wpa_hexdump(MSG_DEBUG,
|
||||
"RSA: Extra data in public key SEQUENCE",
|
||||
pos, end - pos);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return key;
|
||||
|
||||
error:
|
||||
crypto_rsa_free(key);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* crypto_rsa_get_modulus_len - Get the modulus length of the RSA key
|
||||
* @key: RSA key
|
||||
* Returns: Modulus length of the key
|
||||
*/
|
||||
size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key)
|
||||
{
|
||||
return bignum_get_unsigned_bin_len(key->n);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* crypto_rsa_exptmod - RSA modular exponentiation
|
||||
* @in: Input data
|
||||
* @inlen: Input data length
|
||||
* @out: Buffer for output data
|
||||
* @outlen: Maximum size of the output buffer and used size on success
|
||||
* @key: RSA key
|
||||
* @use_private: 1 = Use RSA private key, 0 = Use RSA public key
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen,
|
||||
struct crypto_rsa_key *key, int use_private)
|
||||
{
|
||||
struct bignum *tmp, *a = NULL, *b = NULL;
|
||||
int ret = -1;
|
||||
size_t modlen;
|
||||
|
||||
if (use_private && !key->private_key)
|
||||
return -1;
|
||||
|
||||
tmp = bignum_init();
|
||||
if (tmp == NULL)
|
||||
return -1;
|
||||
|
||||
if (bignum_set_unsigned_bin(tmp, in, inlen) < 0)
|
||||
goto error;
|
||||
if (bignum_cmp(key->n, tmp) < 0) {
|
||||
/* Too large input value for the RSA key modulus */
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (use_private) {
|
||||
/*
|
||||
* Decrypt (or sign) using Chinese remainer theorem to speed
|
||||
* up calculation. This is equivalent to tmp = tmp^d mod n
|
||||
* (which would require more CPU to calculate directly).
|
||||
*
|
||||
* dmp1 = (1/e) mod (p-1)
|
||||
* dmq1 = (1/e) mod (q-1)
|
||||
* iqmp = (1/q) mod p, where p > q
|
||||
* m1 = c^dmp1 mod p
|
||||
* m2 = c^dmq1 mod q
|
||||
* h = q^-1 (m1 - m2) mod p
|
||||
* m = m2 + hq
|
||||
*/
|
||||
a = bignum_init();
|
||||
b = bignum_init();
|
||||
if (a == NULL || b == NULL)
|
||||
goto error;
|
||||
|
||||
/* a = tmp^dmp1 mod p */
|
||||
if (bignum_exptmod(tmp, key->dmp1, key->p, a) < 0)
|
||||
goto error;
|
||||
|
||||
/* b = tmp^dmq1 mod q */
|
||||
if (bignum_exptmod(tmp, key->dmq1, key->q, b) < 0)
|
||||
goto error;
|
||||
|
||||
/* tmp = (a - b) * (1/q mod p) (mod p) */
|
||||
if (bignum_sub(a, b, tmp) < 0 ||
|
||||
bignum_mulmod(tmp, key->iqmp, key->p, tmp) < 0)
|
||||
goto error;
|
||||
|
||||
/* tmp = b + q * tmp */
|
||||
if (bignum_mul(tmp, key->q, tmp) < 0 ||
|
||||
bignum_add(tmp, b, tmp) < 0)
|
||||
goto error;
|
||||
} else {
|
||||
/* Encrypt (or verify signature) */
|
||||
/* tmp = tmp^e mod N */
|
||||
if (bignum_exptmod(tmp, key->e, key->n, tmp) < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
modlen = crypto_rsa_get_modulus_len(key);
|
||||
if (modlen > *outlen) {
|
||||
*outlen = modlen;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (bignum_get_unsigned_bin_len(tmp) > modlen)
|
||||
goto error; /* should never happen */
|
||||
|
||||
*outlen = modlen;
|
||||
os_memset(out, 0, modlen);
|
||||
if (bignum_get_unsigned_bin(
|
||||
tmp, out +
|
||||
(modlen - bignum_get_unsigned_bin_len(tmp)), NULL) < 0)
|
||||
goto error;
|
||||
|
||||
ret = 0;
|
||||
|
||||
error:
|
||||
|
||||
bignum_deinit(tmp);
|
||||
bignum_deinit(a);
|
||||
bignum_deinit(b);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* crypto_rsa_free - Free RSA key
|
||||
* @key: RSA key to be freed
|
||||
*
|
||||
* This function frees an RSA key imported with either
|
||||
* crypto_rsa_import_public_key() or crypto_rsa_import_private_key().
|
||||
*/
|
||||
void crypto_rsa_free(struct crypto_rsa_key *key)
|
||||
{
|
||||
if (key) {
|
||||
bignum_deinit(key->n);
|
||||
bignum_deinit(key->e);
|
||||
bignum_deinit(key->d);
|
||||
bignum_deinit(key->p);
|
||||
bignum_deinit(key->q);
|
||||
bignum_deinit(key->dmp1);
|
||||
bignum_deinit(key->dmq1);
|
||||
bignum_deinit(key->iqmp);
|
||||
os_free(key);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,716 @@
|
|||
/*
|
||||
* TLS interface functions and an internal TLS implementation
|
||||
* Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*
|
||||
* This file interface functions for hostapd/wpa_supplicant to use the
|
||||
* integrated TLSv1 implementation.
|
||||
*/
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "crypto/sha1.h"
|
||||
#include "crypto/md5.h"
|
||||
#include "wpa2/tls/tls.h"
|
||||
#include "wpa2/tls/tlsv1_client.h"
|
||||
#include "wpa2/tls/tlsv1_server.h"
|
||||
|
||||
#ifndef CONFIG_TLS_INTERNAL_CLIENT
|
||||
#define CONFIG_TLS_INTERNAL_CLIENT
|
||||
#endif
|
||||
|
||||
static int tls_ref_count = 0;
|
||||
|
||||
struct tls_global {
|
||||
int server;
|
||||
struct tlsv1_credentials *server_cred;
|
||||
int check_crl;
|
||||
};
|
||||
|
||||
struct tls_connection {
|
||||
struct tlsv1_client *client;
|
||||
struct tlsv1_server *server;
|
||||
};
|
||||
|
||||
|
||||
void * tls_init(void)
|
||||
{
|
||||
struct tls_global *global;
|
||||
|
||||
if (tls_ref_count == 0) {
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
if (tlsv1_client_global_init())
|
||||
return NULL;
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
if (tlsv1_server_global_init())
|
||||
return NULL;
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
}
|
||||
tls_ref_count++;
|
||||
|
||||
global = (struct tls_global *)os_zalloc(sizeof(*global));
|
||||
if (global == NULL)
|
||||
return NULL;
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
void tls_deinit(void *ssl_ctx)
|
||||
{
|
||||
struct tls_global *global = ssl_ctx;
|
||||
tls_ref_count--;
|
||||
if (tls_ref_count == 0) {
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
tlsv1_client_global_deinit();
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
tlsv1_cred_free(global->server_cred);
|
||||
tlsv1_server_global_deinit();
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
}
|
||||
os_free(global);
|
||||
}
|
||||
|
||||
|
||||
int tls_get_errors(void *tls_ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct tls_connection * tls_connection_init(void *tls_ctx)
|
||||
{
|
||||
struct tls_connection *conn;
|
||||
struct tls_global *global = tls_ctx;
|
||||
|
||||
conn = (struct tls_connection *)os_zalloc(sizeof(*conn));
|
||||
if (conn == NULL)
|
||||
return NULL;
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
if (!global->server) {
|
||||
conn->client = tlsv1_client_init();
|
||||
if (conn->client == NULL) {
|
||||
os_free(conn);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
if (global->server) {
|
||||
conn->server = tlsv1_server_init(global->server_cred);
|
||||
if (conn->server == NULL) {
|
||||
os_free(conn);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
|
||||
void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
|
||||
{
|
||||
if (conn == NULL)
|
||||
return;
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
if (conn->client)
|
||||
tlsv1_client_deinit(conn->client);
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
if (conn->server)
|
||||
tlsv1_server_deinit(conn->server);
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
os_free(conn);
|
||||
}
|
||||
|
||||
|
||||
int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
|
||||
{
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
if (conn->client)
|
||||
return tlsv1_client_established(conn->client);
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
if (conn->server)
|
||||
return tlsv1_server_established(conn->server);
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
|
||||
{
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
if (conn->client)
|
||||
return tlsv1_client_shutdown(conn->client);
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
if (conn->server)
|
||||
return tlsv1_server_shutdown(conn->server);
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
|
||||
const struct tls_connection_params *params)
|
||||
{
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
struct tlsv1_credentials *cred;
|
||||
|
||||
if (conn->client == NULL)
|
||||
return -1;
|
||||
|
||||
cred = tlsv1_cred_alloc();
|
||||
if (cred == NULL)
|
||||
return -1;
|
||||
|
||||
if (tlsv1_set_ca_cert(cred, params->ca_cert,
|
||||
params->ca_cert_blob, params->ca_cert_blob_len,
|
||||
params->ca_path)) {
|
||||
wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA "
|
||||
"certificates");
|
||||
tlsv1_cred_free(cred);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tlsv1_set_cert(cred, params->client_cert,
|
||||
params->client_cert_blob,
|
||||
params->client_cert_blob_len)) {
|
||||
wpa_printf(MSG_INFO, "TLS: Failed to configure client "
|
||||
"certificate");
|
||||
tlsv1_cred_free(cred);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tlsv1_set_private_key(cred, params->private_key,
|
||||
params->private_key_passwd,
|
||||
params->private_key_blob,
|
||||
params->private_key_blob_len)) {
|
||||
wpa_printf(MSG_INFO, "TLS: Failed to load private key");
|
||||
tlsv1_cred_free(cred);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tlsv1_client_set_cred(conn->client, cred) < 0) {
|
||||
tlsv1_cred_free(cred);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tlsv1_client_set_time_checks(
|
||||
conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS));
|
||||
//conn->client, !(TLS_CONN_DISABLE_TIME_CHECKS)); //snake
|
||||
|
||||
return 0;
|
||||
#else /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
return -1;
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
}
|
||||
|
||||
|
||||
int tls_global_set_params(void *tls_ctx,
|
||||
const struct tls_connection_params *params)
|
||||
{
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
struct tls_global *global = tls_ctx;
|
||||
struct tlsv1_credentials *cred;
|
||||
|
||||
/* Currently, global parameters are only set when running in server
|
||||
* mode. */
|
||||
global->server = 1;
|
||||
tlsv1_cred_free(global->server_cred);
|
||||
global->server_cred = cred = tlsv1_cred_alloc();
|
||||
if (cred == NULL)
|
||||
return -1;
|
||||
|
||||
if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob,
|
||||
params->ca_cert_blob_len, params->ca_path)) {
|
||||
wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA "
|
||||
"certificates");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tlsv1_set_cert(cred, params->client_cert, params->client_cert_blob,
|
||||
params->client_cert_blob_len)) {
|
||||
wpa_printf(MSG_INFO, "TLS: Failed to configure server "
|
||||
"certificate");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tlsv1_set_private_key(cred, params->private_key,
|
||||
params->private_key_passwd,
|
||||
params->private_key_blob,
|
||||
params->private_key_blob_len)) {
|
||||
wpa_printf(MSG_INFO, "TLS: Failed to load private key");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob,
|
||||
params->dh_blob_len)) {
|
||||
wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#else /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
return -1;
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
}
|
||||
|
||||
|
||||
int tls_global_set_verify(void *tls_ctx, int check_crl)
|
||||
{
|
||||
struct tls_global *global = tls_ctx;
|
||||
global->check_crl = check_crl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
|
||||
int verify_peer)
|
||||
{
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
if (conn->server)
|
||||
return tlsv1_server_set_verify(conn->server, verify_peer);
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
|
||||
struct tls_keys *keys)
|
||||
{
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
if (conn->client)
|
||||
return tlsv1_client_get_keys(conn->client, keys);
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
if (conn->server)
|
||||
return tlsv1_server_get_keys(conn->server, keys);
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
|
||||
const char *label, int server_random_first,
|
||||
u8 *out, size_t out_len)
|
||||
{
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
if (conn->client) {
|
||||
return tlsv1_client_prf(conn->client, label,
|
||||
server_random_first,
|
||||
out, out_len);
|
||||
}
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
if (conn->server) {
|
||||
return tlsv1_server_prf(conn->server, label,
|
||||
server_random_first,
|
||||
out, out_len);
|
||||
}
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
struct wpabuf * tls_connection_handshake(void *tls_ctx,
|
||||
struct tls_connection *conn,
|
||||
const struct wpabuf *in_data,
|
||||
struct wpabuf **appl_data)
|
||||
{
|
||||
return tls_connection_handshake2(tls_ctx, conn, in_data, appl_data,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
struct wpabuf * tls_connection_handshake2(void *tls_ctx,
|
||||
struct tls_connection *conn,
|
||||
const struct wpabuf *in_data,
|
||||
struct wpabuf **appl_data,
|
||||
int *need_more_data)
|
||||
{
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
u8 *res, *ad;
|
||||
size_t res_len, ad_len;
|
||||
struct wpabuf *out;
|
||||
|
||||
if (conn->client == NULL)
|
||||
return NULL;
|
||||
|
||||
ad = NULL;
|
||||
res = tlsv1_client_handshake(conn->client,
|
||||
in_data ? wpabuf_head(in_data) : NULL,
|
||||
in_data ? wpabuf_len(in_data) : 0,
|
||||
&res_len, &ad, &ad_len, need_more_data);
|
||||
if (res == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
out = wpabuf_alloc_ext_data(res, res_len);
|
||||
if (out == NULL) {
|
||||
os_free(res);
|
||||
os_free(ad);
|
||||
return NULL;
|
||||
}
|
||||
if (appl_data) {
|
||||
if (ad) {
|
||||
*appl_data = wpabuf_alloc_ext_data(ad, ad_len);
|
||||
if (*appl_data == NULL)
|
||||
os_free(ad);
|
||||
} else
|
||||
*appl_data = NULL;
|
||||
} else
|
||||
os_free(ad);
|
||||
|
||||
return out;
|
||||
#else /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
return NULL;
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
}
|
||||
|
||||
|
||||
struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
|
||||
struct tls_connection *conn,
|
||||
const struct wpabuf *in_data,
|
||||
struct wpabuf **appl_data)
|
||||
{
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
u8 *res;
|
||||
size_t res_len;
|
||||
struct wpabuf *out;
|
||||
|
||||
if (conn->server == NULL)
|
||||
return NULL;
|
||||
|
||||
if (appl_data)
|
||||
*appl_data = NULL;
|
||||
|
||||
res = tlsv1_server_handshake(conn->server, wpabuf_head(in_data),
|
||||
wpabuf_len(in_data), &res_len);
|
||||
if (res == NULL && tlsv1_server_established(conn->server))
|
||||
return wpabuf_alloc(0);
|
||||
if (res == NULL)
|
||||
return NULL;
|
||||
out = wpabuf_alloc_ext_data(res, res_len);
|
||||
if (out == NULL) {
|
||||
os_free(res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return out;
|
||||
#else /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
return NULL;
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
}
|
||||
|
||||
|
||||
struct wpabuf * tls_connection_encrypt(void *tls_ctx,
|
||||
struct tls_connection *conn,
|
||||
const struct wpabuf *in_data)
|
||||
{
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
if (conn->client) {
|
||||
struct wpabuf *buf;
|
||||
int res;
|
||||
buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
res = tlsv1_client_encrypt(conn->client, wpabuf_head(in_data),
|
||||
wpabuf_len(in_data),
|
||||
wpabuf_mhead(buf),
|
||||
wpabuf_size(buf));
|
||||
if (res < 0) {
|
||||
wpabuf_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
wpabuf_put(buf, res);
|
||||
return buf;
|
||||
}
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
if (conn->server) {
|
||||
struct wpabuf *buf;
|
||||
int res;
|
||||
buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
res = tlsv1_server_encrypt(conn->server, wpabuf_head(in_data),
|
||||
wpabuf_len(in_data),
|
||||
wpabuf_mhead(buf),
|
||||
wpabuf_size(buf));
|
||||
if (res < 0) {
|
||||
wpabuf_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
wpabuf_put(buf, res);
|
||||
return buf;
|
||||
}
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
struct wpabuf * tls_connection_decrypt(void *tls_ctx,
|
||||
struct tls_connection *conn,
|
||||
const struct wpabuf *in_data)
|
||||
{
|
||||
return tls_connection_decrypt2(tls_ctx, conn, in_data, NULL);
|
||||
}
|
||||
|
||||
|
||||
struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
|
||||
struct tls_connection *conn,
|
||||
const struct wpabuf *in_data,
|
||||
int *need_more_data)
|
||||
{
|
||||
if (need_more_data)
|
||||
*need_more_data = 0;
|
||||
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
if (conn->client) {
|
||||
return tlsv1_client_decrypt(conn->client, wpabuf_head(in_data),
|
||||
wpabuf_len(in_data),
|
||||
need_more_data);
|
||||
}
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
if (conn->server) {
|
||||
struct wpabuf *buf;
|
||||
int res;
|
||||
buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
res = tlsv1_server_decrypt(conn->server, wpabuf_head(in_data),
|
||||
wpabuf_len(in_data),
|
||||
wpabuf_mhead(buf),
|
||||
wpabuf_size(buf));
|
||||
if (res < 0) {
|
||||
wpabuf_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
wpabuf_put(buf, res);
|
||||
return buf;
|
||||
}
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
|
||||
{
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
if (conn->client)
|
||||
return tlsv1_client_resumed(conn->client);
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
if (conn->server)
|
||||
return tlsv1_server_resumed(conn->server);
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
|
||||
u8 *ciphers)
|
||||
{
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
if (conn->client)
|
||||
return tlsv1_client_set_cipher_list(conn->client, ciphers);
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
if (conn->server)
|
||||
return tlsv1_server_set_cipher_list(conn->server, ciphers);
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
if (conn == NULL)
|
||||
return -1;
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
if (conn->client)
|
||||
return tlsv1_client_get_cipher(conn->client, buf, buflen);
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
if (conn->server)
|
||||
return tlsv1_server_get_cipher(conn->server, buf, buflen);
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int tls_connection_enable_workaround(void *tls_ctx,
|
||||
struct tls_connection *conn)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
|
||||
int ext_type, const u8 *data,
|
||||
size_t data_len)
|
||||
{
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
if (conn->client) {
|
||||
return tlsv1_client_hello_ext(conn->client, ext_type,
|
||||
data, data_len);
|
||||
}
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int tls_connection_get_write_alerts(void *tls_ctx,
|
||||
struct tls_connection *conn)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int tls_connection_get_keyblock_size(void *tls_ctx,
|
||||
struct tls_connection *conn)
|
||||
{
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
if (conn->client)
|
||||
return tlsv1_client_get_keyblock_size(conn->client);
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
if (conn->server)
|
||||
return tlsv1_server_get_keyblock_size(conn->server);
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
unsigned int tls_capabilities(void *tls_ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int tls_connection_set_session_ticket_cb(void *tls_ctx,
|
||||
struct tls_connection *conn,
|
||||
tls_session_ticket_cb cb,
|
||||
void *ctx)
|
||||
{
|
||||
#ifdef CONFIG_TLS_INTERNAL_CLIENT
|
||||
if (conn->client) {
|
||||
tlsv1_client_set_session_ticket_cb(conn->client, cb, ctx);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
|
||||
#ifdef CONFIG_TLS_INTERNAL_SERVER
|
||||
if (conn->server) {
|
||||
tlsv1_server_set_session_ticket_cb(conn->server, cb, ctx);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_TLS_INTERNAL_SERVER */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* tls_prf_sha1_md5 - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246)
|
||||
* @secret: Key for PRF
|
||||
* @secret_len: Length of the key in bytes
|
||||
* @label: A unique label for each purpose of the PRF
|
||||
* @seed: Seed value to bind into the key
|
||||
* @seed_len: Length of the seed
|
||||
* @out: Buffer for the generated pseudo-random key
|
||||
* @outlen: Number of bytes of key to generate
|
||||
* Returns: 0 on success, -1 on failure.
|
||||
*
|
||||
* This function is used to derive new, cryptographically separate keys from a
|
||||
* given key in TLS. This PRF is defined in RFC 2246, Chapter 5.
|
||||
*/
|
||||
int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
|
||||
const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
|
||||
{
|
||||
size_t L_S1, L_S2, i;
|
||||
const u8 *S1, *S2;
|
||||
u8 A_MD5[MD5_MAC_LEN], A_SHA1[SHA1_MAC_LEN];
|
||||
u8 P_MD5[MD5_MAC_LEN], P_SHA1[SHA1_MAC_LEN];
|
||||
int MD5_pos, SHA1_pos;
|
||||
const u8 *MD5_addr[3];
|
||||
size_t MD5_len[3];
|
||||
const unsigned char *SHA1_addr[3];
|
||||
size_t SHA1_len[3];
|
||||
|
||||
if (secret_len & 1)
|
||||
return -1;
|
||||
|
||||
MD5_addr[0] = A_MD5;
|
||||
MD5_len[0] = MD5_MAC_LEN;
|
||||
MD5_addr[1] = (unsigned char *) label;
|
||||
MD5_len[1] = os_strlen(label);
|
||||
MD5_addr[2] = seed;
|
||||
MD5_len[2] = seed_len;
|
||||
|
||||
SHA1_addr[0] = A_SHA1;
|
||||
SHA1_len[0] = SHA1_MAC_LEN;
|
||||
SHA1_addr[1] = (unsigned char *) label;
|
||||
SHA1_len[1] = os_strlen(label);
|
||||
SHA1_addr[2] = seed;
|
||||
SHA1_len[2] = seed_len;
|
||||
|
||||
/* RFC 2246, Chapter 5
|
||||
* A(0) = seed, A(i) = HMAC(secret, A(i-1))
|
||||
* P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + ..
|
||||
* PRF = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed)
|
||||
*/
|
||||
|
||||
L_S1 = L_S2 = (secret_len + 1) / 2;
|
||||
S1 = secret;
|
||||
S2 = secret + L_S1;
|
||||
if (secret_len & 1) {
|
||||
/* The last byte of S1 will be shared with S2 */
|
||||
S2--;
|
||||
}
|
||||
|
||||
hmac_md5_vector(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5);
|
||||
hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1);
|
||||
|
||||
MD5_pos = MD5_MAC_LEN;
|
||||
SHA1_pos = SHA1_MAC_LEN;
|
||||
for (i = 0; i < outlen; i++) {
|
||||
if (MD5_pos == MD5_MAC_LEN) {
|
||||
hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5);
|
||||
MD5_pos = 0;
|
||||
hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5);
|
||||
}
|
||||
if (SHA1_pos == SHA1_MAC_LEN) {
|
||||
hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len,
|
||||
P_SHA1);
|
||||
SHA1_pos = 0;
|
||||
hmac_sha1(S2, L_S2, A_SHA1, SHA1_MAC_LEN, A_SHA1);
|
||||
}
|
||||
|
||||
out[i] = P_MD5[MD5_pos] ^ P_SHA1[SHA1_pos];
|
||||
|
||||
MD5_pos++;
|
||||
SHA1_pos++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,837 @@
|
|||
/*
|
||||
* TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
|
||||
* Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "crypto/sha1.h"
|
||||
#include "wpa2/tls/tls.h"
|
||||
#include "wpa2/tls/tlsv1_common.h"
|
||||
#include "wpa2/tls/tlsv1_record.h"
|
||||
#include "wpa2/tls/tlsv1_client.h"
|
||||
#include "wpa2/tls/tlsv1_client_i.h"
|
||||
|
||||
/* TODO:
|
||||
* Support for a message fragmented across several records (RFC 2246, 6.2.1)
|
||||
*/
|
||||
|
||||
|
||||
void tls_alert(struct tlsv1_client *conn, u8 level, u8 description)
|
||||
{
|
||||
conn->alert_level = level;
|
||||
conn->alert_description = description;
|
||||
}
|
||||
|
||||
|
||||
void tlsv1_client_free_dh(struct tlsv1_client *conn)
|
||||
{
|
||||
os_free(conn->dh_p);
|
||||
os_free(conn->dh_g);
|
||||
os_free(conn->dh_ys);
|
||||
conn->dh_p = conn->dh_g = conn->dh_ys = NULL;
|
||||
}
|
||||
|
||||
|
||||
int tls_derive_pre_master_secret(u8 *pre_master_secret)
|
||||
{
|
||||
WPA_PUT_BE16(pre_master_secret, TLS_VERSION);
|
||||
if (os_get_random(pre_master_secret + 2,
|
||||
TLS_PRE_MASTER_SECRET_LEN - 2))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int tls_derive_keys(struct tlsv1_client *conn,
|
||||
const u8 *pre_master_secret, size_t pre_master_secret_len)
|
||||
{
|
||||
u8 seed[2 * TLS_RANDOM_LEN];
|
||||
u8 key_block[TLS_MAX_KEY_BLOCK_LEN];
|
||||
u8 *pos;
|
||||
size_t key_block_len;
|
||||
|
||||
if (pre_master_secret) {
|
||||
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret",
|
||||
pre_master_secret, pre_master_secret_len);
|
||||
os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
|
||||
os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
|
||||
TLS_RANDOM_LEN);
|
||||
if (tls_prf(conn->rl.tls_version,
|
||||
pre_master_secret, pre_master_secret_len,
|
||||
"master secret", seed, 2 * TLS_RANDOM_LEN,
|
||||
conn->master_secret, TLS_MASTER_SECRET_LEN)) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
|
||||
"master_secret");
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret",
|
||||
conn->master_secret, TLS_MASTER_SECRET_LEN);
|
||||
}
|
||||
|
||||
os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
|
||||
os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
|
||||
key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len);
|
||||
if (conn->rl.tls_version == TLS_VERSION_1)
|
||||
key_block_len += 2 * conn->rl.iv_size;
|
||||
if (tls_prf(conn->rl.tls_version,
|
||||
conn->master_secret, TLS_MASTER_SECRET_LEN,
|
||||
"key expansion", seed, 2 * TLS_RANDOM_LEN,
|
||||
key_block, key_block_len)) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block",
|
||||
key_block, key_block_len);
|
||||
|
||||
pos = key_block;
|
||||
|
||||
/* client_write_MAC_secret */
|
||||
os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size);
|
||||
pos += conn->rl.hash_size;
|
||||
/* server_write_MAC_secret */
|
||||
os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size);
|
||||
pos += conn->rl.hash_size;
|
||||
|
||||
/* client_write_key */
|
||||
os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len);
|
||||
pos += conn->rl.key_material_len;
|
||||
/* server_write_key */
|
||||
os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
|
||||
pos += conn->rl.key_material_len;
|
||||
|
||||
if (conn->rl.tls_version == TLS_VERSION_1) {
|
||||
/* client_write_IV */
|
||||
os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
|
||||
pos += conn->rl.iv_size;
|
||||
/* server_write_IV */
|
||||
os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
|
||||
pos += conn->rl.iv_size;
|
||||
} else {
|
||||
/*
|
||||
* Use IV field to set the mask value for TLS v1.1. A fixed
|
||||
* mask of zero is used per the RFC 4346, 6.2.3.2 CBC Block
|
||||
* Cipher option 2a.
|
||||
*/
|
||||
os_memset(conn->rl.write_iv, 0, conn->rl.iv_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_client_handshake - Process TLS handshake
|
||||
* @conn: TLSv1 client connection data from tlsv1_client_init()
|
||||
* @in_data: Input data from TLS peer
|
||||
* @in_len: Input data length
|
||||
* @out_len: Length of the output buffer.
|
||||
* @appl_data: Pointer to application data pointer, or %NULL if dropped
|
||||
* @appl_data_len: Pointer to variable that is set to appl_data length
|
||||
* @need_more_data: Set to 1 if more data would be needed to complete
|
||||
* processing
|
||||
* Returns: Pointer to output data, %NULL on failure
|
||||
*/
|
||||
u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
|
||||
const u8 *in_data, size_t in_len,
|
||||
size_t *out_len, u8 **appl_data,
|
||||
size_t *appl_data_len, int *need_more_data)
|
||||
{
|
||||
const u8 *pos, *end;
|
||||
u8 *msg = NULL, *in_msg = NULL, *in_pos, *in_end, alert, ct;
|
||||
size_t in_msg_len;
|
||||
int no_appl_data;
|
||||
int used;
|
||||
|
||||
if (need_more_data)
|
||||
*need_more_data = 0;
|
||||
|
||||
if (conn->state == CLIENT_HELLO) {
|
||||
if (in_len)
|
||||
return NULL;
|
||||
return tls_send_client_hello(conn, out_len);
|
||||
}
|
||||
|
||||
if (conn->partial_input) {
|
||||
if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
|
||||
"memory for pending record");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
goto failed;
|
||||
}
|
||||
wpabuf_put_data(conn->partial_input, in_data, in_len);
|
||||
in_data = wpabuf_head(conn->partial_input);
|
||||
in_len = wpabuf_len(conn->partial_input);
|
||||
}
|
||||
|
||||
if (in_data == NULL || in_len == 0)
|
||||
return NULL;
|
||||
|
||||
pos = in_data;
|
||||
end = in_data + in_len;
|
||||
in_msg = os_malloc(in_len);
|
||||
if (in_msg == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Each received packet may include multiple records */
|
||||
while (pos < end) {
|
||||
in_msg_len = in_len;
|
||||
used = tlsv1_record_receive(&conn->rl, pos, end - pos,
|
||||
in_msg, &in_msg_len, &alert);
|
||||
if (used < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
|
||||
"record failed");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
|
||||
goto failed;
|
||||
}
|
||||
if (used == 0) {
|
||||
struct wpabuf *partial;
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
|
||||
partial = wpabuf_alloc_copy(pos, end - pos);
|
||||
wpabuf_free(conn->partial_input);
|
||||
conn->partial_input = partial;
|
||||
if (conn->partial_input == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
|
||||
"allocate memory for pending "
|
||||
"record");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
goto failed;
|
||||
}
|
||||
os_free(in_msg);
|
||||
if (need_more_data)
|
||||
*need_more_data = 1;
|
||||
return NULL;
|
||||
}
|
||||
ct = pos[0];
|
||||
|
||||
in_pos = in_msg;
|
||||
in_end = in_msg + in_msg_len;
|
||||
|
||||
/* Each received record may include multiple messages of the
|
||||
* same ContentType. */
|
||||
while (in_pos < in_end) {
|
||||
in_msg_len = in_end - in_pos;
|
||||
if (tlsv1_client_process_handshake(conn, ct, in_pos,
|
||||
&in_msg_len,
|
||||
appl_data,
|
||||
appl_data_len) < 0)
|
||||
goto failed;
|
||||
in_pos += in_msg_len;
|
||||
}
|
||||
|
||||
pos += used;
|
||||
}
|
||||
|
||||
os_free(in_msg);
|
||||
in_msg = NULL;
|
||||
|
||||
no_appl_data = appl_data == NULL || *appl_data == NULL;
|
||||
msg = tlsv1_client_handshake_write(conn, out_len, no_appl_data);
|
||||
|
||||
failed:
|
||||
os_free(in_msg);
|
||||
if (conn->alert_level) {
|
||||
wpabuf_free(conn->partial_input);
|
||||
conn->partial_input = NULL;
|
||||
conn->state = FAILED;
|
||||
os_free(msg);
|
||||
msg = tlsv1_client_send_alert(conn, conn->alert_level,
|
||||
conn->alert_description,
|
||||
out_len);
|
||||
} else if (msg == NULL) {
|
||||
msg = (u8 *)os_zalloc(1);
|
||||
*out_len = 0;
|
||||
}
|
||||
|
||||
if (need_more_data == NULL || !(*need_more_data)) {
|
||||
wpabuf_free(conn->partial_input);
|
||||
conn->partial_input = NULL;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_client_encrypt - Encrypt data into TLS tunnel
|
||||
* @conn: TLSv1 client connection data from tlsv1_client_init()
|
||||
* @in_data: Pointer to plaintext data to be encrypted
|
||||
* @in_len: Input buffer length
|
||||
* @out_data: Pointer to output buffer (encrypted TLS data)
|
||||
* @out_len: Maximum out_data length
|
||||
* Returns: Number of bytes written to out_data, -1 on failure
|
||||
*
|
||||
* This function is used after TLS handshake has been completed successfully to
|
||||
* send data in the encrypted tunnel.
|
||||
*/
|
||||
int tlsv1_client_encrypt(struct tlsv1_client *conn,
|
||||
const u8 *in_data, size_t in_len,
|
||||
u8 *out_data, size_t out_len)
|
||||
{
|
||||
size_t rlen;
|
||||
|
||||
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
|
||||
in_data, in_len);
|
||||
|
||||
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
|
||||
out_data, out_len, in_data, in_len, &rlen) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return rlen;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_client_decrypt - Decrypt data from TLS tunnel
|
||||
* @conn: TLSv1 client connection data from tlsv1_client_init()
|
||||
* @in_data: Pointer to input buffer (encrypted TLS data)
|
||||
* @in_len: Input buffer length
|
||||
* @need_more_data: Set to 1 if more data would be needed to complete
|
||||
* processing
|
||||
* Returns: Decrypted data or %NULL on failure
|
||||
*
|
||||
* This function is used after TLS handshake has been completed successfully to
|
||||
* receive data from the encrypted tunnel.
|
||||
*/
|
||||
struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn,
|
||||
const u8 *in_data, size_t in_len,
|
||||
int *need_more_data)
|
||||
{
|
||||
const u8 *in_end, *pos;
|
||||
int used;
|
||||
u8 alert, *out_pos, ct;
|
||||
size_t olen;
|
||||
struct wpabuf *buf = NULL;
|
||||
|
||||
if (need_more_data)
|
||||
*need_more_data = 0;
|
||||
|
||||
if (conn->partial_input) {
|
||||
if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
|
||||
"memory for pending record");
|
||||
alert = TLS_ALERT_INTERNAL_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
wpabuf_put_data(conn->partial_input, in_data, in_len);
|
||||
in_data = wpabuf_head(conn->partial_input);
|
||||
in_len = wpabuf_len(conn->partial_input);
|
||||
}
|
||||
|
||||
pos = in_data;
|
||||
in_end = in_data + in_len;
|
||||
|
||||
while (pos < in_end) {
|
||||
ct = pos[0];
|
||||
if (wpabuf_resize(&buf, in_end - pos) < 0) {
|
||||
alert = TLS_ALERT_INTERNAL_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
out_pos = wpabuf_put(buf, 0);
|
||||
olen = wpabuf_tailroom(buf);
|
||||
used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
|
||||
out_pos, &olen, &alert);
|
||||
if (used < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
|
||||
"failed");
|
||||
goto fail;
|
||||
}
|
||||
if (used == 0) {
|
||||
struct wpabuf *partial;
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
|
||||
partial = wpabuf_alloc_copy(pos, in_end - pos);
|
||||
wpabuf_free(conn->partial_input);
|
||||
conn->partial_input = partial;
|
||||
if (conn->partial_input == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
|
||||
"allocate memory for pending "
|
||||
"record");
|
||||
alert = TLS_ALERT_INTERNAL_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
if (need_more_data)
|
||||
*need_more_data = 1;
|
||||
return buf;
|
||||
}
|
||||
|
||||
if (ct == TLS_CONTENT_TYPE_ALERT) {
|
||||
if (olen < 2) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Alert "
|
||||
"underflow");
|
||||
alert = TLS_ALERT_DECODE_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
|
||||
out_pos[0], out_pos[1]);
|
||||
if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
|
||||
/* Continue processing */
|
||||
pos += used;
|
||||
continue;
|
||||
}
|
||||
|
||||
alert = out_pos[1];
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
|
||||
"0x%x when decrypting application data",
|
||||
pos[0]);
|
||||
alert = TLS_ALERT_UNEXPECTED_MESSAGE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
wpabuf_put(buf, olen);
|
||||
|
||||
pos += used;
|
||||
}
|
||||
|
||||
wpabuf_free(conn->partial_input);
|
||||
conn->partial_input = NULL;
|
||||
return buf;
|
||||
|
||||
fail:
|
||||
wpabuf_free(buf);
|
||||
wpabuf_free(conn->partial_input);
|
||||
conn->partial_input = NULL;
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_client_global_init - Initialize TLSv1 client
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*
|
||||
* This function must be called before using any other TLSv1 client functions.
|
||||
*/
|
||||
int tlsv1_client_global_init(void)
|
||||
{
|
||||
return crypto_global_init();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_client_global_deinit - Deinitialize TLSv1 client
|
||||
*
|
||||
* This function can be used to deinitialize the TLSv1 client that was
|
||||
* initialized by calling tlsv1_client_global_init(). No TLSv1 client functions
|
||||
* can be called after this before calling tlsv1_client_global_init() again.
|
||||
*/
|
||||
void tlsv1_client_global_deinit(void)
|
||||
{
|
||||
crypto_global_deinit();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_client_init - Initialize TLSv1 client connection
|
||||
* Returns: Pointer to TLSv1 client connection data or %NULL on failure
|
||||
*/
|
||||
struct tlsv1_client * tlsv1_client_init(void)
|
||||
{
|
||||
struct tlsv1_client *conn;
|
||||
size_t count;
|
||||
u16 *suites;
|
||||
|
||||
conn = (struct tlsv1_client *)os_zalloc(sizeof(*conn));
|
||||
if (conn == NULL)
|
||||
return NULL;
|
||||
|
||||
conn->state = CLIENT_HELLO;
|
||||
|
||||
if (tls_verify_hash_init(&conn->verify) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify "
|
||||
"hash");
|
||||
os_free(conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
suites = conn->cipher_suites;
|
||||
suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
|
||||
suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
|
||||
suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
|
||||
suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
|
||||
suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
|
||||
suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
|
||||
suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
|
||||
conn->num_cipher_suites = count;
|
||||
|
||||
conn->rl.tls_version = TLS_VERSION;
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_client_deinit - Deinitialize TLSv1 client connection
|
||||
* @conn: TLSv1 client connection data from tlsv1_client_init()
|
||||
*/
|
||||
void tlsv1_client_deinit(struct tlsv1_client *conn)
|
||||
{
|
||||
crypto_public_key_free(conn->server_rsa_key);
|
||||
tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
|
||||
tlsv1_record_change_write_cipher(&conn->rl);
|
||||
tlsv1_record_change_read_cipher(&conn->rl);
|
||||
tls_verify_hash_free(&conn->verify);
|
||||
os_free(conn->client_hello_ext);
|
||||
tlsv1_client_free_dh(conn);
|
||||
tlsv1_cred_free(conn->cred);
|
||||
wpabuf_free(conn->partial_input);
|
||||
os_free(conn);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_client_established - Check whether connection has been established
|
||||
* @conn: TLSv1 client connection data from tlsv1_client_init()
|
||||
* Returns: 1 if connection is established, 0 if not
|
||||
*/
|
||||
int tlsv1_client_established(struct tlsv1_client *conn)
|
||||
{
|
||||
return conn->state == ESTABLISHED;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_client_prf - Use TLS-PRF to derive keying material
|
||||
* @conn: TLSv1 client connection data from tlsv1_client_init()
|
||||
* @label: Label (e.g., description of the key) for PRF
|
||||
* @server_random_first: seed is 0 = client_random|server_random,
|
||||
* 1 = server_random|client_random
|
||||
* @out: Buffer for output data from TLS-PRF
|
||||
* @out_len: Length of the output buffer
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
|
||||
int server_random_first, u8 *out, size_t out_len)
|
||||
{
|
||||
u8 seed[2 * TLS_RANDOM_LEN];
|
||||
|
||||
if (conn->state != ESTABLISHED)
|
||||
return -1;
|
||||
|
||||
if (server_random_first) {
|
||||
os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
|
||||
os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
|
||||
TLS_RANDOM_LEN);
|
||||
} else {
|
||||
os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
|
||||
os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
|
||||
TLS_RANDOM_LEN);
|
||||
}
|
||||
|
||||
return tls_prf(conn->rl.tls_version,
|
||||
conn->master_secret, TLS_MASTER_SECRET_LEN,
|
||||
label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_client_get_cipher - Get current cipher name
|
||||
* @conn: TLSv1 client connection data from tlsv1_client_init()
|
||||
* @buf: Buffer for the cipher name
|
||||
* @buflen: buf size
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*
|
||||
* Get the name of the currently used cipher.
|
||||
*/
|
||||
int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
#ifndef ESPRESSIF_USE
|
||||
char *cipher;
|
||||
|
||||
switch (conn->rl.cipher_suite) {
|
||||
case TLS_RSA_WITH_RC4_128_MD5:
|
||||
cipher = "RC4-MD5";
|
||||
break;
|
||||
case TLS_RSA_WITH_RC4_128_SHA:
|
||||
cipher = "RC4-SHA";
|
||||
break;
|
||||
case TLS_RSA_WITH_DES_CBC_SHA:
|
||||
cipher = "DES-CBC-SHA";
|
||||
break;
|
||||
case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
|
||||
cipher = "DES-CBC3-SHA";
|
||||
break;
|
||||
case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
|
||||
cipher = "ADH-AES-128-SHA256";
|
||||
break;
|
||||
case TLS_DH_anon_WITH_AES_128_CBC_SHA:
|
||||
cipher = "ADH-AES-128-SHA";
|
||||
break;
|
||||
case TLS_RSA_WITH_AES_256_CBC_SHA:
|
||||
cipher = "AES-256-SHA";
|
||||
break;
|
||||
case TLS_RSA_WITH_AES_256_CBC_SHA256:
|
||||
cipher = "AES-256-SHA256";
|
||||
break;
|
||||
case TLS_RSA_WITH_AES_128_CBC_SHA:
|
||||
cipher = "AES-128-SHA";
|
||||
break;
|
||||
case TLS_RSA_WITH_AES_128_CBC_SHA256:
|
||||
cipher = "AES-128-SHA256";
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
os_memcpy((u8 *)buf, (u8 *)cipher, buflen);
|
||||
|
||||
return 0;
|
||||
#else
|
||||
char cipher[20];
|
||||
|
||||
switch (conn->rl.cipher_suite) {
|
||||
case TLS_RSA_WITH_RC4_128_MD5:
|
||||
strcpy(cipher, "RC4-MD5");
|
||||
break;
|
||||
case TLS_RSA_WITH_RC4_128_SHA:
|
||||
strcpy(cipher, "RC4-SHA");
|
||||
break;
|
||||
case TLS_RSA_WITH_DES_CBC_SHA:
|
||||
strcpy(cipher, "DES-CBC-SHA");
|
||||
break;
|
||||
case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
|
||||
strcpy(cipher, "DES-CBC3-SHA");
|
||||
break;
|
||||
case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
|
||||
strcpy(cipher, "ADH-AES-128-SHA256");
|
||||
break;
|
||||
case TLS_DH_anon_WITH_AES_128_CBC_SHA:
|
||||
strcpy(cipher, "ADH-AES-128-SHA");
|
||||
break;
|
||||
case TLS_RSA_WITH_AES_256_CBC_SHA:
|
||||
strcpy(cipher, "AES-256-SHA");
|
||||
break;
|
||||
case TLS_RSA_WITH_AES_256_CBC_SHA256:
|
||||
strcpy(cipher, "AES-256-SHA256");
|
||||
break;
|
||||
case TLS_RSA_WITH_AES_128_CBC_SHA:
|
||||
strcpy(cipher, "AES-128-SHA");
|
||||
break;
|
||||
case TLS_RSA_WITH_AES_128_CBC_SHA256:
|
||||
strcpy(cipher, "AES-128-SHA256");
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
os_memcpy((u8 *)buf, (u8 *)cipher, buflen);
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_client_shutdown - Shutdown TLS connection
|
||||
* @conn: TLSv1 client connection data from tlsv1_client_init()
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int tlsv1_client_shutdown(struct tlsv1_client *conn)
|
||||
{
|
||||
conn->state = CLIENT_HELLO;
|
||||
|
||||
if (tls_verify_hash_init(&conn->verify) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify "
|
||||
"hash");
|
||||
return -1;
|
||||
}
|
||||
|
||||
tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
|
||||
tlsv1_record_change_write_cipher(&conn->rl);
|
||||
tlsv1_record_change_read_cipher(&conn->rl);
|
||||
|
||||
conn->certificate_requested = 0;
|
||||
crypto_public_key_free(conn->server_rsa_key);
|
||||
conn->server_rsa_key = NULL;
|
||||
conn->session_resumed = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_client_resumed - Was session resumption used
|
||||
* @conn: TLSv1 client connection data from tlsv1_client_init()
|
||||
* Returns: 1 if current session used session resumption, 0 if not
|
||||
*/
|
||||
int tlsv1_client_resumed(struct tlsv1_client *conn)
|
||||
{
|
||||
return !!conn->session_resumed;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_client_hello_ext - Set TLS extension for ClientHello
|
||||
* @conn: TLSv1 client connection data from tlsv1_client_init()
|
||||
* @ext_type: Extension type
|
||||
* @data: Extension payload (%NULL to remove extension)
|
||||
* @data_len: Extension payload length
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type,
|
||||
const u8 *data, size_t data_len)
|
||||
{
|
||||
u8 *pos;
|
||||
|
||||
conn->session_ticket_included = 0;
|
||||
os_free(conn->client_hello_ext);
|
||||
conn->client_hello_ext = NULL;
|
||||
conn->client_hello_ext_len = 0;
|
||||
|
||||
if (data == NULL || data_len == 0)
|
||||
return 0;
|
||||
|
||||
pos = conn->client_hello_ext = os_malloc(6 + data_len);
|
||||
if (pos == NULL)
|
||||
return -1;
|
||||
|
||||
WPA_PUT_BE16(pos, 4 + data_len);
|
||||
pos += 2;
|
||||
WPA_PUT_BE16(pos, ext_type);
|
||||
pos += 2;
|
||||
WPA_PUT_BE16(pos, data_len);
|
||||
pos += 2;
|
||||
os_memcpy(pos, data, data_len);
|
||||
conn->client_hello_ext_len = 6 + data_len;
|
||||
|
||||
if (ext_type == TLS_EXT_PAC_OPAQUE) {
|
||||
conn->session_ticket_included = 1;
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Using session ticket");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_client_get_keys - Get master key and random data from TLS connection
|
||||
* @conn: TLSv1 client connection data from tlsv1_client_init()
|
||||
* @keys: Structure of key/random data (filled on success)
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys)
|
||||
{
|
||||
os_memset(keys, 0, sizeof(*keys));
|
||||
if (conn->state == CLIENT_HELLO)
|
||||
return -1;
|
||||
|
||||
keys->client_random = conn->client_random;
|
||||
keys->client_random_len = TLS_RANDOM_LEN;
|
||||
|
||||
if (conn->state != SERVER_HELLO) {
|
||||
keys->server_random = conn->server_random;
|
||||
keys->server_random_len = TLS_RANDOM_LEN;
|
||||
keys->master_key = conn->master_secret;
|
||||
keys->master_key_len = TLS_MASTER_SECRET_LEN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_client_get_keyblock_size - Get TLS key_block size
|
||||
* @conn: TLSv1 client connection data from tlsv1_client_init()
|
||||
* Returns: Size of the key_block for the negotiated cipher suite or -1 on
|
||||
* failure
|
||||
*/
|
||||
int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn)
|
||||
{
|
||||
if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO)
|
||||
return -1;
|
||||
|
||||
return 2 * (conn->rl.hash_size + conn->rl.key_material_len +
|
||||
conn->rl.iv_size);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_client_set_cipher_list - Configure acceptable cipher suites
|
||||
* @conn: TLSv1 client connection data from tlsv1_client_init()
|
||||
* @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
|
||||
* (TLS_CIPHER_*).
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers)
|
||||
{
|
||||
size_t count;
|
||||
u16 *suites;
|
||||
|
||||
/* TODO: implement proper configuration of cipher suites */
|
||||
if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
|
||||
count = 0;
|
||||
suites = conn->cipher_suites;
|
||||
suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA256;
|
||||
suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
|
||||
suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA256;
|
||||
suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
|
||||
suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
|
||||
suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
|
||||
suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA;
|
||||
|
||||
/*
|
||||
* Cisco AP (at least 350 and 1200 series) local authentication
|
||||
* server does not know how to search cipher suites from the
|
||||
* list and seem to require that the last entry in the list is
|
||||
* the one that it wants to use. However, TLS specification
|
||||
* requires the list to be in the client preference order. As a
|
||||
* workaround, add anon-DH AES-128-SHA1 again at the end of the
|
||||
* list to allow the Cisco code to find it.
|
||||
*/
|
||||
suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
|
||||
conn->num_cipher_suites = count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_client_set_cred - Set client credentials
|
||||
* @conn: TLSv1 client connection data from tlsv1_client_init()
|
||||
* @cred: Credentials from tlsv1_cred_alloc()
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*
|
||||
* On success, the client takes ownership of the credentials block and caller
|
||||
* must not free it. On failure, caller is responsible for freeing the
|
||||
* credential block.
|
||||
*/
|
||||
int tlsv1_client_set_cred(struct tlsv1_client *conn,
|
||||
struct tlsv1_credentials *cred)
|
||||
{
|
||||
tlsv1_cred_free(conn->cred);
|
||||
conn->cred = cred;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled)
|
||||
{
|
||||
conn->disable_time_checks = !enabled;
|
||||
}
|
||||
|
||||
|
||||
void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
|
||||
tlsv1_client_session_ticket_cb cb,
|
||||
void *ctx)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)",
|
||||
cb, ctx);
|
||||
conn->session_ticket_cb = cb;
|
||||
conn->session_ticket_cb_ctx = ctx;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,901 @@
|
|||
/*
|
||||
* TLSv1 client - write handshake message
|
||||
* Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "crypto/md5.h"
|
||||
#include "crypto/sha1.h"
|
||||
#include "crypto/sha256.h"
|
||||
#include "crypto/random.h"
|
||||
#include "wpa2/tls/tls.h"
|
||||
#include "wpa2/tls/x509v3.h"
|
||||
#include "wpa2/tls/tlsv1_common.h"
|
||||
#include "wpa2/tls/tlsv1_record.h"
|
||||
#include "wpa2/tls/tlsv1_client.h"
|
||||
#include "wpa2/tls/tlsv1_client_i.h"
|
||||
|
||||
#include "wpa2/eap_peer/eap_i.h"
|
||||
|
||||
static size_t tls_client_cert_chain_der_len(struct tlsv1_client *conn)
|
||||
{
|
||||
size_t len = 0;
|
||||
struct x509_certificate *cert;
|
||||
|
||||
if (conn->cred == NULL)
|
||||
return 0;
|
||||
|
||||
cert = conn->cred->cert;
|
||||
while (cert) {
|
||||
len += 3 + cert->cert_len;
|
||||
if (x509_certificate_self_signed(cert))
|
||||
break;
|
||||
cert = x509_certificate_get_subject(conn->cred->trusted_certs,
|
||||
&cert->issuer);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len)
|
||||
{
|
||||
u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr;
|
||||
struct os_time now;
|
||||
size_t len, i;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello");
|
||||
*out_len = 0;
|
||||
|
||||
os_get_time(&now);
|
||||
WPA_PUT_BE32(conn->client_random, now.sec);
|
||||
if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) {
|
||||
wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
|
||||
"client_random");
|
||||
return NULL;
|
||||
}
|
||||
wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random",
|
||||
conn->client_random, TLS_RANDOM_LEN);
|
||||
|
||||
len = 100 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len;
|
||||
hello = os_malloc(len);
|
||||
if (hello == NULL)
|
||||
return NULL;
|
||||
end = hello + len;
|
||||
|
||||
rhdr = hello;
|
||||
pos = rhdr + TLS_RECORD_HEADER_LEN;
|
||||
|
||||
/* opaque fragment[TLSPlaintext.length] */
|
||||
|
||||
/* Handshake */
|
||||
hs_start = pos;
|
||||
/* HandshakeType msg_type */
|
||||
*pos++ = TLS_HANDSHAKE_TYPE_CLIENT_HELLO;
|
||||
/* uint24 length (to be filled) */
|
||||
hs_length = pos;
|
||||
pos += 3;
|
||||
/* body - ClientHello */
|
||||
/* ProtocolVersion client_version */
|
||||
WPA_PUT_BE16(pos, TLS_VERSION);
|
||||
pos += 2;
|
||||
/* Random random: uint32 gmt_unix_time, opaque random_bytes */
|
||||
os_memcpy(pos, conn->client_random, TLS_RANDOM_LEN);
|
||||
pos += TLS_RANDOM_LEN;
|
||||
/* SessionID session_id */
|
||||
*pos++ = conn->session_id_len;
|
||||
os_memcpy(pos, conn->session_id, conn->session_id_len);
|
||||
pos += conn->session_id_len;
|
||||
/* CipherSuite cipher_suites<2..2^16-1> */
|
||||
WPA_PUT_BE16(pos, 2 * conn->num_cipher_suites);
|
||||
pos += 2;
|
||||
for (i = 0; i < conn->num_cipher_suites; i++) {
|
||||
WPA_PUT_BE16(pos, conn->cipher_suites[i]);
|
||||
pos += 2;
|
||||
}
|
||||
/* CompressionMethod compression_methods<1..2^8-1> */
|
||||
*pos++ = 1;
|
||||
*pos++ = TLS_COMPRESSION_NULL;
|
||||
|
||||
if (conn->client_hello_ext) {
|
||||
os_memcpy(pos, conn->client_hello_ext,
|
||||
conn->client_hello_ext_len);
|
||||
pos += conn->client_hello_ext_len;
|
||||
}
|
||||
|
||||
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
|
||||
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
|
||||
|
||||
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
|
||||
rhdr, end - rhdr, hs_start, pos - hs_start,
|
||||
out_len) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
os_free(hello);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conn->state = SERVER_HELLO;
|
||||
|
||||
return hello;
|
||||
}
|
||||
|
||||
|
||||
static int tls_write_client_certificate(struct tlsv1_client *conn,
|
||||
u8 **msgpos, u8 *end)
|
||||
{
|
||||
u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start;
|
||||
size_t rlen;
|
||||
struct x509_certificate *cert;
|
||||
|
||||
pos = *msgpos;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
|
||||
rhdr = pos;
|
||||
pos += TLS_RECORD_HEADER_LEN;
|
||||
|
||||
/* opaque fragment[TLSPlaintext.length] */
|
||||
|
||||
/* Handshake */
|
||||
hs_start = pos;
|
||||
/* HandshakeType msg_type */
|
||||
*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE;
|
||||
/* uint24 length (to be filled) */
|
||||
hs_length = pos;
|
||||
pos += 3;
|
||||
/* body - Certificate */
|
||||
/* uint24 length (to be filled) */
|
||||
cert_start = pos;
|
||||
pos += 3;
|
||||
cert = conn->cred ? conn->cred->cert : NULL;
|
||||
while (cert) {
|
||||
if (pos + 3 + cert->cert_len > end) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
|
||||
"for Certificate (cert_len=%lu left=%lu)",
|
||||
(unsigned long) cert->cert_len,
|
||||
(unsigned long) (end - pos));
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
WPA_PUT_BE24(pos, cert->cert_len);
|
||||
pos += 3;
|
||||
os_memcpy(pos, cert->cert_start, cert->cert_len);
|
||||
pos += cert->cert_len;
|
||||
|
||||
if (x509_certificate_self_signed(cert))
|
||||
break;
|
||||
cert = x509_certificate_get_subject(conn->cred->trusted_certs,
|
||||
&cert->issuer);
|
||||
}
|
||||
if (conn->cred == NULL || cert == conn->cred->cert || cert == NULL) {
|
||||
/*
|
||||
* Client was not configured with all the needed certificates
|
||||
* to form a full certificate chain. The server may fail to
|
||||
* validate the chain unless it is configured with all the
|
||||
* missing CA certificates.
|
||||
*/
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Full client certificate chain "
|
||||
"not configured - validation may fail");
|
||||
}
|
||||
WPA_PUT_BE24(cert_start, pos - cert_start - 3);
|
||||
|
||||
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
|
||||
|
||||
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
|
||||
rhdr, end - rhdr, hs_start, pos - hs_start,
|
||||
&rlen) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
pos = rhdr + rlen;
|
||||
|
||||
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
|
||||
|
||||
*msgpos = pos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end)
|
||||
{
|
||||
/* ClientDiffieHellmanPublic */
|
||||
u8 *csecret, *csecret_start, *dh_yc, *shared;
|
||||
size_t csecret_len, dh_yc_len, shared_len;
|
||||
|
||||
csecret_len = conn->dh_p_len;
|
||||
csecret = os_malloc(csecret_len);
|
||||
if (csecret == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
|
||||
"memory for Yc (Diffie-Hellman)");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
if (random_get_bytes(csecret, csecret_len)) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
|
||||
"data for Diffie-Hellman");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
os_free(csecret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (os_memcmp(csecret, conn->dh_p, csecret_len) > 0)
|
||||
csecret[0] = 0; /* make sure Yc < p */
|
||||
|
||||
csecret_start = csecret;
|
||||
while (csecret_len > 1 && *csecret_start == 0) {
|
||||
csecret_start++;
|
||||
csecret_len--;
|
||||
}
|
||||
wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH client's secret value",
|
||||
csecret_start, csecret_len);
|
||||
|
||||
/* Yc = g^csecret mod p */
|
||||
dh_yc_len = conn->dh_p_len;
|
||||
dh_yc = os_malloc(dh_yc_len);
|
||||
if (dh_yc == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
|
||||
"memory for Diffie-Hellman");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
os_free(csecret);
|
||||
return -1;
|
||||
}
|
||||
if (wpa2_crypto_funcs.crypto_mod_exp) {
|
||||
if(wpa2_crypto_funcs.crypto_mod_exp(conn->dh_g, conn->dh_g_len,
|
||||
csecret_start, csecret_len,
|
||||
conn->dh_p, conn->dh_p_len,
|
||||
dh_yc, &dh_yc_len)) {
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
os_free(csecret);
|
||||
os_free(dh_yc);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
os_free(csecret);
|
||||
os_free(dh_yc);
|
||||
wpa_printf(MSG_ERROR, "Fail to register crypto_mod_exp function!\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)",
|
||||
dh_yc, dh_yc_len);
|
||||
|
||||
WPA_PUT_BE16(*pos, dh_yc_len);
|
||||
*pos += 2;
|
||||
if (*pos + dh_yc_len > end) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the "
|
||||
"message buffer for Yc");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
os_free(csecret);
|
||||
os_free(dh_yc);
|
||||
return -1;
|
||||
}
|
||||
os_memcpy(*pos, dh_yc, dh_yc_len);
|
||||
*pos += dh_yc_len;
|
||||
os_free(dh_yc);
|
||||
|
||||
shared_len = conn->dh_p_len;
|
||||
shared = os_malloc(shared_len);
|
||||
if (shared == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for "
|
||||
"DH");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
os_free(csecret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* shared = Ys^csecret mod p */
|
||||
if (wpa2_crypto_funcs.crypto_mod_exp) {
|
||||
if(wpa2_crypto_funcs.crypto_mod_exp(conn->dh_ys, conn->dh_ys_len,
|
||||
csecret_start, csecret_len,
|
||||
conn->dh_p, conn->dh_p_len,
|
||||
shared, &shared_len)) {
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
os_free(csecret);
|
||||
os_free(shared);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
os_free(csecret);
|
||||
os_free(shared);
|
||||
wpa_printf(MSG_ERROR, "Fail to register crypto_mod_exp function!\r\n");
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange",
|
||||
shared, shared_len);
|
||||
|
||||
os_memset(csecret_start, 0, csecret_len);
|
||||
os_free(csecret);
|
||||
if (tls_derive_keys(conn, shared, shared_len)) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
os_free(shared);
|
||||
return -1;
|
||||
}
|
||||
os_memset(shared, 0, shared_len);
|
||||
os_free(shared);
|
||||
tlsv1_client_free_dh(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int tlsv1_key_x_rsa(struct tlsv1_client *conn, u8 **pos, u8 *end)
|
||||
{
|
||||
u8 pre_master_secret[TLS_PRE_MASTER_SECRET_LEN];
|
||||
size_t clen;
|
||||
int res;
|
||||
|
||||
if (tls_derive_pre_master_secret(pre_master_secret) < 0 ||
|
||||
tls_derive_keys(conn, pre_master_secret,
|
||||
TLS_PRE_MASTER_SECRET_LEN)) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* EncryptedPreMasterSecret */
|
||||
if (conn->server_rsa_key == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: No server RSA key to "
|
||||
"use for encrypting pre-master secret");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* RSA encrypted value is encoded with PKCS #1 v1.5 block type 2. */
|
||||
*pos += 2;
|
||||
clen = end - *pos;
|
||||
res = crypto_public_key_encrypt_pkcs1_v15(
|
||||
conn->server_rsa_key,
|
||||
pre_master_secret, TLS_PRE_MASTER_SECRET_LEN,
|
||||
*pos, &clen);
|
||||
os_memset(pre_master_secret, 0, TLS_PRE_MASTER_SECRET_LEN);
|
||||
if (res < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: RSA encryption failed");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
WPA_PUT_BE16(*pos - 2, clen);
|
||||
wpa_hexdump(MSG_MSGDUMP, "TLSv1: Encrypted pre_master_secret",
|
||||
*pos, clen);
|
||||
*pos += clen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int tls_write_client_key_exchange(struct tlsv1_client *conn,
|
||||
u8 **msgpos, u8 *end)
|
||||
{
|
||||
u8 *pos, *rhdr, *hs_start, *hs_length;
|
||||
size_t rlen;
|
||||
tls_key_exchange keyx;
|
||||
const struct tls_cipher_suite *suite;
|
||||
|
||||
suite = tls_get_cipher_suite(conn->rl.cipher_suite);
|
||||
if (suite == NULL)
|
||||
keyx = TLS_KEY_X_NULL;
|
||||
else
|
||||
keyx = suite->key_exchange;
|
||||
|
||||
pos = *msgpos;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Send ClientKeyExchange");
|
||||
|
||||
rhdr = pos;
|
||||
pos += TLS_RECORD_HEADER_LEN;
|
||||
|
||||
/* opaque fragment[TLSPlaintext.length] */
|
||||
|
||||
/* Handshake */
|
||||
hs_start = pos;
|
||||
/* HandshakeType msg_type */
|
||||
*pos++ = TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE;
|
||||
/* uint24 length (to be filled) */
|
||||
hs_length = pos;
|
||||
pos += 3;
|
||||
/* body - ClientKeyExchange */
|
||||
if (keyx == TLS_KEY_X_DH_anon) {
|
||||
if (tlsv1_key_x_anon_dh(conn, &pos, end) < 0)
|
||||
return -1;
|
||||
} else {
|
||||
if (tlsv1_key_x_rsa(conn, &pos, end) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
|
||||
|
||||
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
|
||||
rhdr, end - rhdr, hs_start, pos - hs_start,
|
||||
&rlen) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
pos = rhdr + rlen;
|
||||
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
|
||||
|
||||
*msgpos = pos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
|
||||
u8 **msgpos, u8 *end)
|
||||
{
|
||||
u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start;
|
||||
size_t rlen, hlen, clen;
|
||||
u8 hash[100], *hpos;
|
||||
enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
|
||||
|
||||
pos = *msgpos;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateVerify");
|
||||
rhdr = pos;
|
||||
pos += TLS_RECORD_HEADER_LEN;
|
||||
|
||||
/* Handshake */
|
||||
hs_start = pos;
|
||||
/* HandshakeType msg_type */
|
||||
*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY;
|
||||
/* uint24 length (to be filled) */
|
||||
hs_length = pos;
|
||||
pos += 3;
|
||||
|
||||
/*
|
||||
* RFC 2246: 7.4.3 and 7.4.8:
|
||||
* Signature signature
|
||||
*
|
||||
* RSA:
|
||||
* digitally-signed struct {
|
||||
* opaque md5_hash[16];
|
||||
* opaque sha_hash[20];
|
||||
* };
|
||||
*
|
||||
* DSA:
|
||||
* digitally-signed struct {
|
||||
* opaque sha_hash[20];
|
||||
* };
|
||||
*
|
||||
* The hash values are calculated over all handshake messages sent or
|
||||
* received starting at ClientHello up to, but not including, this
|
||||
* CertificateVerify message, including the type and length fields of
|
||||
* the handshake messages.
|
||||
*/
|
||||
|
||||
hpos = hash;
|
||||
|
||||
#ifdef CONFIG_TLSV12
|
||||
if (conn->rl.tls_version == TLS_VERSION_1_2) {
|
||||
hlen = SHA256_MAC_LEN;
|
||||
if (wpa2_crypto_funcs.crypto_hash_finish) {
|
||||
if (conn->verify.sha256_cert == NULL ||
|
||||
wpa2_crypto_funcs.crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) <
|
||||
0) {
|
||||
conn->verify.sha256_cert = NULL;
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
conn->verify.sha256_cert = NULL;
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
conn->verify.sha256_cert = NULL;
|
||||
|
||||
/*
|
||||
* RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
|
||||
*
|
||||
* DigestInfo ::= SEQUENCE {
|
||||
* digestAlgorithm DigestAlgorithm,
|
||||
* digest OCTET STRING
|
||||
* }
|
||||
*
|
||||
* SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
|
||||
*
|
||||
* DER encoded DigestInfo for SHA256 per RFC 3447:
|
||||
* 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||
|
||||
* H
|
||||
*/
|
||||
os_memmove(hash + 19, hash, hlen);
|
||||
hlen += 19;
|
||||
os_memcpy(hash, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65"
|
||||
"\x03\x04\x02\x01\x05\x00\x04\x20", 19);
|
||||
} else {
|
||||
#endif /* CONFIG_TLSV12 */
|
||||
|
||||
if (alg == SIGN_ALG_RSA) {
|
||||
hlen = MD5_MAC_LEN;
|
||||
if (conn->verify.md5_cert == NULL ||
|
||||
crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0)
|
||||
{
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
conn->verify.md5_cert = NULL;
|
||||
crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
|
||||
conn->verify.sha1_cert = NULL;
|
||||
return -1;
|
||||
}
|
||||
hpos += MD5_MAC_LEN;
|
||||
} else
|
||||
crypto_hash_finish(conn->verify.md5_cert, NULL, NULL);
|
||||
|
||||
conn->verify.md5_cert = NULL;
|
||||
hlen = SHA1_MAC_LEN;
|
||||
if (conn->verify.sha1_cert == NULL ||
|
||||
crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) {
|
||||
conn->verify.sha1_cert = NULL;
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
conn->verify.sha1_cert = NULL;
|
||||
|
||||
if (alg == SIGN_ALG_RSA)
|
||||
hlen += MD5_MAC_LEN;
|
||||
|
||||
#ifdef CONFIG_TLSV12
|
||||
}
|
||||
#endif /* CONFIG_TLSV12 */
|
||||
|
||||
wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
|
||||
|
||||
#ifdef CONFIG_TLSV12
|
||||
if (conn->rl.tls_version >= TLS_VERSION_1_2) {
|
||||
/*
|
||||
* RFC 5246, 4.7:
|
||||
* TLS v1.2 adds explicit indication of the used signature and
|
||||
* hash algorithms.
|
||||
*
|
||||
* struct {
|
||||
* HashAlgorithm hash;
|
||||
* SignatureAlgorithm signature;
|
||||
* } SignatureAndHashAlgorithm;
|
||||
*/
|
||||
*pos++ = TLS_HASH_ALG_SHA256;
|
||||
*pos++ = TLS_SIGN_ALG_RSA;
|
||||
}
|
||||
#endif /* CONFIG_TLSV12 */
|
||||
|
||||
/*
|
||||
* RFC 2246, 4.7:
|
||||
* In digital signing, one-way hash functions are used as input for a
|
||||
* signing algorithm. A digitally-signed element is encoded as an
|
||||
* opaque vector <0..2^16-1>, where the length is specified by the
|
||||
* signing algorithm and key.
|
||||
*
|
||||
* In RSA signing, a 36-byte structure of two hashes (one SHA and one
|
||||
* MD5) is signed (encrypted with the private key). It is encoded with
|
||||
* PKCS #1 block type 0 or type 1 as described in [PKCS1].
|
||||
*/
|
||||
signed_start = pos; /* length to be filled */
|
||||
pos += 2;
|
||||
clen = end - pos;
|
||||
if (conn->cred == NULL ||
|
||||
crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen,
|
||||
pos, &clen) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
WPA_PUT_BE16(signed_start, clen);
|
||||
|
||||
pos += clen;
|
||||
|
||||
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
|
||||
|
||||
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
|
||||
rhdr, end - rhdr, hs_start, pos - hs_start,
|
||||
&rlen) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
pos = rhdr + rlen;
|
||||
|
||||
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
|
||||
|
||||
*msgpos = pos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn,
|
||||
u8 **msgpos, u8 *end)
|
||||
{
|
||||
size_t rlen;
|
||||
u8 payload[1];
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
|
||||
|
||||
payload[0] = TLS_CHANGE_CIPHER_SPEC;
|
||||
|
||||
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
|
||||
*msgpos, end - *msgpos, payload, sizeof(payload),
|
||||
&rlen) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tlsv1_record_change_write_cipher(&conn->rl) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for "
|
||||
"record layer");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*msgpos += rlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int tls_write_client_finished(struct tlsv1_client *conn,
|
||||
u8 **msgpos, u8 *end)
|
||||
{
|
||||
u8 *pos, *hs_start;
|
||||
size_t rlen, hlen;
|
||||
u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
|
||||
u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
|
||||
|
||||
/* Encrypted Handshake Message: Finished */
|
||||
|
||||
#ifdef CONFIG_TLSV12
|
||||
if (conn->rl.tls_version >= TLS_VERSION_1_2) {
|
||||
hlen = SHA256_MAC_LEN;
|
||||
if (wpa2_crypto_funcs.crypto_hash_finish) {
|
||||
if (conn->verify.sha256_client == NULL ||
|
||||
wpa2_crypto_funcs.crypto_hash_finish(conn->verify.sha256_client, hash, &hlen)
|
||||
< 0) {
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
conn->verify.sha256_client = NULL;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
conn->verify.sha256_client = NULL;
|
||||
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
conn->verify.sha256_client = NULL;
|
||||
} else {
|
||||
#endif /* CONFIG_TLSV12 */
|
||||
|
||||
hlen = MD5_MAC_LEN;
|
||||
if (conn->verify.md5_client == NULL ||
|
||||
crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) {
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
conn->verify.md5_client = NULL;
|
||||
crypto_hash_finish(conn->verify.sha1_client, NULL, NULL);
|
||||
conn->verify.sha1_client = NULL;
|
||||
return -1;
|
||||
}
|
||||
conn->verify.md5_client = NULL;
|
||||
hlen = SHA1_MAC_LEN;
|
||||
if (conn->verify.sha1_client == NULL ||
|
||||
crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN,
|
||||
&hlen) < 0) {
|
||||
conn->verify.sha1_client = NULL;
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
conn->verify.sha1_client = NULL;
|
||||
hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
|
||||
|
||||
#ifdef CONFIG_TLSV12
|
||||
}
|
||||
#endif /* CONFIG_TLSV12 */
|
||||
|
||||
if (tls_prf(conn->rl.tls_version,
|
||||
conn->master_secret, TLS_MASTER_SECRET_LEN,
|
||||
"client finished", hash, hlen,
|
||||
verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
|
||||
verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
|
||||
|
||||
/* Handshake */
|
||||
pos = hs_start = verify_data;
|
||||
/* HandshakeType msg_type */
|
||||
*pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
|
||||
/* uint24 length */
|
||||
WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
|
||||
pos += 3;
|
||||
pos += TLS_VERIFY_DATA_LEN; /* verify_data already in place */
|
||||
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
|
||||
|
||||
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
|
||||
*msgpos, end - *msgpos, hs_start, pos - hs_start,
|
||||
&rlen) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
|
||||
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*msgpos += rlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static u8 * tls_send_client_key_exchange(struct tlsv1_client *conn,
|
||||
size_t *out_len)
|
||||
{
|
||||
u8 *msg, *end, *pos;
|
||||
size_t msglen;
|
||||
|
||||
*out_len = 0;
|
||||
|
||||
msglen = 2000;
|
||||
if (conn->certificate_requested)
|
||||
msglen += tls_client_cert_chain_der_len(conn);
|
||||
|
||||
msg = os_malloc(msglen);
|
||||
if (msg == NULL)
|
||||
return NULL;
|
||||
|
||||
pos = msg;
|
||||
end = msg + msglen;
|
||||
|
||||
if (conn->certificate_requested) {
|
||||
if (tls_write_client_certificate(conn, &pos, end) < 0) {
|
||||
os_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (tls_write_client_key_exchange(conn, &pos, end) < 0 ||
|
||||
(conn->certificate_requested && conn->cred && conn->cred->key &&
|
||||
tls_write_client_certificate_verify(conn, &pos, end) < 0) ||
|
||||
tls_write_client_change_cipher_spec(conn, &pos, end) < 0 ||
|
||||
tls_write_client_finished(conn, &pos, end) < 0) {
|
||||
os_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*out_len = pos - msg;
|
||||
conn->state = SERVER_CHANGE_CIPHER_SPEC;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
static u8 * tls_send_change_cipher_spec(struct tlsv1_client *conn,
|
||||
size_t *out_len)
|
||||
{
|
||||
u8 *msg, *end, *pos;
|
||||
|
||||
*out_len = 0;
|
||||
|
||||
msg = os_malloc(1000);
|
||||
if (msg == NULL)
|
||||
return NULL;
|
||||
|
||||
pos = msg;
|
||||
end = msg + 1000;
|
||||
|
||||
if (tls_write_client_change_cipher_spec(conn, &pos, end) < 0 ||
|
||||
tls_write_client_finished(conn, &pos, end) < 0) {
|
||||
os_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*out_len = pos - msg;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Session resumption completed "
|
||||
"successfully");
|
||||
|
||||
conn->state = ESTABLISHED;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len,
|
||||
int no_appl_data)
|
||||
{
|
||||
switch (conn->state) {
|
||||
case CLIENT_KEY_EXCHANGE:
|
||||
return tls_send_client_key_exchange(conn, out_len);
|
||||
case CHANGE_CIPHER_SPEC:
|
||||
return tls_send_change_cipher_spec(conn, out_len);
|
||||
case ACK_FINISHED:
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed "
|
||||
"successfully");
|
||||
conn->state = ESTABLISHED;
|
||||
*out_len = 0;
|
||||
if (no_appl_data) {
|
||||
/* Need to return something to get final TLS ACK. */
|
||||
return os_malloc(1);
|
||||
}
|
||||
return NULL;
|
||||
default:
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while "
|
||||
"generating reply", conn->state);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level,
|
||||
u8 description, size_t *out_len)
|
||||
{
|
||||
u8 *alert, *pos, *length;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description);
|
||||
*out_len = 0;
|
||||
|
||||
alert = os_malloc(10);
|
||||
if (alert == NULL)
|
||||
return NULL;
|
||||
|
||||
pos = alert;
|
||||
|
||||
/* TLSPlaintext */
|
||||
/* ContentType type */
|
||||
*pos++ = TLS_CONTENT_TYPE_ALERT;
|
||||
/* ProtocolVersion version */
|
||||
WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version :
|
||||
TLS_VERSION);
|
||||
pos += 2;
|
||||
/* uint16 length (to be filled) */
|
||||
length = pos;
|
||||
pos += 2;
|
||||
/* opaque fragment[TLSPlaintext.length] */
|
||||
|
||||
/* Alert */
|
||||
/* AlertLevel level */
|
||||
*pos++ = level;
|
||||
/* AlertDescription description */
|
||||
*pos++ = description;
|
||||
|
||||
WPA_PUT_BE16(length, pos - length - 2);
|
||||
*out_len = pos - alert;
|
||||
|
||||
return alert;
|
||||
}
|
|
@ -0,0 +1,337 @@
|
|||
/*
|
||||
* TLSv1 common routines
|
||||
* Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "crypto/sha1.h"
|
||||
#include "crypto/sha256.h"
|
||||
#include "wpa2/tls/tls.h"
|
||||
#include "wpa2/tls/x509v3.h"
|
||||
#include "wpa2/tls/tlsv1_common.h"
|
||||
#include "wpa2/eap_peer/eap_i.h"
|
||||
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* RFC 2246 Section 9: Mandatory to implement TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
|
||||
* Add support for commonly used cipher suites; don't bother with exportable
|
||||
* suites.
|
||||
*/
|
||||
|
||||
static const struct tls_cipher_suite tls_cipher_suites[] = {
|
||||
{ TLS_NULL_WITH_NULL_NULL, TLS_KEY_X_NULL, TLS_CIPHER_NULL,
|
||||
TLS_HASH_NULL },
|
||||
{ TLS_RSA_WITH_RC4_128_MD5, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128,
|
||||
TLS_HASH_MD5 },
|
||||
{ TLS_RSA_WITH_RC4_128_SHA, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128,
|
||||
TLS_HASH_SHA },
|
||||
{ TLS_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_DES_CBC,
|
||||
TLS_HASH_SHA },
|
||||
{ TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_RSA,
|
||||
TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
|
||||
{ TLS_DH_anon_WITH_RC4_128_MD5, TLS_KEY_X_DH_anon,
|
||||
TLS_CIPHER_RC4_128, TLS_HASH_MD5 },
|
||||
{ TLS_DH_anon_WITH_DES_CBC_SHA, TLS_KEY_X_DH_anon,
|
||||
TLS_CIPHER_DES_CBC, TLS_HASH_SHA },
|
||||
{ TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DH_anon,
|
||||
TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
|
||||
{ TLS_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC,
|
||||
TLS_HASH_SHA },
|
||||
{ TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_KEY_X_DH_anon,
|
||||
TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA },
|
||||
{ TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC,
|
||||
TLS_HASH_SHA },
|
||||
{ TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon,
|
||||
TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA },
|
||||
{ TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_RSA,
|
||||
TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
|
||||
{ TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_RSA,
|
||||
TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 },
|
||||
{ TLS_DH_anon_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DH_anon,
|
||||
TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
|
||||
{ TLS_DH_anon_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DH_anon,
|
||||
TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }
|
||||
};
|
||||
|
||||
#define NUM_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
|
||||
#define NUM_TLS_CIPHER_SUITES NUM_ELEMS(tls_cipher_suites)
|
||||
|
||||
|
||||
static const struct tls_cipher_data tls_ciphers[] = {
|
||||
{ TLS_CIPHER_NULL, TLS_CIPHER_STREAM, 0, 0, 0,
|
||||
CRYPTO_CIPHER_NULL },
|
||||
{ TLS_CIPHER_IDEA_CBC, TLS_CIPHER_BLOCK, 16, 16, 8,
|
||||
CRYPTO_CIPHER_NULL },
|
||||
{ TLS_CIPHER_RC2_CBC_40, TLS_CIPHER_BLOCK, 5, 16, 0,
|
||||
CRYPTO_CIPHER_ALG_RC2 },
|
||||
{ TLS_CIPHER_RC4_40, TLS_CIPHER_STREAM, 5, 16, 0,
|
||||
CRYPTO_CIPHER_ALG_RC4 },
|
||||
{ TLS_CIPHER_RC4_128, TLS_CIPHER_STREAM, 16, 16, 0,
|
||||
CRYPTO_CIPHER_ALG_RC4 },
|
||||
{ TLS_CIPHER_DES40_CBC, TLS_CIPHER_BLOCK, 5, 8, 8,
|
||||
CRYPTO_CIPHER_ALG_DES },
|
||||
{ TLS_CIPHER_DES_CBC, TLS_CIPHER_BLOCK, 8, 8, 8,
|
||||
CRYPTO_CIPHER_ALG_DES },
|
||||
{ TLS_CIPHER_3DES_EDE_CBC, TLS_CIPHER_BLOCK, 24, 24, 8,
|
||||
CRYPTO_CIPHER_ALG_3DES },
|
||||
{ TLS_CIPHER_AES_128_CBC, TLS_CIPHER_BLOCK, 16, 16, 16,
|
||||
CRYPTO_CIPHER_ALG_AES },
|
||||
{ TLS_CIPHER_AES_256_CBC, TLS_CIPHER_BLOCK, 32, 32, 16,
|
||||
CRYPTO_CIPHER_ALG_AES }
|
||||
};
|
||||
|
||||
#define NUM_TLS_CIPHER_DATA NUM_ELEMS(tls_ciphers)
|
||||
|
||||
|
||||
/**
|
||||
* tls_get_cipher_suite - Get TLS cipher suite
|
||||
* @suite: Cipher suite identifier
|
||||
* Returns: Pointer to the cipher data or %NULL if not found
|
||||
*/
|
||||
const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < NUM_TLS_CIPHER_SUITES; i++)
|
||||
if (tls_cipher_suites[i].suite == suite)
|
||||
return &tls_cipher_suites[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < NUM_TLS_CIPHER_DATA; i++)
|
||||
if (tls_ciphers[i].cipher == cipher)
|
||||
return &tls_ciphers[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int tls_server_key_exchange_allowed(tls_cipher cipher)
|
||||
{
|
||||
const struct tls_cipher_suite *suite;
|
||||
|
||||
/* RFC 2246, Section 7.4.3 */
|
||||
suite = tls_get_cipher_suite(cipher);
|
||||
if (suite == NULL)
|
||||
return 0;
|
||||
|
||||
switch (suite->key_exchange) {
|
||||
case TLS_KEY_X_DHE_DSS:
|
||||
case TLS_KEY_X_DHE_DSS_EXPORT:
|
||||
case TLS_KEY_X_DHE_RSA:
|
||||
case TLS_KEY_X_DHE_RSA_EXPORT:
|
||||
case TLS_KEY_X_DH_anon_EXPORT:
|
||||
case TLS_KEY_X_DH_anon:
|
||||
return 1;
|
||||
case TLS_KEY_X_RSA_EXPORT:
|
||||
return 1 /* FIX: public key len > 512 bits */;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tls_parse_cert - Parse DER encoded X.509 certificate and get public key
|
||||
* @buf: ASN.1 DER encoded certificate
|
||||
* @len: Length of the buffer
|
||||
* @pk: Buffer for returning the allocated public key
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*
|
||||
* This functions parses an ASN.1 DER encoded X.509 certificate and retrieves
|
||||
* the public key from it. The caller is responsible for freeing the public key
|
||||
* by calling crypto_public_key_free().
|
||||
*/
|
||||
int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk)
|
||||
{
|
||||
struct x509_certificate *cert;
|
||||
|
||||
wpa_hexdump(MSG_MSGDUMP, "TLSv1: Parse ASN.1 DER certificate",
|
||||
buf, len);
|
||||
|
||||
*pk = crypto_public_key_from_cert(buf, len);
|
||||
if (*pk)
|
||||
return 0;
|
||||
|
||||
cert = x509_certificate_parse(buf, len);
|
||||
if (cert == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse X.509 "
|
||||
"certificate");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* TODO
|
||||
* verify key usage (must allow encryption)
|
||||
*
|
||||
* All certificate profiles, key and cryptographic formats are
|
||||
* defined by the IETF PKIX working group [PKIX]. When a key
|
||||
* usage extension is present, the digitalSignature bit must be
|
||||
* set for the key to be eligible for signing, as described
|
||||
* above, and the keyEncipherment bit must be present to allow
|
||||
* encryption, as described above. The keyAgreement bit must be
|
||||
* set on Diffie-Hellman certificates. (PKIX: RFC 3280)
|
||||
*/
|
||||
|
||||
*pk = crypto_public_key_import(cert->public_key, cert->public_key_len);
|
||||
x509_certificate_free(cert);
|
||||
|
||||
if (*pk == NULL) {
|
||||
wpa_printf(MSG_ERROR, "TLSv1: Failed to import "
|
||||
"server public key");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int tls_verify_hash_init(struct tls_verify_hash *verify)
|
||||
{
|
||||
tls_verify_hash_free(verify);
|
||||
verify->md5_client = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
|
||||
verify->md5_server = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
|
||||
verify->md5_cert = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
|
||||
verify->sha1_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
|
||||
verify->sha1_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
|
||||
verify->sha1_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
|
||||
if (verify->md5_client == NULL || verify->md5_server == NULL ||
|
||||
verify->md5_cert == NULL || verify->sha1_client == NULL ||
|
||||
verify->sha1_server == NULL || verify->sha1_cert == NULL) {
|
||||
tls_verify_hash_free(verify);
|
||||
return -1;
|
||||
}
|
||||
#ifdef CONFIG_TLSV12
|
||||
if (wpa2_crypto_funcs.crypto_hash_init) {
|
||||
verify->sha256_client = wpa2_crypto_funcs.crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
|
||||
verify->sha256_server = wpa2_crypto_funcs.crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
|
||||
verify->sha256_cert = wpa2_crypto_funcs.crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash init function!\r\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
if (verify->sha256_client == NULL ||
|
||||
verify->sha256_server == NULL ||
|
||||
verify->sha256_cert == NULL) {
|
||||
tls_verify_hash_free(verify);
|
||||
return -1;
|
||||
}
|
||||
#endif /* CONFIG_TLSV12 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf,
|
||||
size_t len)
|
||||
{
|
||||
if (verify->md5_client && verify->sha1_client) {
|
||||
crypto_hash_update(verify->md5_client, buf, len);
|
||||
crypto_hash_update(verify->sha1_client, buf, len);
|
||||
}
|
||||
if (verify->md5_server && verify->sha1_server) {
|
||||
crypto_hash_update(verify->md5_server, buf, len);
|
||||
crypto_hash_update(verify->sha1_server, buf, len);
|
||||
}
|
||||
if (verify->md5_cert && verify->sha1_cert) {
|
||||
crypto_hash_update(verify->md5_cert, buf, len);
|
||||
crypto_hash_update(verify->sha1_cert, buf, len);
|
||||
}
|
||||
#ifdef CONFIG_TLSV12
|
||||
if (wpa2_crypto_funcs.crypto_hash_update) {
|
||||
if (verify->sha256_client)
|
||||
wpa2_crypto_funcs.crypto_hash_update(verify->sha256_client, buf, len);
|
||||
if (verify->sha256_server)
|
||||
wpa2_crypto_funcs.crypto_hash_update(verify->sha256_server, buf, len);
|
||||
if (verify->sha256_cert)
|
||||
wpa2_crypto_funcs.crypto_hash_update(verify->sha256_cert, buf, len);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash update function!\r\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
#endif /* CONFIG_TLSV12 */
|
||||
}
|
||||
|
||||
|
||||
void tls_verify_hash_free(struct tls_verify_hash *verify)
|
||||
{
|
||||
crypto_hash_finish(verify->md5_client, NULL, NULL);
|
||||
crypto_hash_finish(verify->md5_server, NULL, NULL);
|
||||
crypto_hash_finish(verify->md5_cert, NULL, NULL);
|
||||
crypto_hash_finish(verify->sha1_client, NULL, NULL);
|
||||
crypto_hash_finish(verify->sha1_server, NULL, NULL);
|
||||
crypto_hash_finish(verify->sha1_cert, NULL, NULL);
|
||||
verify->md5_client = NULL;
|
||||
verify->md5_server = NULL;
|
||||
verify->md5_cert = NULL;
|
||||
verify->sha1_client = NULL;
|
||||
verify->sha1_server = NULL;
|
||||
verify->sha1_cert = NULL;
|
||||
#ifdef CONFIG_TLSV12
|
||||
if (wpa2_crypto_funcs.crypto_hash_finish) {
|
||||
wpa2_crypto_funcs.crypto_hash_finish(verify->sha256_client, NULL, NULL);
|
||||
wpa2_crypto_funcs.crypto_hash_finish(verify->sha256_server, NULL, NULL);
|
||||
wpa2_crypto_funcs.crypto_hash_finish(verify->sha256_cert, NULL, NULL);
|
||||
verify->sha256_client = NULL;
|
||||
verify->sha256_server = NULL;
|
||||
verify->sha256_cert = NULL;
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash finish function!\r\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
#endif /* CONFIG_TLSV12 */
|
||||
}
|
||||
|
||||
|
||||
int tls_version_ok(u16 ver)
|
||||
{
|
||||
if (ver == TLS_VERSION_1)
|
||||
return 1;
|
||||
#ifdef CONFIG_TLSV11
|
||||
if (ver == TLS_VERSION_1_1)
|
||||
return 1;
|
||||
#endif /* CONFIG_TLSV11 */
|
||||
#ifdef CONFIG_TLSV12
|
||||
if (ver == TLS_VERSION_1_2)
|
||||
return 1;
|
||||
#endif /* CONFIG_TLSV12 */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
const char * tls_version_str(u16 ver)
|
||||
{
|
||||
switch (ver) {
|
||||
case TLS_VERSION_1:
|
||||
return "1.0";
|
||||
case TLS_VERSION_1_1:
|
||||
return "1.1";
|
||||
case TLS_VERSION_1_2:
|
||||
return "1.2";
|
||||
}
|
||||
|
||||
return "?";
|
||||
}
|
||||
|
||||
|
||||
int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label,
|
||||
const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
|
||||
{
|
||||
#ifdef CONFIG_TLSV12
|
||||
if (ver >= TLS_VERSION_1_2) {
|
||||
tls_prf_sha256(secret, secret_len, label, seed, seed_len,
|
||||
out, outlen);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_TLSV12 */
|
||||
|
||||
return tls_prf_sha1_md5(secret, secret_len, label, seed, seed_len, out,
|
||||
outlen);
|
||||
}
|
|
@ -0,0 +1,505 @@
|
|||
/*
|
||||
* TLSv1 credentials
|
||||
* Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "wpa2/utils/base64.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "wpa2/tls/x509v3.h"
|
||||
#include "wpa2/tls/tlsv1_cred.h"
|
||||
|
||||
struct tlsv1_credentials * tlsv1_cred_alloc(void)
|
||||
{
|
||||
struct tlsv1_credentials *cred;
|
||||
cred = (struct tlsv1_credentials *)os_zalloc(sizeof(*cred));
|
||||
return cred;
|
||||
}
|
||||
|
||||
|
||||
void tlsv1_cred_free(struct tlsv1_credentials *cred)
|
||||
{
|
||||
if (cred == NULL)
|
||||
return;
|
||||
|
||||
x509_certificate_chain_free(cred->trusted_certs);
|
||||
x509_certificate_chain_free(cred->cert);
|
||||
crypto_private_key_free(cred->key);
|
||||
os_free(cred->dh_p);
|
||||
os_free(cred->dh_g);
|
||||
os_free(cred);
|
||||
}
|
||||
|
||||
|
||||
static int tlsv1_add_cert_der(struct x509_certificate **chain,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
struct x509_certificate *cert, *p;
|
||||
char name[128];
|
||||
|
||||
cert = x509_certificate_parse(buf, len);
|
||||
if (cert == NULL) {
|
||||
wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = *chain;
|
||||
while (p && p->next)
|
||||
p = p->next;
|
||||
if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) {
|
||||
/*
|
||||
* The new certificate is the issuer of the last certificate in
|
||||
* the chain - add the new certificate to the end.
|
||||
*/
|
||||
p->next = cert;
|
||||
} else {
|
||||
/* Add to the beginning of the chain */
|
||||
cert->next = *chain;
|
||||
*chain = cert;
|
||||
}
|
||||
|
||||
x509_name_string(&cert->subject, name, sizeof(name));
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----";
|
||||
static const char *pem_cert_end = "-----END CERTIFICATE-----";
|
||||
static const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
|
||||
static const char *pem_key_end = "-----END RSA PRIVATE KEY-----";
|
||||
static const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----";
|
||||
static const char *pem_key2_end = "-----END PRIVATE KEY-----";
|
||||
static const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
|
||||
static const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----";
|
||||
|
||||
|
||||
static const u8 * search_tag(const char *tag, const u8 *buf, size_t len)
|
||||
{
|
||||
size_t i, plen;
|
||||
|
||||
plen = os_strlen(tag);
|
||||
if (len < plen)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < len - plen; i++) {
|
||||
if (os_memcmp(buf + i, tag, plen) == 0)
|
||||
return buf + i;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int tlsv1_add_cert(struct x509_certificate **chain,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
const u8 *pos, *end;
|
||||
unsigned char *der;
|
||||
size_t der_len;
|
||||
|
||||
pos = search_tag(pem_cert_begin, buf, len);
|
||||
if (!pos) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - "
|
||||
"assume DER format");
|
||||
return tlsv1_add_cert_der(chain, buf, len);
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into "
|
||||
"DER format");
|
||||
|
||||
while (pos) {
|
||||
pos += os_strlen(pem_cert_begin);
|
||||
end = search_tag(pem_cert_end, pos, buf + len - pos);
|
||||
if (end == NULL) {
|
||||
wpa_printf(MSG_INFO, "TLSv1: Could not find PEM "
|
||||
"certificate end tag (%s)", pem_cert_end);
|
||||
return -1;
|
||||
}
|
||||
|
||||
der = base64_decode(pos, end - pos, &der_len);
|
||||
if (der == NULL) {
|
||||
wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM "
|
||||
"certificate");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tlsv1_add_cert_der(chain, der, der_len) < 0) {
|
||||
wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM "
|
||||
"certificate after DER conversion");
|
||||
os_free(der);
|
||||
return -1;
|
||||
}
|
||||
|
||||
os_free(der);
|
||||
|
||||
end += os_strlen(pem_cert_end);
|
||||
pos = search_tag(pem_cert_begin, end, buf + len - end);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int tlsv1_set_cert_chain(struct x509_certificate **chain,
|
||||
const char *cert, const u8 *cert_blob,
|
||||
size_t cert_blob_len)
|
||||
{
|
||||
if (cert_blob)
|
||||
return tlsv1_add_cert(chain, cert_blob, cert_blob_len);
|
||||
|
||||
if (cert) {
|
||||
u8 *buf = NULL;
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
//buf = (u8 *) os_readfile(cert, &len);
|
||||
if (buf == NULL) {
|
||||
wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
|
||||
cert);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = tlsv1_add_cert(chain, buf, len);
|
||||
os_free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_set_ca_cert - Set trusted CA certificate(s)
|
||||
* @cred: TLSv1 credentials from tlsv1_cred_alloc()
|
||||
* @cert: File or reference name for X.509 certificate in PEM or DER format
|
||||
* @cert_blob: cert as inlined data or %NULL if not used
|
||||
* @cert_blob_len: ca_cert_blob length
|
||||
* @path: Path to CA certificates (not yet supported)
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
|
||||
const u8 *cert_blob, size_t cert_blob_len,
|
||||
const char *path)
|
||||
{
|
||||
if (tlsv1_set_cert_chain(&cred->trusted_certs, cert,
|
||||
cert_blob, cert_blob_len) < 0)
|
||||
return -1;
|
||||
|
||||
if (path) {
|
||||
/* TODO: add support for reading number of certificate files */
|
||||
wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory "
|
||||
"not yet supported");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_set_cert - Set certificate
|
||||
* @cred: TLSv1 credentials from tlsv1_cred_alloc()
|
||||
* @cert: File or reference name for X.509 certificate in PEM or DER format
|
||||
* @cert_blob: cert as inlined data or %NULL if not used
|
||||
* @cert_blob_len: cert_blob length
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
|
||||
const u8 *cert_blob, size_t cert_blob_len)
|
||||
{
|
||||
return tlsv1_set_cert_chain(&cred->cert, cert,
|
||||
cert_blob, cert_blob_len);
|
||||
}
|
||||
|
||||
|
||||
static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len)
|
||||
{
|
||||
const u8 *pos, *end;
|
||||
unsigned char *der;
|
||||
size_t der_len;
|
||||
struct crypto_private_key *pkey;
|
||||
|
||||
pos = search_tag(pem_key_begin, key, len);
|
||||
if (!pos) {
|
||||
pos = search_tag(pem_key2_begin, key, len);
|
||||
if (!pos)
|
||||
return NULL;
|
||||
pos += os_strlen(pem_key2_begin);
|
||||
end = search_tag(pem_key2_end, pos, key + len - pos);
|
||||
if (!end)
|
||||
return NULL;
|
||||
} else {
|
||||
const u8 *pos2;
|
||||
pos += os_strlen(pem_key_begin);
|
||||
end = search_tag(pem_key_end, pos, key + len - pos);
|
||||
if (!end)
|
||||
return NULL;
|
||||
pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos);
|
||||
if (pos2) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key "
|
||||
"format (Proc-Type/DEK-Info)");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
der = base64_decode(pos, end - pos, &der_len);
|
||||
if (!der)
|
||||
return NULL;
|
||||
pkey = crypto_private_key_import(der, der_len, NULL);
|
||||
os_free(der);
|
||||
return pkey;
|
||||
}
|
||||
|
||||
|
||||
static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key,
|
||||
size_t len,
|
||||
const char *passwd)
|
||||
{
|
||||
const u8 *pos, *end;
|
||||
unsigned char *der;
|
||||
size_t der_len;
|
||||
struct crypto_private_key *pkey;
|
||||
|
||||
if (passwd == NULL)
|
||||
return NULL;
|
||||
pos = search_tag(pem_key_enc_begin, key, len);
|
||||
if (!pos)
|
||||
return NULL;
|
||||
pos += os_strlen(pem_key_enc_begin);
|
||||
end = search_tag(pem_key_enc_end, pos, key + len - pos);
|
||||
if (!end)
|
||||
return NULL;
|
||||
|
||||
der = base64_decode(pos, end - pos, &der_len);
|
||||
if (!der)
|
||||
return NULL;
|
||||
pkey = crypto_private_key_import(der, der_len, passwd);
|
||||
os_free(der);
|
||||
return pkey;
|
||||
}
|
||||
|
||||
|
||||
static int tlsv1_set_key(struct tlsv1_credentials *cred,
|
||||
const u8 *key, size_t len, const char *passwd)
|
||||
{
|
||||
cred->key = crypto_private_key_import(key, len, passwd);
|
||||
if (cred->key == NULL)
|
||||
cred->key = tlsv1_set_key_pem(key, len);
|
||||
if (cred->key == NULL)
|
||||
cred->key = tlsv1_set_key_enc_pem(key, len, passwd);
|
||||
if (cred->key == NULL) {
|
||||
wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_set_private_key - Set private key
|
||||
* @cred: TLSv1 credentials from tlsv1_cred_alloc()
|
||||
* @private_key: File or reference name for the key in PEM or DER format
|
||||
* @private_key_passwd: Passphrase for decrypted private key, %NULL if no
|
||||
* passphrase is used.
|
||||
* @private_key_blob: private_key as inlined data or %NULL if not used
|
||||
* @private_key_blob_len: private_key_blob length
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int tlsv1_set_private_key(struct tlsv1_credentials *cred,
|
||||
const char *private_key,
|
||||
const char *private_key_passwd,
|
||||
const u8 *private_key_blob,
|
||||
size_t private_key_blob_len)
|
||||
{
|
||||
crypto_private_key_free(cred->key);
|
||||
cred->key = NULL;
|
||||
|
||||
if (private_key_blob)
|
||||
return tlsv1_set_key(cred, private_key_blob,
|
||||
private_key_blob_len,
|
||||
private_key_passwd);
|
||||
|
||||
if (private_key) {
|
||||
u8 *buf = NULL;
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
//buf = (u8 *) os_readfile(private_key, &len);
|
||||
if (buf == NULL) {
|
||||
wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
|
||||
private_key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = tlsv1_set_key(cred, buf, len, private_key_passwd);
|
||||
os_free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred,
|
||||
const u8 *dh, size_t len)
|
||||
{
|
||||
struct asn1_hdr hdr;
|
||||
const u8 *pos, *end;
|
||||
|
||||
pos = dh;
|
||||
end = dh + len;
|
||||
|
||||
/*
|
||||
* DHParameter ::= SEQUENCE {
|
||||
* prime INTEGER, -- p
|
||||
* base INTEGER, -- g
|
||||
* privateValueLength INTEGER OPTIONAL }
|
||||
*/
|
||||
|
||||
/* DHParamer ::= SEQUENCE */
|
||||
if (asn1_get_next(pos, len, &hdr) < 0 ||
|
||||
hdr.class != ASN1_CLASS_UNIVERSAL ||
|
||||
hdr.tag != ASN1_TAG_SEQUENCE) {
|
||||
wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a "
|
||||
"valid SEQUENCE - found class %d tag 0x%x",
|
||||
hdr.class, hdr.tag);
|
||||
return -1;
|
||||
}
|
||||
pos = hdr.payload;
|
||||
|
||||
/* prime INTEGER */
|
||||
if (asn1_get_next(pos, end - pos, &hdr) < 0)
|
||||
return -1;
|
||||
|
||||
if (hdr.class != ASN1_CLASS_UNIVERSAL ||
|
||||
hdr.tag != ASN1_TAG_INTEGER) {
|
||||
wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; "
|
||||
"class=%d tag=0x%x", hdr.class, hdr.tag);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length);
|
||||
if (hdr.length == 0)
|
||||
return -1;
|
||||
os_free(cred->dh_p);
|
||||
cred->dh_p = os_malloc(hdr.length);
|
||||
if (cred->dh_p == NULL)
|
||||
return -1;
|
||||
os_memcpy(cred->dh_p, hdr.payload, hdr.length);
|
||||
cred->dh_p_len = hdr.length;
|
||||
pos = hdr.payload + hdr.length;
|
||||
|
||||
/* base INTEGER */
|
||||
if (asn1_get_next(pos, end - pos, &hdr) < 0)
|
||||
return -1;
|
||||
|
||||
if (hdr.class != ASN1_CLASS_UNIVERSAL ||
|
||||
hdr.tag != ASN1_TAG_INTEGER) {
|
||||
wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; "
|
||||
"class=%d tag=0x%x", hdr.class, hdr.tag);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length);
|
||||
if (hdr.length == 0)
|
||||
return -1;
|
||||
os_free(cred->dh_g);
|
||||
cred->dh_g = os_malloc(hdr.length);
|
||||
if (cred->dh_g == NULL)
|
||||
return -1;
|
||||
os_memcpy(cred->dh_g, hdr.payload, hdr.length);
|
||||
cred->dh_g_len = hdr.length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----";
|
||||
static const char *pem_dhparams_end = "-----END DH PARAMETERS-----";
|
||||
|
||||
|
||||
static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
const u8 *pos, *end;
|
||||
unsigned char *der;
|
||||
size_t der_len;
|
||||
|
||||
pos = search_tag(pem_dhparams_begin, buf, len);
|
||||
if (!pos) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - "
|
||||
"assume DER format");
|
||||
return tlsv1_set_dhparams_der(cred, buf, len);
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER "
|
||||
"format");
|
||||
|
||||
pos += os_strlen(pem_dhparams_begin);
|
||||
end = search_tag(pem_dhparams_end, pos, buf + len - pos);
|
||||
if (end == NULL) {
|
||||
wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end "
|
||||
"tag (%s)", pem_dhparams_end);
|
||||
return -1;
|
||||
}
|
||||
|
||||
der = base64_decode(pos, end - pos, &der_len);
|
||||
if (der == NULL) {
|
||||
wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) {
|
||||
wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams "
|
||||
"DER conversion");
|
||||
os_free(der);
|
||||
return -1;
|
||||
}
|
||||
|
||||
os_free(der);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_set_dhparams - Set Diffie-Hellman parameters
|
||||
* @cred: TLSv1 credentials from tlsv1_cred_alloc()
|
||||
* @dh_file: File or reference name for the DH params in PEM or DER format
|
||||
* @dh_blob: DH params as inlined data or %NULL if not used
|
||||
* @dh_blob_len: dh_blob length
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
|
||||
const u8 *dh_blob, size_t dh_blob_len)
|
||||
{
|
||||
if (dh_blob)
|
||||
return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len);
|
||||
|
||||
if (dh_file) {
|
||||
u8 *buf = NULL;
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
//buf = (u8 *) os_readfile(dh_file, &len);
|
||||
if (buf == NULL) {
|
||||
wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
|
||||
dh_file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = tlsv1_set_dhparams_blob(cred, buf, len);
|
||||
os_free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,553 @@
|
|||
/*
|
||||
* TLSv1 Record Protocol
|
||||
* Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "crypto/md5.h"
|
||||
#include "crypto/sha1.h"
|
||||
#include "crypto/sha256.h"
|
||||
#include "wpa2/tls/tlsv1_common.h"
|
||||
#include "wpa2/tls/tlsv1_record.h"
|
||||
|
||||
#include "wpa2/eap_peer/eap_i.h"
|
||||
|
||||
/**
|
||||
* tlsv1_record_set_cipher_suite - TLS record layer: Set cipher suite
|
||||
* @rl: Pointer to TLS record layer data
|
||||
* @cipher_suite: New cipher suite
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*
|
||||
* This function is used to prepare TLS record layer for cipher suite change.
|
||||
* tlsv1_record_change_write_cipher() and
|
||||
* tlsv1_record_change_read_cipher() functions can then be used to change the
|
||||
* currently used ciphers.
|
||||
*/
|
||||
int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl,
|
||||
u16 cipher_suite)
|
||||
{
|
||||
const struct tls_cipher_suite *suite;
|
||||
const struct tls_cipher_data *data;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Selected cipher suite: 0x%04x",
|
||||
cipher_suite);
|
||||
rl->cipher_suite = cipher_suite;
|
||||
|
||||
suite = tls_get_cipher_suite(cipher_suite);
|
||||
if (suite == NULL)
|
||||
return -1;
|
||||
|
||||
if (suite->hash == TLS_HASH_MD5) {
|
||||
rl->hash_alg = CRYPTO_HASH_ALG_HMAC_MD5;
|
||||
rl->hash_size = MD5_MAC_LEN;
|
||||
} else if (suite->hash == TLS_HASH_SHA) {
|
||||
rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1;
|
||||
rl->hash_size = SHA1_MAC_LEN;
|
||||
} else if (suite->hash == TLS_HASH_SHA256) {
|
||||
rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA256;
|
||||
rl->hash_size = SHA256_MAC_LEN;
|
||||
}
|
||||
|
||||
data = tls_get_cipher_data(suite->cipher);
|
||||
if (data == NULL)
|
||||
return -1;
|
||||
|
||||
rl->key_material_len = data->key_material;
|
||||
rl->iv_size = data->block_size;
|
||||
rl->cipher_alg = data->alg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_record_change_write_cipher - TLS record layer: Change write cipher
|
||||
* @rl: Pointer to TLS record layer data
|
||||
* Returns: 0 on success (cipher changed), -1 on failure
|
||||
*
|
||||
* This function changes TLS record layer to use the new cipher suite
|
||||
* configured with tlsv1_record_set_cipher_suite() for writing.
|
||||
*/
|
||||
int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New write cipher suite "
|
||||
"0x%04x", rl->cipher_suite);
|
||||
rl->write_cipher_suite = rl->cipher_suite;
|
||||
os_memset(rl->write_seq_num, 0, TLS_SEQ_NUM_LEN);
|
||||
|
||||
if (rl->write_cbc) {
|
||||
if (wpa2_crypto_funcs.crypto_cipher_deinit) {
|
||||
wpa2_crypto_funcs.crypto_cipher_deinit(rl->write_cbc);
|
||||
rl->write_cbc = NULL;
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (rl->cipher_alg != CRYPTO_CIPHER_NULL) {
|
||||
if (wpa2_crypto_funcs.crypto_cipher_init) {
|
||||
rl->write_cbc = wpa2_crypto_funcs.crypto_cipher_init(rl->cipher_alg,
|
||||
rl->write_iv, rl->write_key,
|
||||
rl->key_material_len);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "Fail to register crypto_cipher_init function!\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rl->write_cbc == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize "
|
||||
"cipher");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_record_change_read_cipher - TLS record layer: Change read cipher
|
||||
* @rl: Pointer to TLS record layer data
|
||||
* Returns: 0 on success (cipher changed), -1 on failure
|
||||
*
|
||||
* This function changes TLS record layer to use the new cipher suite
|
||||
* configured with tlsv1_record_set_cipher_suite() for reading.
|
||||
*/
|
||||
int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New read cipher suite "
|
||||
"0x%04x \n", rl->cipher_suite);
|
||||
rl->read_cipher_suite = rl->cipher_suite;
|
||||
os_memset(rl->read_seq_num, 0, TLS_SEQ_NUM_LEN);
|
||||
|
||||
if (rl->read_cbc) {
|
||||
if (wpa2_crypto_funcs.crypto_cipher_deinit) {
|
||||
wpa2_crypto_funcs.crypto_cipher_deinit(rl->read_cbc);
|
||||
rl->read_cbc = NULL;
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (rl->cipher_alg != CRYPTO_CIPHER_NULL) {
|
||||
if(wpa2_crypto_funcs.crypto_cipher_init) {
|
||||
rl->read_cbc = wpa2_crypto_funcs.crypto_cipher_init(rl->cipher_alg,
|
||||
rl->read_iv, rl->read_key,
|
||||
rl->key_material_len);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "Fail to register crypto_cipher_init function!\r\n");
|
||||
return -1;
|
||||
}
|
||||
if (rl->read_cbc == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize "
|
||||
"cipher");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_record_send - TLS record layer: Send a message
|
||||
* @rl: Pointer to TLS record layer data
|
||||
* @content_type: Content type (TLS_CONTENT_TYPE_*)
|
||||
* @buf: Buffer for the generated TLS message (needs to have extra space for
|
||||
* header, IV (TLS v1.1), and HMAC)
|
||||
* @buf_size: Maximum buf size
|
||||
* @payload: Payload to be sent
|
||||
* @payload_len: Length of the payload
|
||||
* @out_len: Buffer for returning the used buf length
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*
|
||||
* This function fills in the TLS record layer header, adds HMAC, and encrypts
|
||||
* the data using the current write cipher.
|
||||
*/
|
||||
int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
|
||||
size_t buf_size, const u8 *payload, size_t payload_len,
|
||||
size_t *out_len)
|
||||
{
|
||||
u8 *pos, *ct_start, *length, *cpayload;
|
||||
struct crypto_hash *hmac = NULL;
|
||||
size_t clen;
|
||||
int explicit_iv;
|
||||
|
||||
pos = buf;
|
||||
if (pos + TLS_RECORD_HEADER_LEN > buf + buf_size)
|
||||
return -1;
|
||||
|
||||
/* ContentType type */
|
||||
ct_start = pos;
|
||||
*pos++ = content_type;
|
||||
/* ProtocolVersion version */
|
||||
WPA_PUT_BE16(pos, rl->tls_version);
|
||||
pos += 2;
|
||||
/* uint16 length */
|
||||
length = pos;
|
||||
WPA_PUT_BE16(length, payload_len);
|
||||
pos += 2;
|
||||
|
||||
cpayload = pos;
|
||||
explicit_iv = rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL &&
|
||||
rl->iv_size && rl->tls_version >= TLS_VERSION_1_1;
|
||||
if (explicit_iv) {
|
||||
/* opaque IV[Cipherspec.block_length] */
|
||||
if (pos + rl->iv_size > buf + buf_size)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Use random number R per the RFC 4346, 6.2.3.2 CBC Block
|
||||
* Cipher option 2a.
|
||||
*/
|
||||
|
||||
if (os_get_random(pos, rl->iv_size))
|
||||
return -1;
|
||||
pos += rl->iv_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* opaque fragment[TLSPlaintext.length]
|
||||
* (opaque content[TLSCompressed.length] in GenericBlockCipher)
|
||||
*/
|
||||
if (pos + payload_len > buf + buf_size)
|
||||
return -1;
|
||||
os_memmove(pos, payload, payload_len);
|
||||
pos += payload_len;
|
||||
|
||||
if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
|
||||
/*
|
||||
* MAC calculated over seq_num + TLSCompressed.type +
|
||||
* TLSCompressed.version + TLSCompressed.length +
|
||||
* TLSCompressed.fragment
|
||||
*/
|
||||
if (wpa2_crypto_funcs.crypto_hash_init) {
|
||||
hmac = wpa2_crypto_funcs.crypto_hash_init(rl->hash_alg, rl->write_mac_secret, rl->hash_size);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash init!\r\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
if (hmac == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
|
||||
"to initialize HMAC");
|
||||
return -1;
|
||||
}
|
||||
if (wpa2_crypto_funcs.crypto_hash_update) {
|
||||
wpa2_crypto_funcs.crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN);
|
||||
/* type + version + length + fragment */
|
||||
wpa2_crypto_funcs.crypto_hash_update(hmac, ct_start, TLS_RECORD_HEADER_LEN);
|
||||
wpa2_crypto_funcs.crypto_hash_update(hmac, payload, payload_len);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash update!\r\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
clen = buf + buf_size - pos;
|
||||
if (clen < rl->hash_size) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not "
|
||||
"enough room for MAC");
|
||||
if (wpa2_crypto_funcs.crypto_hash_finish) {
|
||||
wpa2_crypto_funcs.crypto_hash_finish(hmac, NULL, NULL);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash finish function!\r\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (wpa2_crypto_funcs.crypto_hash_finish) {
|
||||
if ((int)wpa2_crypto_funcs.crypto_hash_finish(hmac, pos, (int *)&clen) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed to calculate HMAC");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n",__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Write HMAC",
|
||||
pos, clen);
|
||||
pos += clen;
|
||||
if (rl->iv_size) {
|
||||
size_t len = pos - cpayload;
|
||||
size_t pad;
|
||||
pad = (len + 1) % rl->iv_size;
|
||||
if (pad)
|
||||
pad = rl->iv_size - pad;
|
||||
if (pos + pad + 1 > buf + buf_size) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: No room for "
|
||||
"block cipher padding");
|
||||
return -1;
|
||||
}
|
||||
os_memset(pos, pad, pad + 1);
|
||||
pos += pad + 1;
|
||||
}
|
||||
|
||||
if (wpa2_crypto_funcs.crypto_cipher_encrypt) {
|
||||
if ((int)wpa2_crypto_funcs.crypto_cipher_encrypt(rl->write_cbc, cpayload,
|
||||
cpayload, pos - cpayload) < 0)
|
||||
return -1;
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "Fail to register crypto_cipher_encrypt function!\r\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
WPA_PUT_BE16(length, pos - length - 2);
|
||||
inc_byte_array(rl->write_seq_num, TLS_SEQ_NUM_LEN);
|
||||
|
||||
*out_len = pos - buf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_record_receive - TLS record layer: Process a received message
|
||||
* @rl: Pointer to TLS record layer data
|
||||
* @in_data: Received data
|
||||
* @in_len: Length of the received data
|
||||
* @out_data: Buffer for output data (must be at least as long as in_data)
|
||||
* @out_len: Set to maximum out_data length by caller; used to return the
|
||||
* length of the used data
|
||||
* @alert: Buffer for returning an alert value on failure
|
||||
* Returns: Number of bytes used from in_data on success, 0 if record was not
|
||||
* complete (more data needed), or -1 on failure
|
||||
*
|
||||
* This function decrypts the received message, verifies HMAC and TLS record
|
||||
* layer header.
|
||||
*/
|
||||
int tlsv1_record_receive(struct tlsv1_record_layer *rl,
|
||||
const u8 *in_data, size_t in_len,
|
||||
u8 *out_data, size_t *out_len, u8 *alert)
|
||||
{
|
||||
size_t i, rlen, hlen;
|
||||
u8 padlen;
|
||||
struct crypto_hash *hmac = NULL;
|
||||
u8 len[2], hash[100];
|
||||
int force_mac_error = 0;
|
||||
u8 ct;
|
||||
|
||||
if (in_len < TLS_RECORD_HEADER_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu) - "
|
||||
"need more data",
|
||||
(unsigned long) in_len);
|
||||
wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
|
||||
in_data, in_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ct = in_data[0];
|
||||
rlen = WPA_GET_BE16(in_data + 3);
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d "
|
||||
"length %d", ct, in_data[1], in_data[2], (int) rlen);
|
||||
|
||||
/*
|
||||
* TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the
|
||||
* protocol version in record layer. As such, accept any {03,xx} value
|
||||
* to remain compatible with existing implementations.
|
||||
*/
|
||||
if (in_data[1] != 0x03) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version "
|
||||
"%u.%u", in_data[1], in_data[2]);
|
||||
*alert = TLS_ALERT_PROTOCOL_VERSION;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* TLSCiphertext must not be more than 2^14+2048 bytes */
|
||||
if (TLS_RECORD_HEADER_LEN + rlen > 18432) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
|
||||
(unsigned long) (TLS_RECORD_HEADER_LEN + rlen));
|
||||
*alert = TLS_ALERT_RECORD_OVERFLOW;
|
||||
return -1;
|
||||
}
|
||||
|
||||
in_data += TLS_RECORD_HEADER_LEN;
|
||||
in_len -= TLS_RECORD_HEADER_LEN;
|
||||
|
||||
if (rlen > in_len) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included "
|
||||
"(rlen=%lu > in_len=%lu)",
|
||||
(unsigned long) rlen, (unsigned long) in_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
|
||||
in_data, rlen);
|
||||
|
||||
if (ct != TLS_CONTENT_TYPE_HANDSHAKE &&
|
||||
ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC &&
|
||||
ct != TLS_CONTENT_TYPE_ALERT &&
|
||||
ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Ignore record with unknown "
|
||||
"content type 0x%x", ct);
|
||||
*alert = TLS_ALERT_UNEXPECTED_MESSAGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
in_len = rlen;
|
||||
|
||||
if (*out_len < in_len) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Not enough output buffer for "
|
||||
"processing received record");
|
||||
*alert = TLS_ALERT_INTERNAL_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
|
||||
size_t plen;
|
||||
if (wpa2_crypto_funcs.crypto_cipher_decrypt) {
|
||||
if ((int)wpa2_crypto_funcs.crypto_cipher_decrypt(rl->read_cbc, in_data,
|
||||
out_data, in_len) < 0) {
|
||||
*alert = TLS_ALERT_DECRYPTION_FAILED;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "Fail to register crypto cipher decrypt function. \r\n");
|
||||
*alert = TLS_ALERT_DECRYPTION_FAILED;
|
||||
return -1;
|
||||
}
|
||||
plen = in_len;
|
||||
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted "
|
||||
"data", out_data, plen);
|
||||
|
||||
if (rl->iv_size) {
|
||||
/*
|
||||
* TLS v1.0 defines different alert values for various
|
||||
* failures. That may information to aid in attacks, so
|
||||
* use the same bad_record_mac alert regardless of the
|
||||
* issues.
|
||||
*
|
||||
* In addition, instead of returning immediately on
|
||||
* error, run through the MAC check to make timing
|
||||
* attacks more difficult.
|
||||
*/
|
||||
|
||||
if (rl->tls_version >= TLS_VERSION_1_1) {
|
||||
/* Remove opaque IV[Cipherspec.block_length] */
|
||||
if (plen < rl->iv_size) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1.1: Not "
|
||||
"enough room for IV");
|
||||
force_mac_error = 1;
|
||||
goto check_mac;
|
||||
}
|
||||
os_memmove(out_data, out_data + rl->iv_size,
|
||||
plen - rl->iv_size);
|
||||
plen -= rl->iv_size;
|
||||
}
|
||||
|
||||
/* Verify and remove padding */
|
||||
if (plen == 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Too short record"
|
||||
" (no pad)");
|
||||
force_mac_error = 1;
|
||||
goto check_mac;
|
||||
}
|
||||
padlen = out_data[plen - 1];
|
||||
if (padlen >= plen) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad "
|
||||
"length (%u, plen=%lu) in "
|
||||
"received record",
|
||||
padlen, (unsigned long) plen);
|
||||
force_mac_error = 1;
|
||||
goto check_mac;
|
||||
}
|
||||
for (i = plen - padlen - 1; i < plen - 1; i++) {
|
||||
if (out_data[i] != padlen) {
|
||||
wpa_hexdump(MSG_DEBUG,
|
||||
"TLSv1: Invalid pad in "
|
||||
"received record",
|
||||
out_data + plen - padlen -
|
||||
1, padlen + 1);
|
||||
force_mac_error = 1;
|
||||
goto check_mac;
|
||||
}
|
||||
}
|
||||
|
||||
plen -= padlen + 1;
|
||||
|
||||
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - "
|
||||
"Decrypted data with IV and padding "
|
||||
"removed", out_data, plen);
|
||||
}
|
||||
|
||||
check_mac:
|
||||
if (plen < rl->hash_size) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no "
|
||||
"hash value");
|
||||
*alert = TLS_ALERT_BAD_RECORD_MAC;
|
||||
return -1;
|
||||
}
|
||||
|
||||
plen -= rl->hash_size;
|
||||
|
||||
if (wpa2_crypto_funcs.crypto_hash_init) {
|
||||
hmac = wpa2_crypto_funcs.crypto_hash_init(rl->hash_alg, rl->read_mac_secret, rl->hash_size);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_init function!\r\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hmac == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
|
||||
"to initialize HMAC");
|
||||
*alert = TLS_ALERT_INTERNAL_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (wpa2_crypto_funcs.crypto_hash_update) {
|
||||
wpa2_crypto_funcs.crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN);
|
||||
/* type + version + length + fragment */
|
||||
wpa2_crypto_funcs.crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3);
|
||||
WPA_PUT_BE16(len, plen);
|
||||
wpa2_crypto_funcs.crypto_hash_update(hmac, len, 2);
|
||||
wpa2_crypto_funcs.crypto_hash_update(hmac, out_data, plen);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash update function!\r\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
hlen = sizeof(hash);
|
||||
if (wpa2_crypto_funcs.crypto_hash_finish) {
|
||||
if ((int)wpa2_crypto_funcs.crypto_hash_finish(hmac, hash, (int *)&hlen) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed to calculate HMAC");
|
||||
*alert = TLS_ALERT_INTERNAL_ERROR;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
|
||||
*alert = TLS_ALERT_INTERNAL_ERROR;
|
||||
return -1;
|
||||
}
|
||||
if (hlen != rl->hash_size ||
|
||||
os_memcmp(hash, out_data + plen, hlen) != 0 ||
|
||||
force_mac_error) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in "
|
||||
"received message (force_mac_error=%d)",
|
||||
force_mac_error);
|
||||
*alert = TLS_ALERT_BAD_RECORD_MAC;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*out_len = plen;
|
||||
} else {
|
||||
os_memcpy(out_data, in_data, in_len);
|
||||
*out_len = in_len;
|
||||
}
|
||||
|
||||
/* TLSCompressed must not be more than 2^14+1024 bytes */
|
||||
if (TLS_RECORD_HEADER_LEN + *out_len > 17408) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
|
||||
(unsigned long) (TLS_RECORD_HEADER_LEN + *out_len));
|
||||
*alert = TLS_ALERT_RECORD_OVERFLOW;
|
||||
return -1;
|
||||
}
|
||||
|
||||
inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN);
|
||||
|
||||
return TLS_RECORD_HEADER_LEN + rlen;
|
||||
}
|
|
@ -0,0 +1,656 @@
|
|||
/*
|
||||
* TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
|
||||
* Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "crypto/sha1.h"
|
||||
#include "wpa2/tls/tls.h"
|
||||
#include "wpa2/tls/tlsv1_common.h"
|
||||
#include "wpa2/tls/tlsv1_record.h"
|
||||
#include "wpa2/tls/tlsv1_server.h"
|
||||
#include "wpa2/tls/tlsv1_server_i.h"
|
||||
|
||||
/* TODO:
|
||||
* Support for a message fragmented across several records (RFC 2246, 6.2.1)
|
||||
*/
|
||||
|
||||
|
||||
void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description)
|
||||
{
|
||||
conn->alert_level = level;
|
||||
conn->alert_description = description;
|
||||
}
|
||||
|
||||
|
||||
int tlsv1_server_derive_keys(struct tlsv1_server *conn,
|
||||
const u8 *pre_master_secret,
|
||||
size_t pre_master_secret_len)
|
||||
{
|
||||
u8 seed[2 * TLS_RANDOM_LEN];
|
||||
u8 key_block[TLS_MAX_KEY_BLOCK_LEN];
|
||||
u8 *pos;
|
||||
size_t key_block_len;
|
||||
|
||||
if (pre_master_secret) {
|
||||
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret",
|
||||
pre_master_secret, pre_master_secret_len);
|
||||
os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
|
||||
os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
|
||||
TLS_RANDOM_LEN);
|
||||
if (tls_prf(conn->rl.tls_version,
|
||||
pre_master_secret, pre_master_secret_len,
|
||||
"master secret", seed, 2 * TLS_RANDOM_LEN,
|
||||
conn->master_secret, TLS_MASTER_SECRET_LEN)) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
|
||||
"master_secret");
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret",
|
||||
conn->master_secret, TLS_MASTER_SECRET_LEN);
|
||||
}
|
||||
|
||||
os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
|
||||
os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
|
||||
key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len +
|
||||
conn->rl.iv_size);
|
||||
if (tls_prf(conn->rl.tls_version,
|
||||
conn->master_secret, TLS_MASTER_SECRET_LEN,
|
||||
"key expansion", seed, 2 * TLS_RANDOM_LEN,
|
||||
key_block, key_block_len)) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block",
|
||||
key_block, key_block_len);
|
||||
|
||||
pos = key_block;
|
||||
|
||||
/* client_write_MAC_secret */
|
||||
os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size);
|
||||
pos += conn->rl.hash_size;
|
||||
/* server_write_MAC_secret */
|
||||
os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size);
|
||||
pos += conn->rl.hash_size;
|
||||
|
||||
/* client_write_key */
|
||||
os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
|
||||
pos += conn->rl.key_material_len;
|
||||
/* server_write_key */
|
||||
os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len);
|
||||
pos += conn->rl.key_material_len;
|
||||
|
||||
/* client_write_IV */
|
||||
os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
|
||||
pos += conn->rl.iv_size;
|
||||
/* server_write_IV */
|
||||
os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
|
||||
pos += conn->rl.iv_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_server_handshake - Process TLS handshake
|
||||
* @conn: TLSv1 server connection data from tlsv1_server_init()
|
||||
* @in_data: Input data from TLS peer
|
||||
* @in_len: Input data length
|
||||
* @out_len: Length of the output buffer.
|
||||
* Returns: Pointer to output data, %NULL on failure
|
||||
*/
|
||||
u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
|
||||
const u8 *in_data, size_t in_len,
|
||||
size_t *out_len)
|
||||
{
|
||||
const u8 *pos, *end;
|
||||
u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct;
|
||||
size_t in_msg_len;
|
||||
int used;
|
||||
|
||||
if (in_data == NULL || in_len == 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: No input data to server");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pos = in_data;
|
||||
end = in_data + in_len;
|
||||
in_msg = os_malloc(in_len);
|
||||
if (in_msg == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Each received packet may include multiple records */
|
||||
while (pos < end) {
|
||||
in_msg_len = in_len;
|
||||
used = tlsv1_record_receive(&conn->rl, pos, end - pos,
|
||||
in_msg, &in_msg_len, &alert);
|
||||
if (used < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
|
||||
"record failed");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
|
||||
goto failed;
|
||||
}
|
||||
if (used == 0) {
|
||||
/* need more data */
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
|
||||
"yet supported");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
|
||||
goto failed;
|
||||
}
|
||||
ct = pos[0];
|
||||
|
||||
in_pos = in_msg;
|
||||
in_end = in_msg + in_msg_len;
|
||||
|
||||
/* Each received record may include multiple messages of the
|
||||
* same ContentType. */
|
||||
while (in_pos < in_end) {
|
||||
in_msg_len = in_end - in_pos;
|
||||
if (tlsv1_server_process_handshake(conn, ct, in_pos,
|
||||
&in_msg_len) < 0)
|
||||
goto failed;
|
||||
in_pos += in_msg_len;
|
||||
}
|
||||
|
||||
pos += used;
|
||||
}
|
||||
|
||||
os_free(in_msg);
|
||||
in_msg = NULL;
|
||||
|
||||
msg = tlsv1_server_handshake_write(conn, out_len);
|
||||
|
||||
failed:
|
||||
os_free(in_msg);
|
||||
if (conn->alert_level) {
|
||||
if (conn->state == FAILED) {
|
||||
/* Avoid alert loops */
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Drop alert loop");
|
||||
os_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
conn->state = FAILED;
|
||||
os_free(msg);
|
||||
msg = tlsv1_server_send_alert(conn, conn->alert_level,
|
||||
conn->alert_description,
|
||||
out_len);
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_server_encrypt - Encrypt data into TLS tunnel
|
||||
* @conn: TLSv1 server connection data from tlsv1_server_init()
|
||||
* @in_data: Pointer to plaintext data to be encrypted
|
||||
* @in_len: Input buffer length
|
||||
* @out_data: Pointer to output buffer (encrypted TLS data)
|
||||
* @out_len: Maximum out_data length
|
||||
* Returns: Number of bytes written to out_data, -1 on failure
|
||||
*
|
||||
* This function is used after TLS handshake has been completed successfully to
|
||||
* send data in the encrypted tunnel.
|
||||
*/
|
||||
int tlsv1_server_encrypt(struct tlsv1_server *conn,
|
||||
const u8 *in_data, size_t in_len,
|
||||
u8 *out_data, size_t out_len)
|
||||
{
|
||||
size_t rlen;
|
||||
|
||||
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
|
||||
in_data, in_len);
|
||||
|
||||
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
|
||||
out_data, out_len, in_data, in_len, &rlen) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return rlen;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_server_decrypt - Decrypt data from TLS tunnel
|
||||
* @conn: TLSv1 server connection data from tlsv1_server_init()
|
||||
* @in_data: Pointer to input buffer (encrypted TLS data)
|
||||
* @in_len: Input buffer length
|
||||
* @out_data: Pointer to output buffer (decrypted data from TLS tunnel)
|
||||
* @out_len: Maximum out_data length
|
||||
* Returns: Number of bytes written to out_data, -1 on failure
|
||||
*
|
||||
* This function is used after TLS handshake has been completed successfully to
|
||||
* receive data from the encrypted tunnel.
|
||||
*/
|
||||
int tlsv1_server_decrypt(struct tlsv1_server *conn,
|
||||
const u8 *in_data, size_t in_len,
|
||||
u8 *out_data, size_t out_len)
|
||||
{
|
||||
const u8 *in_end, *pos;
|
||||
int used;
|
||||
u8 alert, *out_end, *out_pos, ct;
|
||||
size_t olen;
|
||||
|
||||
pos = in_data;
|
||||
in_end = in_data + in_len;
|
||||
out_pos = out_data;
|
||||
out_end = out_data + out_len;
|
||||
|
||||
while (pos < in_end) {
|
||||
ct = pos[0];
|
||||
olen = out_end - out_pos;
|
||||
used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
|
||||
out_pos, &olen, &alert);
|
||||
if (used < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
|
||||
"failed");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
|
||||
return -1;
|
||||
}
|
||||
if (used == 0) {
|
||||
/* need more data */
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
|
||||
"yet supported");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ct == TLS_CONTENT_TYPE_ALERT) {
|
||||
if (olen < 2) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Alert "
|
||||
"underflow");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_DECODE_ERROR);
|
||||
return -1;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
|
||||
out_pos[0], out_pos[1]);
|
||||
if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
|
||||
/* Continue processing */
|
||||
pos += used;
|
||||
continue;
|
||||
}
|
||||
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
out_pos[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
|
||||
"0x%x", pos[0]);
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_UNEXPECTED_MESSAGE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
out_pos += olen;
|
||||
if (out_pos > out_end) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
|
||||
"for processing the received record");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pos += used;
|
||||
}
|
||||
|
||||
return out_pos - out_data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_server_global_init - Initialize TLSv1 server
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*
|
||||
* This function must be called before using any other TLSv1 server functions.
|
||||
*/
|
||||
int tlsv1_server_global_init(void)
|
||||
{
|
||||
return crypto_global_init();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_server_global_deinit - Deinitialize TLSv1 server
|
||||
*
|
||||
* This function can be used to deinitialize the TLSv1 server that was
|
||||
* initialized by calling tlsv1_server_global_init(). No TLSv1 server functions
|
||||
* can be called after this before calling tlsv1_server_global_init() again.
|
||||
*/
|
||||
void tlsv1_server_global_deinit(void)
|
||||
{
|
||||
crypto_global_deinit();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_server_init - Initialize TLSv1 server connection
|
||||
* @cred: Pointer to server credentials from tlsv1_server_cred_alloc()
|
||||
* Returns: Pointer to TLSv1 server connection data or %NULL on failure
|
||||
*/
|
||||
struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred)
|
||||
{
|
||||
struct tlsv1_server *conn;
|
||||
size_t count;
|
||||
u16 *suites;
|
||||
|
||||
conn = (struct tlsv1_server *)os_zalloc(sizeof(*conn));
|
||||
if (conn == NULL)
|
||||
return NULL;
|
||||
|
||||
conn->cred = cred;
|
||||
|
||||
conn->state = CLIENT_HELLO;
|
||||
|
||||
if (tls_verify_hash_init(&conn->verify) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify "
|
||||
"hash");
|
||||
os_free(conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
suites = conn->cipher_suites;
|
||||
suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
|
||||
suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
|
||||
suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
|
||||
suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
|
||||
suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
|
||||
conn->num_cipher_suites = count;
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
|
||||
static void tlsv1_server_clear_data(struct tlsv1_server *conn)
|
||||
{
|
||||
tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
|
||||
tlsv1_record_change_write_cipher(&conn->rl);
|
||||
tlsv1_record_change_read_cipher(&conn->rl);
|
||||
tls_verify_hash_free(&conn->verify);
|
||||
|
||||
crypto_public_key_free(conn->client_rsa_key);
|
||||
conn->client_rsa_key = NULL;
|
||||
|
||||
os_free(conn->session_ticket);
|
||||
conn->session_ticket = NULL;
|
||||
conn->session_ticket_len = 0;
|
||||
conn->use_session_ticket = 0;
|
||||
|
||||
os_free(conn->dh_secret);
|
||||
conn->dh_secret = NULL;
|
||||
conn->dh_secret_len = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_server_deinit - Deinitialize TLSv1 server connection
|
||||
* @conn: TLSv1 server connection data from tlsv1_server_init()
|
||||
*/
|
||||
void tlsv1_server_deinit(struct tlsv1_server *conn)
|
||||
{
|
||||
tlsv1_server_clear_data(conn);
|
||||
os_free(conn);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_server_established - Check whether connection has been established
|
||||
* @conn: TLSv1 server connection data from tlsv1_server_init()
|
||||
* Returns: 1 if connection is established, 0 if not
|
||||
*/
|
||||
int tlsv1_server_established(struct tlsv1_server *conn)
|
||||
{
|
||||
return conn->state == ESTABLISHED;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_server_prf - Use TLS-PRF to derive keying material
|
||||
* @conn: TLSv1 server connection data from tlsv1_server_init()
|
||||
* @label: Label (e.g., description of the key) for PRF
|
||||
* @server_random_first: seed is 0 = client_random|server_random,
|
||||
* 1 = server_random|client_random
|
||||
* @out: Buffer for output data from TLS-PRF
|
||||
* @out_len: Length of the output buffer
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
|
||||
int server_random_first, u8 *out, size_t out_len)
|
||||
{
|
||||
u8 seed[2 * TLS_RANDOM_LEN];
|
||||
|
||||
if (conn->state != ESTABLISHED)
|
||||
return -1;
|
||||
|
||||
if (server_random_first) {
|
||||
os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
|
||||
os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
|
||||
TLS_RANDOM_LEN);
|
||||
} else {
|
||||
os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
|
||||
os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
|
||||
TLS_RANDOM_LEN);
|
||||
}
|
||||
|
||||
return tls_prf(conn->rl.tls_version,
|
||||
conn->master_secret, TLS_MASTER_SECRET_LEN,
|
||||
label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_server_get_cipher - Get current cipher name
|
||||
* @conn: TLSv1 server connection data from tlsv1_server_init()
|
||||
* @buf: Buffer for the cipher name
|
||||
* @buflen: buf size
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*
|
||||
* Get the name of the currently used cipher.
|
||||
*/
|
||||
int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
#ifndef ESPRESSIF_USE
|
||||
char *cipher;
|
||||
|
||||
switch (conn->rl.cipher_suite) {
|
||||
case TLS_RSA_WITH_RC4_128_MD5:
|
||||
cipher = "RC4-MD5";
|
||||
break;
|
||||
case TLS_RSA_WITH_RC4_128_SHA:
|
||||
cipher = "RC4-SHA";
|
||||
break;
|
||||
case TLS_RSA_WITH_DES_CBC_SHA:
|
||||
cipher = "DES-CBC-SHA";
|
||||
break;
|
||||
case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
|
||||
cipher = "DES-CBC3-SHA";
|
||||
break;
|
||||
case TLS_DH_anon_WITH_AES_128_CBC_SHA:
|
||||
cipher = "ADH-AES-128-SHA";
|
||||
break;
|
||||
case TLS_RSA_WITH_AES_256_CBC_SHA:
|
||||
cipher = "AES-256-SHA";
|
||||
break;
|
||||
case TLS_RSA_WITH_AES_128_CBC_SHA:
|
||||
cipher = "AES-128-SHA";
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
//if (os_strlcpy(buf, cipher, buflen) >= buflen)
|
||||
// return -1;
|
||||
|
||||
os_memcpy((u8 *)buf, (u8 *)cipher, buflen);
|
||||
|
||||
return 0;
|
||||
#else
|
||||
char cipher[20];
|
||||
|
||||
switch (conn->rl.cipher_suite) {
|
||||
case TLS_RSA_WITH_RC4_128_MD5:
|
||||
strcpy(cipher, "RC4-MD5");
|
||||
break;
|
||||
case TLS_RSA_WITH_RC4_128_SHA:
|
||||
strcpy(cipher, "RC4-SHA");
|
||||
break;
|
||||
case TLS_RSA_WITH_DES_CBC_SHA:
|
||||
strcpy(cipher, "DES-CBC-SHA");
|
||||
break;
|
||||
case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
|
||||
strcpy(cipher, "DES-CBC3-SHA");
|
||||
break;
|
||||
case TLS_DH_anon_WITH_AES_128_CBC_SHA:
|
||||
strcpy(cipher, "ADH-AES-128-SHA");
|
||||
break;
|
||||
case TLS_RSA_WITH_AES_256_CBC_SHA:
|
||||
strcpy(cipher, "AES-256-SHA");
|
||||
break;
|
||||
case TLS_RSA_WITH_AES_128_CBC_SHA:
|
||||
strcpy(cipher, "AES-128-SHA");
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
os_memcpy((u8 *)buf, (u8 *)cipher, buflen);
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_server_shutdown - Shutdown TLS connection
|
||||
* @conn: TLSv1 server connection data from tlsv1_server_init()
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int tlsv1_server_shutdown(struct tlsv1_server *conn)
|
||||
{
|
||||
conn->state = CLIENT_HELLO;
|
||||
|
||||
if (tls_verify_hash_init(&conn->verify) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify "
|
||||
"hash");
|
||||
return -1;
|
||||
}
|
||||
|
||||
tlsv1_server_clear_data(conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_server_resumed - Was session resumption used
|
||||
* @conn: TLSv1 server connection data from tlsv1_server_init()
|
||||
* Returns: 1 if current session used session resumption, 0 if not
|
||||
*/
|
||||
int tlsv1_server_resumed(struct tlsv1_server *conn)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_server_get_keys - Get master key and random data from TLS connection
|
||||
* @conn: TLSv1 server connection data from tlsv1_server_init()
|
||||
* @keys: Structure of key/random data (filled on success)
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys)
|
||||
{
|
||||
os_memset(keys, 0, sizeof(*keys));
|
||||
if (conn->state == CLIENT_HELLO)
|
||||
return -1;
|
||||
|
||||
keys->client_random = conn->client_random;
|
||||
keys->client_random_len = TLS_RANDOM_LEN;
|
||||
|
||||
if (conn->state != SERVER_HELLO) {
|
||||
keys->server_random = conn->server_random;
|
||||
keys->server_random_len = TLS_RANDOM_LEN;
|
||||
keys->master_key = conn->master_secret;
|
||||
keys->master_key_len = TLS_MASTER_SECRET_LEN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_server_get_keyblock_size - Get TLS key_block size
|
||||
* @conn: TLSv1 server connection data from tlsv1_server_init()
|
||||
* Returns: Size of the key_block for the negotiated cipher suite or -1 on
|
||||
* failure
|
||||
*/
|
||||
int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn)
|
||||
{
|
||||
if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO)
|
||||
return -1;
|
||||
|
||||
return 2 * (conn->rl.hash_size + conn->rl.key_material_len +
|
||||
conn->rl.iv_size);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tlsv1_server_set_cipher_list - Configure acceptable cipher suites
|
||||
* @conn: TLSv1 server connection data from tlsv1_server_init()
|
||||
* @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
|
||||
* (TLS_CIPHER_*).
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers)
|
||||
{
|
||||
size_t count;
|
||||
u16 *suites;
|
||||
|
||||
/* TODO: implement proper configuration of cipher suites */
|
||||
if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
|
||||
count = 0;
|
||||
suites = conn->cipher_suites;
|
||||
suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
|
||||
suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
|
||||
suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
|
||||
suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
|
||||
suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
|
||||
suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
|
||||
suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
|
||||
suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
|
||||
suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
|
||||
suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA;
|
||||
conn->num_cipher_suites = count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer)
|
||||
{
|
||||
conn->verify_peer = verify_peer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn,
|
||||
tlsv1_server_session_ticket_cb cb,
|
||||
void *ctx)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)",
|
||||
cb, ctx);
|
||||
conn->session_ticket_cb = cb;
|
||||
conn->session_ticket_cb_ctx = ctx;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,819 @@
|
|||
/*
|
||||
* TLSv1 server - write handshake message
|
||||
* Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "crypto/md5.h"
|
||||
#include "crypto/sha1.h"
|
||||
#include "crypto/sha256.h"
|
||||
#include "crypto/random.h"
|
||||
#include "wpa2/tls/tls.h"
|
||||
#include "wpa2/tls/x509v3.h"
|
||||
#include "wpa2/tls/tlsv1_common.h"
|
||||
#include "wpa2/tls/tlsv1_record.h"
|
||||
#include "wpa2/tls/tlsv1_server.h"
|
||||
#include "wpa2/tls/tlsv1_server_i.h"
|
||||
|
||||
#include "wpa2/eap_peer/eap_i.h"
|
||||
|
||||
static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn)
|
||||
{
|
||||
size_t len = 0;
|
||||
struct x509_certificate *cert;
|
||||
|
||||
cert = conn->cred->cert;
|
||||
while (cert) {
|
||||
len += 3 + cert->cert_len;
|
||||
if (x509_certificate_self_signed(cert))
|
||||
break;
|
||||
cert = x509_certificate_get_subject(conn->cred->trusted_certs,
|
||||
&cert->issuer);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static int tls_write_server_hello(struct tlsv1_server *conn,
|
||||
u8 **msgpos, u8 *end)
|
||||
{
|
||||
u8 *pos, *rhdr, *hs_start, *hs_length;
|
||||
struct os_time now;
|
||||
size_t rlen;
|
||||
|
||||
pos = *msgpos;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHello");
|
||||
rhdr = pos;
|
||||
pos += TLS_RECORD_HEADER_LEN;
|
||||
|
||||
os_get_time(&now);
|
||||
WPA_PUT_BE32(conn->server_random, now.sec);
|
||||
if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) {
|
||||
wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
|
||||
"server_random");
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random",
|
||||
conn->server_random, TLS_RANDOM_LEN);
|
||||
|
||||
conn->session_id_len = TLS_SESSION_ID_MAX_LEN;
|
||||
if (random_get_bytes(conn->session_id, conn->session_id_len)) {
|
||||
wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
|
||||
"session_id");
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id",
|
||||
conn->session_id, conn->session_id_len);
|
||||
|
||||
/* opaque fragment[TLSPlaintext.length] */
|
||||
|
||||
/* Handshake */
|
||||
hs_start = pos;
|
||||
/* HandshakeType msg_type */
|
||||
*pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO;
|
||||
/* uint24 length (to be filled) */
|
||||
hs_length = pos;
|
||||
pos += 3;
|
||||
/* body - ServerHello */
|
||||
/* ProtocolVersion server_version */
|
||||
WPA_PUT_BE16(pos, conn->rl.tls_version);
|
||||
pos += 2;
|
||||
/* Random random: uint32 gmt_unix_time, opaque random_bytes */
|
||||
os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN);
|
||||
pos += TLS_RANDOM_LEN;
|
||||
/* SessionID session_id */
|
||||
*pos++ = conn->session_id_len;
|
||||
os_memcpy(pos, conn->session_id, conn->session_id_len);
|
||||
pos += conn->session_id_len;
|
||||
/* CipherSuite cipher_suite */
|
||||
WPA_PUT_BE16(pos, conn->cipher_suite);
|
||||
pos += 2;
|
||||
/* CompressionMethod compression_method */
|
||||
*pos++ = TLS_COMPRESSION_NULL;
|
||||
|
||||
if (conn->session_ticket && conn->session_ticket_cb) {
|
||||
int res = conn->session_ticket_cb(
|
||||
conn->session_ticket_cb_ctx,
|
||||
conn->session_ticket, conn->session_ticket_len,
|
||||
conn->client_random, conn->server_random,
|
||||
conn->master_secret);
|
||||
if (res < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback "
|
||||
"indicated failure");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_HANDSHAKE_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
conn->use_session_ticket = res;
|
||||
|
||||
if (conn->use_session_ticket) {
|
||||
if (tlsv1_server_derive_keys(conn, NULL, 0) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
|
||||
"derive keys");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* RFC 4507 specifies that server would include an empty
|
||||
* SessionTicket extension in ServerHello and a
|
||||
* NewSessionTicket message after the ServerHello. However,
|
||||
* EAP-FAST (RFC 4851), i.e., the only user of SessionTicket
|
||||
* extension at the moment, does not use such extensions.
|
||||
*
|
||||
* TODO: Add support for configuring RFC 4507 behavior and make
|
||||
* EAP-FAST disable it.
|
||||
*/
|
||||
}
|
||||
|
||||
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
|
||||
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
|
||||
|
||||
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
|
||||
rhdr, end - rhdr, hs_start, pos - hs_start,
|
||||
&rlen) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
pos = rhdr + rlen;
|
||||
|
||||
*msgpos = pos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int tls_write_server_certificate(struct tlsv1_server *conn,
|
||||
u8 **msgpos, u8 *end)
|
||||
{
|
||||
u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start;
|
||||
size_t rlen;
|
||||
struct x509_certificate *cert;
|
||||
const struct tls_cipher_suite *suite;
|
||||
|
||||
suite = tls_get_cipher_suite(conn->rl.cipher_suite);
|
||||
if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Do not send Certificate when "
|
||||
"using anonymous DH");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pos = *msgpos;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
|
||||
rhdr = pos;
|
||||
pos += TLS_RECORD_HEADER_LEN;
|
||||
|
||||
/* opaque fragment[TLSPlaintext.length] */
|
||||
|
||||
/* Handshake */
|
||||
hs_start = pos;
|
||||
/* HandshakeType msg_type */
|
||||
*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE;
|
||||
/* uint24 length (to be filled) */
|
||||
hs_length = pos;
|
||||
pos += 3;
|
||||
/* body - Certificate */
|
||||
/* uint24 length (to be filled) */
|
||||
cert_start = pos;
|
||||
pos += 3;
|
||||
cert = conn->cred->cert;
|
||||
while (cert) {
|
||||
if (pos + 3 + cert->cert_len > end) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
|
||||
"for Certificate (cert_len=%lu left=%lu)",
|
||||
(unsigned long) cert->cert_len,
|
||||
(unsigned long) (end - pos));
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
WPA_PUT_BE24(pos, cert->cert_len);
|
||||
pos += 3;
|
||||
os_memcpy(pos, cert->cert_start, cert->cert_len);
|
||||
pos += cert->cert_len;
|
||||
|
||||
if (x509_certificate_self_signed(cert))
|
||||
break;
|
||||
cert = x509_certificate_get_subject(conn->cred->trusted_certs,
|
||||
&cert->issuer);
|
||||
}
|
||||
if (cert == conn->cred->cert || cert == NULL) {
|
||||
/*
|
||||
* Server was not configured with all the needed certificates
|
||||
* to form a full certificate chain. The client may fail to
|
||||
* validate the chain unless it is configured with all the
|
||||
* missing CA certificates.
|
||||
*/
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Full server certificate chain "
|
||||
"not configured - validation may fail");
|
||||
}
|
||||
WPA_PUT_BE24(cert_start, pos - cert_start - 3);
|
||||
|
||||
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
|
||||
|
||||
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
|
||||
rhdr, end - rhdr, hs_start, pos - hs_start,
|
||||
&rlen) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
pos = rhdr + rlen;
|
||||
|
||||
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
|
||||
|
||||
*msgpos = pos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int tls_write_server_key_exchange(struct tlsv1_server *conn,
|
||||
u8 **msgpos, u8 *end)
|
||||
{
|
||||
tls_key_exchange keyx;
|
||||
const struct tls_cipher_suite *suite;
|
||||
u8 *pos, *rhdr, *hs_start, *hs_length;
|
||||
size_t rlen;
|
||||
u8 *dh_ys;
|
||||
size_t dh_ys_len;
|
||||
|
||||
suite = tls_get_cipher_suite(conn->rl.cipher_suite);
|
||||
if (suite == NULL)
|
||||
keyx = TLS_KEY_X_NULL;
|
||||
else
|
||||
keyx = suite->key_exchange;
|
||||
|
||||
if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: No ServerKeyExchange needed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (keyx != TLS_KEY_X_DH_anon) {
|
||||
/* TODO? */
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet "
|
||||
"supported with key exchange type %d", keyx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (conn->cred == NULL || conn->cred->dh_p == NULL ||
|
||||
conn->cred->dh_g == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available for "
|
||||
"ServerKeyExhcange");
|
||||
return -1;
|
||||
}
|
||||
|
||||
os_free(conn->dh_secret);
|
||||
conn->dh_secret_len = conn->cred->dh_p_len;
|
||||
conn->dh_secret = os_malloc(conn->dh_secret_len);
|
||||
if (conn->dh_secret == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
|
||||
"memory for secret (Diffie-Hellman)");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
if (random_get_bytes(conn->dh_secret, conn->dh_secret_len)) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
|
||||
"data for Diffie-Hellman");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
os_free(conn->dh_secret);
|
||||
conn->dh_secret = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (os_memcmp(conn->dh_secret, conn->cred->dh_p, conn->dh_secret_len) >
|
||||
0)
|
||||
conn->dh_secret[0] = 0; /* make sure secret < p */
|
||||
|
||||
pos = conn->dh_secret;
|
||||
while (pos + 1 < conn->dh_secret + conn->dh_secret_len && *pos == 0)
|
||||
pos++;
|
||||
if (pos != conn->dh_secret) {
|
||||
os_memmove(conn->dh_secret, pos,
|
||||
conn->dh_secret_len - (pos - conn->dh_secret));
|
||||
conn->dh_secret_len -= pos - conn->dh_secret;
|
||||
}
|
||||
wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH server's secret value",
|
||||
conn->dh_secret, conn->dh_secret_len);
|
||||
|
||||
/* Ys = g^secret mod p */
|
||||
dh_ys_len = conn->cred->dh_p_len;
|
||||
dh_ys = os_malloc(dh_ys_len);
|
||||
if (dh_ys == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate memory for "
|
||||
"Diffie-Hellman");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
if(wpa2_crypto_funcs.crypto_mod_exp) {
|
||||
if (wpa2_crypto_funcs.crypto_mod_exp(conn->cred->dh_g, conn->cred->dh_g_len,
|
||||
conn->dh_secret, conn->dh_secret_len,
|
||||
conn->cred->dh_p, conn->cred->dh_p_len,
|
||||
dh_ys, &dh_ys_len)) {
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
os_free(dh_ys);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
os_free(dh_ys);
|
||||
wpa_printf(MSG_ERROR, "Fail to register crypto_mod_exp function!\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
|
||||
dh_ys, dh_ys_len);
|
||||
|
||||
/*
|
||||
* struct {
|
||||
* select (KeyExchangeAlgorithm) {
|
||||
* case diffie_hellman:
|
||||
* ServerDHParams params;
|
||||
* Signature signed_params;
|
||||
* case rsa:
|
||||
* ServerRSAParams params;
|
||||
* Signature signed_params;
|
||||
* };
|
||||
* } ServerKeyExchange;
|
||||
*
|
||||
* struct {
|
||||
* opaque dh_p<1..2^16-1>;
|
||||
* opaque dh_g<1..2^16-1>;
|
||||
* opaque dh_Ys<1..2^16-1>;
|
||||
* } ServerDHParams;
|
||||
*/
|
||||
|
||||
pos = *msgpos;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Send ServerKeyExchange");
|
||||
rhdr = pos;
|
||||
pos += TLS_RECORD_HEADER_LEN;
|
||||
|
||||
/* opaque fragment[TLSPlaintext.length] */
|
||||
|
||||
/* Handshake */
|
||||
hs_start = pos;
|
||||
/* HandshakeType msg_type */
|
||||
*pos++ = TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE;
|
||||
/* uint24 length (to be filled) */
|
||||
hs_length = pos;
|
||||
pos += 3;
|
||||
|
||||
/* body - ServerDHParams */
|
||||
/* dh_p */
|
||||
if (pos + 2 + conn->cred->dh_p_len > end) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
|
||||
"dh_p");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
os_free(dh_ys);
|
||||
return -1;
|
||||
}
|
||||
WPA_PUT_BE16(pos, conn->cred->dh_p_len);
|
||||
pos += 2;
|
||||
os_memcpy(pos, conn->cred->dh_p, conn->cred->dh_p_len);
|
||||
pos += conn->cred->dh_p_len;
|
||||
|
||||
/* dh_g */
|
||||
if (pos + 2 + conn->cred->dh_g_len > end) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
|
||||
"dh_g");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
os_free(dh_ys);
|
||||
return -1;
|
||||
}
|
||||
WPA_PUT_BE16(pos, conn->cred->dh_g_len);
|
||||
pos += 2;
|
||||
os_memcpy(pos, conn->cred->dh_g, conn->cred->dh_g_len);
|
||||
pos += conn->cred->dh_g_len;
|
||||
|
||||
/* dh_Ys */
|
||||
if (pos + 2 + dh_ys_len > end) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
|
||||
"dh_Ys");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
os_free(dh_ys);
|
||||
return -1;
|
||||
}
|
||||
WPA_PUT_BE16(pos, dh_ys_len);
|
||||
pos += 2;
|
||||
os_memcpy(pos, dh_ys, dh_ys_len);
|
||||
pos += dh_ys_len;
|
||||
os_free(dh_ys);
|
||||
|
||||
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
|
||||
|
||||
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
|
||||
rhdr, end - rhdr, hs_start, pos - hs_start,
|
||||
&rlen) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
pos = rhdr + rlen;
|
||||
|
||||
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
|
||||
|
||||
*msgpos = pos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int tls_write_server_certificate_request(struct tlsv1_server *conn,
|
||||
u8 **msgpos, u8 *end)
|
||||
{
|
||||
u8 *pos, *rhdr, *hs_start, *hs_length;
|
||||
size_t rlen;
|
||||
|
||||
if (!conn->verify_peer) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: No CertificateRequest needed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pos = *msgpos;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateRequest");
|
||||
rhdr = pos;
|
||||
pos += TLS_RECORD_HEADER_LEN;
|
||||
|
||||
/* opaque fragment[TLSPlaintext.length] */
|
||||
|
||||
/* Handshake */
|
||||
hs_start = pos;
|
||||
/* HandshakeType msg_type */
|
||||
*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST;
|
||||
/* uint24 length (to be filled) */
|
||||
hs_length = pos;
|
||||
pos += 3;
|
||||
/* body - CertificateRequest */
|
||||
|
||||
/*
|
||||
* enum {
|
||||
* rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),
|
||||
* (255)
|
||||
* } ClientCertificateType;
|
||||
* ClientCertificateType certificate_types<1..2^8-1>
|
||||
*/
|
||||
*pos++ = 1;
|
||||
*pos++ = 1; /* rsa_sign */
|
||||
|
||||
/*
|
||||
* opaque DistinguishedName<1..2^16-1>
|
||||
* DistinguishedName certificate_authorities<3..2^16-1>
|
||||
*/
|
||||
/* TODO: add support for listing DNs for trusted CAs */
|
||||
WPA_PUT_BE16(pos, 0);
|
||||
pos += 2;
|
||||
|
||||
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
|
||||
|
||||
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
|
||||
rhdr, end - rhdr, hs_start, pos - hs_start,
|
||||
&rlen) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
pos = rhdr + rlen;
|
||||
|
||||
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
|
||||
|
||||
*msgpos = pos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int tls_write_server_hello_done(struct tlsv1_server *conn,
|
||||
u8 **msgpos, u8 *end)
|
||||
{
|
||||
u8 *pos;
|
||||
size_t rlen;
|
||||
u8 payload[4];
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone");
|
||||
|
||||
/* opaque fragment[TLSPlaintext.length] */
|
||||
|
||||
/* Handshake */
|
||||
pos = payload;
|
||||
/* HandshakeType msg_type */
|
||||
*pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE;
|
||||
/* uint24 length */
|
||||
WPA_PUT_BE24(pos, 0);
|
||||
pos += 3;
|
||||
/* body - ServerHelloDone (empty) */
|
||||
|
||||
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
|
||||
*msgpos, end - *msgpos, payload, pos - payload,
|
||||
&rlen) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tls_verify_hash_add(&conn->verify, payload, pos - payload);
|
||||
|
||||
*msgpos += rlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
|
||||
u8 **msgpos, u8 *end)
|
||||
{
|
||||
size_t rlen;
|
||||
u8 payload[1];
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
|
||||
|
||||
payload[0] = TLS_CHANGE_CIPHER_SPEC;
|
||||
|
||||
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
|
||||
*msgpos, end - *msgpos, payload, sizeof(payload),
|
||||
&rlen) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tlsv1_record_change_write_cipher(&conn->rl) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for "
|
||||
"record layer");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*msgpos += rlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int tls_write_server_finished(struct tlsv1_server *conn,
|
||||
u8 **msgpos, u8 *end)
|
||||
{
|
||||
u8 *pos, *hs_start;
|
||||
size_t rlen, hlen;
|
||||
u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
|
||||
u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
|
||||
|
||||
pos = *msgpos;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
|
||||
|
||||
/* Encrypted Handshake Message: Finished */
|
||||
|
||||
#ifdef CONFIG_TLSV12
|
||||
if (conn->rl.tls_version >= TLS_VERSION_1_2) {
|
||||
hlen = SHA256_MAC_LEN;
|
||||
if (wpa2_crypto_funcs.crypto_hash_finish) {
|
||||
if (conn->verify.sha256_server == NULL ||
|
||||
wpa2_crypto_funcs.crypto_hash_finish(conn->verify.sha256_server, hash, &hlen)
|
||||
< 0) {
|
||||
conn->verify.sha256_server = NULL;
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
conn->verify.sha256_server = NULL;
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
conn->verify.sha256_server = NULL;
|
||||
} else {
|
||||
#endif /* CONFIG_TLSV12 */
|
||||
|
||||
hlen = MD5_MAC_LEN;
|
||||
if (conn->verify.md5_server == NULL ||
|
||||
crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
conn->verify.md5_server = NULL;
|
||||
crypto_hash_finish(conn->verify.sha1_server, NULL, NULL);
|
||||
conn->verify.sha1_server = NULL;
|
||||
return -1;
|
||||
}
|
||||
conn->verify.md5_server = NULL;
|
||||
hlen = SHA1_MAC_LEN;
|
||||
if (conn->verify.sha1_server == NULL ||
|
||||
crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN,
|
||||
&hlen) < 0) {
|
||||
conn->verify.sha1_server = NULL;
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
conn->verify.sha1_server = NULL;
|
||||
hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
|
||||
|
||||
#ifdef CONFIG_TLSV12
|
||||
}
|
||||
#endif /* CONFIG_TLSV12 */
|
||||
|
||||
if (tls_prf(conn->rl.tls_version,
|
||||
conn->master_secret, TLS_MASTER_SECRET_LEN,
|
||||
"server finished", hash, hlen,
|
||||
verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
|
||||
verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
|
||||
|
||||
/* Handshake */
|
||||
pos = hs_start = verify_data;
|
||||
/* HandshakeType msg_type */
|
||||
*pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
|
||||
/* uint24 length */
|
||||
WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
|
||||
pos += 3;
|
||||
pos += TLS_VERIFY_DATA_LEN;
|
||||
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
|
||||
|
||||
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
|
||||
*msgpos, end - *msgpos, hs_start, pos - hs_start,
|
||||
&rlen) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
|
||||
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
|
||||
TLS_ALERT_INTERNAL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*msgpos += rlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len)
|
||||
{
|
||||
u8 *msg, *end, *pos;
|
||||
size_t msglen;
|
||||
|
||||
*out_len = 0;
|
||||
|
||||
msglen = 1000 + tls_server_cert_chain_der_len(conn);
|
||||
|
||||
msg = os_malloc(msglen);
|
||||
if (msg == NULL)
|
||||
return NULL;
|
||||
|
||||
pos = msg;
|
||||
end = msg + msglen;
|
||||
|
||||
if (tls_write_server_hello(conn, &pos, end) < 0) {
|
||||
os_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (conn->use_session_ticket) {
|
||||
/* Abbreviated handshake using session ticket; RFC 4507 */
|
||||
if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 ||
|
||||
tls_write_server_finished(conn, &pos, end) < 0) {
|
||||
os_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*out_len = pos - msg;
|
||||
|
||||
conn->state = CHANGE_CIPHER_SPEC;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* Full handshake */
|
||||
if (tls_write_server_certificate(conn, &pos, end) < 0 ||
|
||||
tls_write_server_key_exchange(conn, &pos, end) < 0 ||
|
||||
tls_write_server_certificate_request(conn, &pos, end) < 0 ||
|
||||
tls_write_server_hello_done(conn, &pos, end) < 0) {
|
||||
os_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*out_len = pos - msg;
|
||||
|
||||
conn->state = CLIENT_CERTIFICATE;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
static u8 * tls_send_change_cipher_spec(struct tlsv1_server *conn,
|
||||
size_t *out_len)
|
||||
{
|
||||
u8 *msg, *end, *pos;
|
||||
|
||||
*out_len = 0;
|
||||
|
||||
msg = os_malloc(1000);
|
||||
if (msg == NULL)
|
||||
return NULL;
|
||||
|
||||
pos = msg;
|
||||
end = msg + 1000;
|
||||
|
||||
if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 ||
|
||||
tls_write_server_finished(conn, &pos, end) < 0) {
|
||||
os_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*out_len = pos - msg;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed successfully");
|
||||
conn->state = ESTABLISHED;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len)
|
||||
{
|
||||
switch (conn->state) {
|
||||
case SERVER_HELLO:
|
||||
return tls_send_server_hello(conn, out_len);
|
||||
case SERVER_CHANGE_CIPHER_SPEC:
|
||||
return tls_send_change_cipher_spec(conn, out_len);
|
||||
default:
|
||||
if (conn->state == ESTABLISHED && conn->use_session_ticket) {
|
||||
/* Abbreviated handshake was already completed. */
|
||||
return NULL;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while "
|
||||
"generating reply", conn->state);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level,
|
||||
u8 description, size_t *out_len)
|
||||
{
|
||||
u8 *alert, *pos, *length;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description);
|
||||
*out_len = 0;
|
||||
|
||||
alert = os_malloc(10);
|
||||
if (alert == NULL)
|
||||
return NULL;
|
||||
|
||||
pos = alert;
|
||||
|
||||
/* TLSPlaintext */
|
||||
/* ContentType type */
|
||||
*pos++ = TLS_CONTENT_TYPE_ALERT;
|
||||
/* ProtocolVersion version */
|
||||
WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version :
|
||||
TLS_VERSION);
|
||||
pos += 2;
|
||||
/* uint16 length (to be filled) */
|
||||
length = pos;
|
||||
pos += 2;
|
||||
/* opaque fragment[TLSPlaintext.length] */
|
||||
|
||||
/* Alert */
|
||||
/* AlertLevel level */
|
||||
*pos++ = level;
|
||||
/* AlertDescription description */
|
||||
*pos++ = description;
|
||||
|
||||
WPA_PUT_BE16(length, pos - length - 2);
|
||||
*out_len = pos - alert;
|
||||
|
||||
return alert;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Base64 encoding/decoding (RFC1341)
|
||||
* Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "os.h"
|
||||
#include "wpa2/utils/base64.h"
|
||||
|
||||
static const unsigned char base64_table[65] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/**
|
||||
* base64_encode - Base64 encode
|
||||
* @src: Data to be encoded
|
||||
* @len: Length of the data to be encoded
|
||||
* @out_len: Pointer to output length variable, or %NULL if not used
|
||||
* Returns: Allocated buffer of out_len bytes of encoded data,
|
||||
* or %NULL on failure
|
||||
*
|
||||
* Caller is responsible for freeing the returned buffer. Returned buffer is
|
||||
* nul terminated to make it easier to use as a C string. The nul terminator is
|
||||
* not included in out_len.
|
||||
*/
|
||||
unsigned char * base64_encode(const unsigned char *src, size_t len,
|
||||
size_t *out_len)
|
||||
{
|
||||
unsigned char *out, *pos;
|
||||
const unsigned char *end, *in;
|
||||
size_t olen;
|
||||
int line_len;
|
||||
|
||||
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
|
||||
olen += olen / 72; /* line feeds */
|
||||
olen++; /* nul termination */
|
||||
if (olen < len)
|
||||
return NULL; /* integer overflow */
|
||||
out = os_malloc(olen);
|
||||
if (out == NULL)
|
||||
return NULL;
|
||||
|
||||
end = src + len;
|
||||
in = src;
|
||||
pos = out;
|
||||
line_len = 0;
|
||||
while (end - in >= 3) {
|
||||
*pos++ = base64_table[in[0] >> 2];
|
||||
*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
|
||||
*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
|
||||
*pos++ = base64_table[in[2] & 0x3f];
|
||||
in += 3;
|
||||
line_len += 4;
|
||||
if (line_len >= 72) {
|
||||
*pos++ = '\n';
|
||||
line_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (end - in) {
|
||||
*pos++ = base64_table[in[0] >> 2];
|
||||
if (end - in == 1) {
|
||||
*pos++ = base64_table[(in[0] & 0x03) << 4];
|
||||
*pos++ = '=';
|
||||
} else {
|
||||
*pos++ = base64_table[((in[0] & 0x03) << 4) |
|
||||
(in[1] >> 4)];
|
||||
*pos++ = base64_table[(in[1] & 0x0f) << 2];
|
||||
}
|
||||
*pos++ = '=';
|
||||
line_len += 4;
|
||||
}
|
||||
|
||||
if (line_len)
|
||||
*pos++ = '\n';
|
||||
|
||||
*pos = '\0';
|
||||
if (out_len)
|
||||
*out_len = pos - out;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* base64_decode - Base64 decode
|
||||
* @src: Data to be decoded
|
||||
* @len: Length of the data to be decoded
|
||||
* @out_len: Pointer to output length variable
|
||||
* Returns: Allocated buffer of out_len bytes of decoded data,
|
||||
* or %NULL on failure
|
||||
*
|
||||
* Caller is responsible for freeing the returned buffer.
|
||||
*/
|
||||
unsigned char * base64_decode(const unsigned char *src, size_t len,
|
||||
size_t *out_len)
|
||||
{
|
||||
unsigned char dtable[256], *out, *pos, block[4], tmp;
|
||||
size_t i, count, olen;
|
||||
int pad = 0;
|
||||
|
||||
os_memset(dtable, 0x80, 256);
|
||||
for (i = 0; i < sizeof(base64_table) - 1; i++)
|
||||
dtable[base64_table[i]] = (unsigned char) i;
|
||||
dtable['='] = 0;
|
||||
|
||||
count = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (dtable[src[i]] != 0x80)
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count == 0 || count % 4)
|
||||
return NULL;
|
||||
|
||||
olen = count / 4 * 3;
|
||||
pos = out = os_malloc(olen);
|
||||
if (out == NULL)
|
||||
return NULL;
|
||||
|
||||
count = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
tmp = dtable[src[i]];
|
||||
if (tmp == 0x80)
|
||||
continue;
|
||||
|
||||
if (src[i] == '=')
|
||||
pad++;
|
||||
block[count] = tmp;
|
||||
count++;
|
||||
if (count == 4) {
|
||||
*pos++ = (block[0] << 2) | (block[1] >> 4);
|
||||
*pos++ = (block[1] << 4) | (block[2] >> 2);
|
||||
*pos++ = (block[2] << 6) | block[3];
|
||||
count = 0;
|
||||
if (pad) {
|
||||
if (pad == 1)
|
||||
pos--;
|
||||
else if (pad == 2)
|
||||
pos -= 2;
|
||||
else {
|
||||
/* Invalid padding */
|
||||
os_free(out);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*out_len = pos - out;
|
||||
return out;
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* External password backend
|
||||
* Copyright (c) 2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "wpa2/utils/ext_password_i.h"
|
||||
|
||||
#ifdef CONFIG_EXT_PASSWORD_TEST
|
||||
extern struct ext_password_backend ext_password_test;
|
||||
#endif /* CONFIG_EXT_PASSWORD_TEST */
|
||||
|
||||
#ifdef CONFIG_EXT_PASSWORD
|
||||
static const struct ext_password_backend *backends[] = {
|
||||
#ifdef CONFIG_EXT_PASSWORD_TEST
|
||||
&ext_password_test,
|
||||
#endif /* CONFIG_EXT_PASSWORD_TEST */
|
||||
NULL
|
||||
};
|
||||
#endif
|
||||
|
||||
struct ext_password_data {
|
||||
const struct ext_password_backend *backend;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_EXT_PASSWORD
|
||||
struct ext_password_data * ext_password_init(const char *backend,
|
||||
const char *params)
|
||||
{
|
||||
struct ext_password_data *data;
|
||||
int i;
|
||||
|
||||
data = (struct ext_password_data *)os_zalloc(sizeof(*data));
|
||||
if (data == NULL)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; backends[i]; i++) {
|
||||
if (os_strcmp(backends[i]->name, backend) == 0) {
|
||||
data->backend = backends[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!data->backend) {
|
||||
os_free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data->priv = data->backend->init(params);
|
||||
if (data->priv == NULL) {
|
||||
os_free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
void ext_password_deinit(struct ext_password_data *data)
|
||||
{
|
||||
if (data && data->backend && data->priv)
|
||||
data->backend->deinit(data->priv);
|
||||
os_free(data);
|
||||
}
|
||||
|
||||
|
||||
struct wpabuf * ext_password_get(struct ext_password_data *data,
|
||||
const char *name)
|
||||
{
|
||||
if (data == NULL)
|
||||
return NULL;
|
||||
return data->backend->get(data->priv, name);
|
||||
}
|
||||
#endif /* CONFIG_EXT_PASSWORD */
|
||||
|
||||
struct wpabuf * ext_password_alloc(size_t len)
|
||||
{
|
||||
struct wpabuf *buf;
|
||||
|
||||
buf = wpabuf_alloc(len);
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
|
||||
#ifdef __linux__
|
||||
if (mlock(wpabuf_head(buf), wpabuf_len(buf)) < 0) {
|
||||
wpa_printf(MSG_ERROR, "EXT PW: mlock failed: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
#endif /* __linux__ */
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXT_PASSWORD
|
||||
void ext_password_free(struct wpabuf *pw)
|
||||
{
|
||||
if (pw == NULL)
|
||||
return;
|
||||
os_memset(wpabuf_mhead(pw), 0, wpabuf_len(pw));
|
||||
#ifdef __linux__
|
||||
if (munlock(wpabuf_head(pw), wpabuf_len(pw)) < 0) {
|
||||
wpa_printf(MSG_ERROR, "EXT PW: munlock failed: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
#endif /* __linux__ */
|
||||
wpabuf_free(pw);
|
||||
}
|
||||
#endif /* CONFIG_EXT_PASSWORD */
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* EAP common peer/server definitions
|
||||
* Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "wpa2/eap_peer/eap_defs.h"
|
||||
#include "wpa2/eap_peer/eap_common.h"
|
||||
|
||||
/**
|
||||
* eap_hdr_len_valid - Validate EAP header length field
|
||||
* @msg: EAP frame (starting with EAP header)
|
||||
* @min_payload: Minimum payload length needed
|
||||
* Returns: 1 for valid header, 0 for invalid
|
||||
*
|
||||
* This is a helper function that does minimal validation of EAP messages. The
|
||||
* length field is verified to be large enough to include the header and not
|
||||
* too large to go beyond the end of the buffer.
|
||||
*/
|
||||
int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload)
|
||||
{
|
||||
const struct eap_hdr *hdr;
|
||||
size_t len;
|
||||
|
||||
if (msg == NULL)
|
||||
return 0;
|
||||
|
||||
hdr = wpabuf_head(msg);
|
||||
|
||||
if (wpabuf_len(msg) < sizeof(*hdr)) {
|
||||
wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = be_to_host16(hdr->length);
|
||||
if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) {
|
||||
wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_hdr_validate - Validate EAP header
|
||||
* @vendor: Expected EAP Vendor-Id (0 = IETF)
|
||||
* @eap_type: Expected EAP type number
|
||||
* @msg: EAP frame (starting with EAP header)
|
||||
* @plen: Pointer to variable to contain the returned payload length
|
||||
* Returns: Pointer to EAP payload (after type field), or %NULL on failure
|
||||
*
|
||||
* This is a helper function for EAP method implementations. This is usually
|
||||
* called in the beginning of struct eap_method::process() function to verify
|
||||
* that the received EAP request packet has a valid header. This function is
|
||||
* able to process both legacy and expanded EAP headers and in most cases, the
|
||||
* caller can just use the returned payload pointer (into *plen) for processing
|
||||
* the payload regardless of whether the packet used the expanded EAP header or
|
||||
* not.
|
||||
*/
|
||||
const u8 * eap_hdr_validate(int vendor, EapType eap_type,
|
||||
const struct wpabuf *msg, size_t *plen)
|
||||
{
|
||||
const struct eap_hdr *hdr;
|
||||
const u8 *pos;
|
||||
size_t len;
|
||||
|
||||
if (!eap_hdr_len_valid(msg, 1))
|
||||
return NULL;
|
||||
|
||||
hdr = wpabuf_head(msg);
|
||||
len = be_to_host16(hdr->length);
|
||||
pos = (const u8 *) (hdr + 1);
|
||||
|
||||
if (*pos == EAP_TYPE_EXPANDED) {
|
||||
int exp_vendor;
|
||||
u32 exp_type;
|
||||
if (len < sizeof(*hdr) + 8) {
|
||||
wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP "
|
||||
"length");
|
||||
return NULL;
|
||||
}
|
||||
pos++;
|
||||
exp_vendor = WPA_GET_BE24(pos);
|
||||
pos += 3;
|
||||
exp_type = WPA_GET_BE32(pos);
|
||||
pos += 4;
|
||||
if (exp_vendor != vendor || exp_type != (u32) eap_type) {
|
||||
wpa_printf(MSG_INFO, "EAP: Invalid expanded frame "
|
||||
"type");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*plen = len - sizeof(*hdr) - 8;
|
||||
return pos;
|
||||
} else {
|
||||
if (vendor != EAP_VENDOR_IETF || *pos != eap_type) {
|
||||
wpa_printf(MSG_INFO, "EAP: Invalid frame type");
|
||||
return NULL;
|
||||
}
|
||||
*plen = len - sizeof(*hdr) - 1;
|
||||
return pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_msg_alloc - Allocate a buffer for an EAP message
|
||||
* @vendor: Vendor-Id (0 = IETF)
|
||||
* @type: EAP type
|
||||
* @payload_len: Payload length in bytes (data after Type)
|
||||
* @code: Message Code (EAP_CODE_*)
|
||||
* @identifier: Identifier
|
||||
* Returns: Pointer to the allocated message buffer or %NULL on error
|
||||
*
|
||||
* This function can be used to allocate a buffer for an EAP message and fill
|
||||
* in the EAP header. This function is automatically using expanded EAP header
|
||||
* if the selected Vendor-Id is not IETF. In other words, most EAP methods do
|
||||
* not need to separately select which header type to use when using this
|
||||
* function to allocate the message buffers. The returned buffer has room for
|
||||
* payload_len bytes and has the EAP header and Type field already filled in.
|
||||
*/
|
||||
struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
|
||||
u8 code, u8 identifier)
|
||||
{
|
||||
struct wpabuf *buf;
|
||||
struct eap_hdr *hdr;
|
||||
size_t len;
|
||||
|
||||
len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) +
|
||||
payload_len;
|
||||
buf = wpabuf_alloc(len);
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
|
||||
hdr = wpabuf_put(buf, sizeof(*hdr));
|
||||
hdr->code = code;
|
||||
hdr->identifier = identifier;
|
||||
hdr->length = host_to_be16(len);
|
||||
|
||||
if (vendor == EAP_VENDOR_IETF) {
|
||||
wpabuf_put_u8(buf, type);
|
||||
} else {
|
||||
wpabuf_put_u8(buf, EAP_TYPE_EXPANDED);
|
||||
wpabuf_put_be24(buf, vendor);
|
||||
wpabuf_put_be32(buf, type);
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_update_len - Update EAP header length
|
||||
* @msg: EAP message from eap_msg_alloc
|
||||
*
|
||||
* This function updates the length field in the EAP header to match with the
|
||||
* current length for the buffer. This allows eap_msg_alloc() to be used to
|
||||
* allocate a larger buffer than the exact message length (e.g., if exact
|
||||
* message length is not yet known).
|
||||
*/
|
||||
void eap_update_len(struct wpabuf *msg)
|
||||
{
|
||||
struct eap_hdr *hdr;
|
||||
hdr = wpabuf_mhead(msg);
|
||||
if (wpabuf_len(msg) < sizeof(*hdr))
|
||||
return;
|
||||
hdr->length = host_to_be16(wpabuf_len(msg));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_get_id - Get EAP Identifier from wpabuf
|
||||
* @msg: Buffer starting with an EAP header
|
||||
* Returns: The Identifier field from the EAP header
|
||||
*/
|
||||
u8 eap_get_id(const struct wpabuf *msg)
|
||||
{
|
||||
const struct eap_hdr *eap;
|
||||
|
||||
if (wpabuf_len(msg) < sizeof(*eap))
|
||||
return 0;
|
||||
|
||||
eap = wpabuf_head(msg);
|
||||
return eap->identifier;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eap_get_id - Get EAP Type from wpabuf
|
||||
* @msg: Buffer starting with an EAP header
|
||||
* Returns: The EAP Type after the EAP header
|
||||
*/
|
||||
EapType eap_get_type(const struct wpabuf *msg)
|
||||
{
|
||||
if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1)
|
||||
return EAP_TYPE_NONE;
|
||||
|
||||
return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)];
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Universally Unique IDentifier (UUID)
|
||||
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "wps/utils/uuid.h"
|
||||
|
||||
int uuid_str2bin(const char *str, u8 *bin)
|
||||
{
|
||||
const char *pos;
|
||||
u8 *opos;
|
||||
|
||||
pos = str;
|
||||
opos = bin;
|
||||
|
||||
if (hexstr2bin(pos, opos, 4))
|
||||
return -1;
|
||||
pos += 8;
|
||||
opos += 4;
|
||||
|
||||
if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
|
||||
return -1;
|
||||
pos += 4;
|
||||
opos += 2;
|
||||
|
||||
if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
|
||||
return -1;
|
||||
pos += 4;
|
||||
opos += 2;
|
||||
|
||||
if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
|
||||
return -1;
|
||||
pos += 4;
|
||||
opos += 2;
|
||||
|
||||
if (*pos++ != '-' || hexstr2bin(pos, opos, 6))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uuid_bin2str(const u8 *bin, char *str, size_t max_len)
|
||||
{
|
||||
int len;
|
||||
len = snprintf(str, max_len, "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
|
||||
"%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||
bin[0], bin[1], bin[2], bin[3],
|
||||
bin[4], bin[5], bin[6], bin[7],
|
||||
bin[8], bin[9], bin[10], bin[11],
|
||||
bin[12], bin[13], bin[14], bin[15]);
|
||||
if (len < 0 || (size_t) len >= max_len)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int is_nil_uuid(const u8 *uuid)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < UUID_LEN; i++)
|
||||
if (uuid[i])
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,641 @@
|
|||
/*
|
||||
* Wi-Fi Protected Setup
|
||||
* Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
#include "wpa/includes.h"
|
||||
#include "wpa/wpa.h"
|
||||
#include "wpa/common.h"
|
||||
#include "wpa/eapol_common.h"
|
||||
#include "wpa/wpa_debug.h"
|
||||
#include "wpa/ieee802_11_defs.h"
|
||||
|
||||
#include "crypto/dh_group5.h"
|
||||
|
||||
#include "wps/wps_i.h"
|
||||
#include "wps/wps_dev_attr.h"
|
||||
|
||||
#include "wpa2/eap_peer/eap_defs.h"
|
||||
#include "wpa2/eap_peer/eap_common.h"
|
||||
|
||||
|
||||
/**
|
||||
* wps_process_msg - Process a WPS message
|
||||
* @wps: WPS Registration protocol data from wps_init()
|
||||
* @op_code: Message OP Code
|
||||
* @msg: Message data
|
||||
* Returns: Processing result
|
||||
*
|
||||
* This function is used to process WPS messages with OP Codes WSC_ACK,
|
||||
* WSC_NACK, WSC_MSG, and WSC_Done. The caller (e.g., EAP server/peer) is
|
||||
* responsible for reassembling the messages before calling this function.
|
||||
* Response to this message is built by calling wps_get_msg().
|
||||
*/
|
||||
enum wps_process_res wps_process_msg(struct wps_data *wps,
|
||||
enum wsc_op_code op_code,
|
||||
const struct wpabuf *msg)
|
||||
{
|
||||
if (wps->registrar)
|
||||
return wps_registrar_process_msg(wps, op_code, msg);
|
||||
else
|
||||
return wps_enrollee_process_msg(wps, op_code, msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wps_get_msg - Build a WPS message
|
||||
* @wps: WPS Registration protocol data from wps_init()
|
||||
* @op_code: Buffer for returning message OP Code
|
||||
* Returns: The generated WPS message or %NULL on failure
|
||||
*
|
||||
* This function is used to build a response to a message processed by calling
|
||||
* wps_process_msg(). The caller is responsible for freeing the buffer.
|
||||
*/
|
||||
struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code)
|
||||
{
|
||||
if (wps->registrar)
|
||||
return wps_registrar_get_msg(wps, op_code);
|
||||
else
|
||||
return wps_enrollee_get_msg(wps, op_code);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wps_is_selected_pbc_registrar - Check whether WPS IE indicates active PBC
|
||||
* @msg: WPS IE contents from Beacon or Probe Response frame
|
||||
* Returns: 1 if PBC Registrar is active, 0 if not
|
||||
*/
|
||||
int wps_is_selected_pbc_registrar(const struct wpabuf *msg, u8 *bssid)
|
||||
{
|
||||
struct wps_sm *sm = wps_sm_get();
|
||||
struct wps_parse_attr *attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
|
||||
int i = 0;
|
||||
|
||||
/*
|
||||
* In theory, this could also verify that attr.sel_reg_config_methods
|
||||
* includes WPS_CONFIG_PUSHBUTTON, but some deployed AP implementations
|
||||
* do not set Selected Registrar Config Methods attribute properly, so
|
||||
* it is safer to just use Device Password ID here.
|
||||
*/
|
||||
|
||||
if (wps_parse_msg(msg, attr) < 0) {
|
||||
os_free(attr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!attr->selected_registrar || *attr->selected_registrar == 0) {
|
||||
if (sm->ignore_sel_reg == false) {
|
||||
os_free(attr);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
for (i = 0; i < WPS_MAX_DIS_AP_NUM; i++) {
|
||||
if (0 == os_memcmp(sm->dis_ap_list[i].bssid, bssid, 6)) {
|
||||
wpa_printf(MSG_DEBUG, "discard ap bssid[%02x:%02x:%02x:%02x:%02x:%02x]\n", \
|
||||
bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
|
||||
os_free(attr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!attr->dev_password_id ||
|
||||
WPA_GET_BE16(attr->dev_password_id) != DEV_PW_PUSHBUTTON) {
|
||||
os_free(attr);
|
||||
return 0;
|
||||
}
|
||||
#if 0
|
||||
#ifdef CONFIG_WPS_STRICT
|
||||
if (!attr->sel_reg_config_methods ||
|
||||
!(WPA_GET_BE16(attr->sel_reg_config_methods) &
|
||||
WPS_CONFIG_PUSHBUTTON)) {
|
||||
os_free(attr);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_WPS_STRICT */
|
||||
#endif
|
||||
os_free(attr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_WPS_PIN
|
||||
|
||||
static int is_selected_pin_registrar(struct wps_parse_attr *attr, u8 *bssid)
|
||||
{
|
||||
struct wps_sm *sm = wps_sm_get();
|
||||
int i = 0;
|
||||
|
||||
if (!sm || !bssid){
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* In theory, this could also verify that attr.sel_reg_config_methods
|
||||
* includes WPS_CONFIG_LABEL, WPS_CONFIG_DISPLAY, or WPS_CONFIG_KEYPAD,
|
||||
* but some deployed AP implementations do not set Selected Registrar
|
||||
* Config Methods attribute properly, so it is safer to just use
|
||||
* Device Password ID here.
|
||||
*/
|
||||
|
||||
if (!attr->selected_registrar || *attr->selected_registrar == 0) {
|
||||
if (sm->ignore_sel_reg == false) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
for (i = 0; i < WPS_MAX_DIS_AP_NUM; i++) {
|
||||
if (0 == os_memcmp(sm->dis_ap_list[i].bssid, bssid, 6)) {
|
||||
wpa_printf(MSG_DEBUG, "discard ap bssid[%02x:%02x:%02x:%02x:%02x:%02x]\n", \
|
||||
bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (attr->dev_password_id != NULL &&
|
||||
WPA_GET_BE16(attr->dev_password_id) == DEV_PW_PUSHBUTTON) {
|
||||
return 0;
|
||||
}
|
||||
#ifdef CONFIG_WPS_STRICT
|
||||
if (!attr->sel_reg_config_methods)// ||
|
||||
//!(WPA_GET_BE16(attr->sel_reg_config_methods) &
|
||||
//(WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD)))
|
||||
return 0;
|
||||
#endif /* CONFIG_WPS_STRICT */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN
|
||||
* @msg: WPS IE contents from Beacon or Probe Response frame
|
||||
* Returns: 1 if PIN Registrar is active, 0 if not
|
||||
*/
|
||||
int wps_is_selected_pin_registrar(const struct wpabuf *msg, u8 *bssid)
|
||||
{
|
||||
struct wps_parse_attr *attr;
|
||||
int ret;
|
||||
|
||||
attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
|
||||
if (attr == NULL)
|
||||
return -99;
|
||||
|
||||
if (wps_parse_msg(msg, attr) < 0) {
|
||||
os_free(attr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = is_selected_pin_registrar(attr, bssid);
|
||||
os_free(attr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* wps_is_addr_authorized - Check whether WPS IE authorizes MAC address
|
||||
* @msg: WPS IE contents from Beacon or Probe Response frame
|
||||
* @addr: MAC address to search for
|
||||
* @ver1_compat: Whether to use version 1 compatibility mode
|
||||
* Returns: 2 if the specified address is explicit authorized, 1 if address is
|
||||
* authorized (broadcast), 0 if not
|
||||
*/
|
||||
int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
|
||||
int ver1_compat)
|
||||
{
|
||||
struct wps_sm *sm = wps_sm_get();
|
||||
struct wps_parse_attr *attr;
|
||||
int ret = 0;
|
||||
unsigned int i;
|
||||
const u8 *pos;
|
||||
const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
if (!sm){
|
||||
return -10;
|
||||
}
|
||||
|
||||
attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
|
||||
if (attr == NULL) {
|
||||
ret = -99;
|
||||
goto _out;
|
||||
}
|
||||
|
||||
if (wps_parse_msg(msg, attr) < 0) {
|
||||
ret = 0;
|
||||
goto _out;
|
||||
}
|
||||
|
||||
if (!attr->version2 && ver1_compat) {
|
||||
/*
|
||||
* Version 1.0 AP - AuthorizedMACs not used, so revert back to
|
||||
* old mechanism of using SelectedRegistrar.
|
||||
*/
|
||||
#ifdef CONFIG_WPS_PIN
|
||||
|
||||
ret = is_selected_pin_registrar(attr, sm->config.bssid);
|
||||
goto _out;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!attr->authorized_macs) {
|
||||
ret = 0;
|
||||
goto _out;
|
||||
}
|
||||
|
||||
pos = attr->authorized_macs;
|
||||
for (i = 0; i < attr->authorized_macs_len / ETH_ALEN; i++) {
|
||||
if (os_memcmp(pos, addr, ETH_ALEN) == 0) {
|
||||
ret = 2;
|
||||
goto _out;
|
||||
}
|
||||
if (os_memcmp(pos, bcast, ETH_ALEN) == 0) {
|
||||
ret = 1;
|
||||
goto _out;
|
||||
}
|
||||
pos += ETH_ALEN;
|
||||
}
|
||||
_out:
|
||||
if (attr)
|
||||
os_free(attr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wps_ap_priority_compar - Prioritize WPS IE from two APs
|
||||
* @wps_a: WPS IE contents from Beacon or Probe Response frame
|
||||
* @wps_b: WPS IE contents from Beacon or Probe Response frame
|
||||
* Returns: 1 if wps_b is considered more likely selection for WPS
|
||||
* provisioning, -1 if wps_a is considered more like, or 0 if no preference
|
||||
*/
|
||||
int wps_ap_priority_compar(const struct wpabuf *wps_a,
|
||||
const struct wpabuf *wps_b)
|
||||
{
|
||||
struct wps_parse_attr *attr_a, *attr_b;
|
||||
int sel_a, sel_b;
|
||||
int ret = 0;
|
||||
|
||||
attr_a = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
|
||||
attr_b = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
|
||||
|
||||
if (attr_a == NULL || attr_b == NULL) {
|
||||
ret = 0;
|
||||
goto _out;
|
||||
}
|
||||
|
||||
if (wps_a == NULL || wps_parse_msg(wps_a, attr_a) < 0)
|
||||
return 1;
|
||||
if (wps_b == NULL || wps_parse_msg(wps_b, attr_b) < 0)
|
||||
return -1;
|
||||
|
||||
sel_a = attr_a->selected_registrar && *attr_a->selected_registrar != 0;
|
||||
sel_b = attr_b->selected_registrar && *attr_b->selected_registrar != 0;
|
||||
|
||||
if (sel_a && !sel_b) {
|
||||
ret = -1;
|
||||
goto _out;
|
||||
}
|
||||
if (!sel_a && sel_b) {
|
||||
ret = 1;
|
||||
goto _out;
|
||||
}
|
||||
|
||||
_out:
|
||||
if (attr_a)
|
||||
os_free(attr_a);
|
||||
if (attr_b)
|
||||
os_free(attr_b);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wps_get_uuid_e - Get UUID-E from WPS IE
|
||||
* @msg: WPS IE contents from Beacon or Probe Response frame
|
||||
* Returns: Pointer to UUID-E or %NULL if not included
|
||||
*
|
||||
* The returned pointer is to the msg contents and it remains valid only as
|
||||
* long as the msg buffer is valid.
|
||||
*/
|
||||
const u8 * wps_get_uuid_e(const struct wpabuf *msg)
|
||||
{
|
||||
struct wps_parse_attr *attr;
|
||||
const u8 *uuid_e;
|
||||
|
||||
attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
|
||||
if (attr == NULL)
|
||||
return NULL;
|
||||
|
||||
if (wps_parse_msg(msg, attr) < 0) {
|
||||
uuid_e = NULL;
|
||||
} else {
|
||||
uuid_e = attr->uuid_e;
|
||||
}
|
||||
os_free(attr);
|
||||
return uuid_e;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wps_is_20 - Check whether WPS attributes claim support for WPS 2.0
|
||||
*/
|
||||
int wps_is_20(const struct wpabuf *msg)
|
||||
{
|
||||
struct wps_parse_attr *attr;
|
||||
int ret;
|
||||
|
||||
attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
|
||||
if (attr == NULL)
|
||||
return 0;
|
||||
|
||||
if (msg == NULL || wps_parse_msg(msg, attr) < 0) {
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = (attr->version2 != NULL);
|
||||
}
|
||||
os_free(attr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wps_build_assoc_req_ie - Build WPS IE for (Re)Association Request
|
||||
* @req_type: Value for Request Type attribute
|
||||
* Returns: WPS IE or %NULL on failure
|
||||
*
|
||||
* The caller is responsible for freeing the buffer.
|
||||
*/
|
||||
struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type)
|
||||
{
|
||||
struct wpabuf *ie;
|
||||
u8 *len;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association "
|
||||
"Request");
|
||||
ie = wpabuf_alloc(100);
|
||||
if (ie == NULL)
|
||||
return NULL;
|
||||
|
||||
wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
|
||||
len = wpabuf_put(ie, 1);
|
||||
wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
|
||||
|
||||
if (wps_build_version(ie) ||
|
||||
wps_build_req_type(ie, req_type) ||
|
||||
wps_build_wfa_ext(ie, 0, NULL, 0)) {
|
||||
wpabuf_free(ie);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*len = wpabuf_len(ie) - 2;
|
||||
|
||||
return ie;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wps_build_assoc_resp_ie - Build WPS IE for (Re)Association Response
|
||||
* Returns: WPS IE or %NULL on failure
|
||||
*
|
||||
* The caller is responsible for freeing the buffer.
|
||||
*/
|
||||
struct wpabuf * wps_build_assoc_resp_ie(void)
|
||||
{
|
||||
struct wpabuf *ie;
|
||||
u8 *len;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association "
|
||||
"Response");
|
||||
ie = wpabuf_alloc(100);
|
||||
if (ie == NULL)
|
||||
return NULL;
|
||||
|
||||
wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
|
||||
len = wpabuf_put(ie, 1);
|
||||
wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
|
||||
|
||||
if (wps_build_version(ie) ||
|
||||
wps_build_resp_type(ie, WPS_RESP_AP) ||
|
||||
wps_build_wfa_ext(ie, 0, NULL, 0)) {
|
||||
wpabuf_free(ie);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*len = wpabuf_len(ie) - 2;
|
||||
|
||||
return ie;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wps_build_probe_req_ie - Build WPS IE for Probe Request
|
||||
* @pw_id: Password ID (DEV_PW_PUSHBUTTON for active PBC and DEV_PW_DEFAULT for
|
||||
* most other use cases)
|
||||
* @dev: Device attributes
|
||||
* @uuid: Own UUID
|
||||
* @req_type: Value for Request Type attribute
|
||||
* @num_req_dev_types: Number of requested device types
|
||||
* @req_dev_types: Requested device types (8 * num_req_dev_types octets) or
|
||||
* %NULL if none
|
||||
* Returns: WPS IE or %NULL on failure
|
||||
*
|
||||
* The caller is responsible for freeing the buffer.
|
||||
*/
|
||||
struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
|
||||
const u8 *uuid,
|
||||
enum wps_request_type req_type,
|
||||
unsigned int num_req_dev_types,
|
||||
const u8 *req_dev_types)
|
||||
{
|
||||
struct wpabuf *ie;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for Probe Request\n");
|
||||
|
||||
ie = wpabuf_alloc(400);
|
||||
if (ie == NULL) {
|
||||
wpa_printf(MSG_ERROR, "WPS: ie alloc failed.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (wps_build_version(ie) ||
|
||||
wps_build_req_type(ie, req_type) ||
|
||||
wps_build_config_methods(ie, dev->config_methods) ||
|
||||
wps_build_uuid_e(ie, uuid) ||
|
||||
wps_build_primary_dev_type(dev, ie) ||
|
||||
wps_build_rf_bands(dev, ie) ||
|
||||
wps_build_assoc_state(NULL, ie) ||
|
||||
wps_build_config_error(ie, WPS_CFG_NO_ERROR) ||
|
||||
wps_build_dev_password_id(ie, pw_id) ||
|
||||
#ifdef CONFIG_WPS2
|
||||
wps_build_manufacturer(dev, ie) ||
|
||||
wps_build_model_name(dev, ie) ||
|
||||
wps_build_model_number(dev, ie) ||
|
||||
wps_build_dev_name(dev, ie) ||
|
||||
wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) ||
|
||||
#endif /* CONFIG_WPS2 */
|
||||
wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types)
|
||||
||
|
||||
wps_build_secondary_dev_type(dev, ie)
|
||||
) {
|
||||
wpabuf_free(ie);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_WPS2
|
||||
if (dev->p2p && wps_build_dev_name(dev, ie)) {
|
||||
wpabuf_free(ie);
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_WPS2 */
|
||||
|
||||
return wps_ie_encapsulate(ie);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_WPS_UPNP
|
||||
|
||||
void wps_free_pending_msgs(struct upnp_pending_message *msgs)
|
||||
{
|
||||
struct upnp_pending_message *p, *prev;
|
||||
p = msgs;
|
||||
while (p) {
|
||||
prev = p;
|
||||
p = p->next;
|
||||
wpabuf_free(prev->msg);
|
||||
os_free(prev);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int wps_attr_text(struct wpabuf *data, char *buf, char *end)
|
||||
{
|
||||
struct wps_parse_attr *attr;
|
||||
char *pos = buf;
|
||||
int ret;
|
||||
|
||||
attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
|
||||
if (attr == NULL)
|
||||
return -99;
|
||||
|
||||
if (wps_parse_msg(data, attr) < 0) {
|
||||
ret = -1;
|
||||
goto _out;
|
||||
}
|
||||
|
||||
if (attr->wps_state) {
|
||||
if (*attr->wps_state == WPS_STATE_NOT_CONFIGURED)
|
||||
ret = snprintf(pos, end - pos,
|
||||
"wps_state=unconfigured\n");
|
||||
else if (*attr->wps_state == WPS_STATE_CONFIGURED)
|
||||
ret = snprintf(pos, end - pos,
|
||||
"wps_state=configured\n");
|
||||
else
|
||||
ret = 0;
|
||||
if (ret < 0 || ret >= end - pos) {
|
||||
ret = pos - buf;
|
||||
goto _out;
|
||||
}
|
||||
pos += ret;
|
||||
}
|
||||
|
||||
if (attr->ap_setup_locked && *attr->ap_setup_locked) {
|
||||
ret = snprintf(pos, end - pos,
|
||||
"wps_ap_setup_locked=1\n");
|
||||
if (ret < 0 || ret >= end - pos) {
|
||||
ret = pos - buf;
|
||||
goto _out;
|
||||
}
|
||||
pos += ret;
|
||||
}
|
||||
|
||||
if (attr->selected_registrar && *attr->selected_registrar) {
|
||||
ret = snprintf(pos, end - pos,
|
||||
"wps_selected_registrar=1\n");
|
||||
if (ret < 0 || ret >= end - pos) {
|
||||
ret = pos - buf;
|
||||
goto _out;
|
||||
}
|
||||
pos += ret;
|
||||
}
|
||||
|
||||
if (attr->dev_password_id) {
|
||||
ret = snprintf(pos, end - pos,
|
||||
"wps_device_password_id=%u\n",
|
||||
WPA_GET_BE16(attr->dev_password_id));
|
||||
if (ret < 0 || ret >= end - pos) {
|
||||
ret = pos - buf;
|
||||
goto _out;
|
||||
}
|
||||
pos += ret;
|
||||
}
|
||||
|
||||
if (attr->sel_reg_config_methods) {
|
||||
ret = snprintf(pos, end - pos,
|
||||
"wps_selected_registrar_config_methods="
|
||||
"0x%04x\n",
|
||||
WPA_GET_BE16(attr->sel_reg_config_methods));
|
||||
if (ret < 0 || ret >= end - pos) {
|
||||
ret = pos - buf;
|
||||
goto _out;
|
||||
}
|
||||
pos += ret;
|
||||
}
|
||||
|
||||
if (attr->primary_dev_type) {
|
||||
char devtype[WPS_DEV_TYPE_BUFSIZE];
|
||||
ret = snprintf(pos, end - pos,
|
||||
"wps_primary_device_type=%s\n",
|
||||
wps_dev_type_bin2str(attr->primary_dev_type,
|
||||
devtype,
|
||||
sizeof(devtype)));
|
||||
if (ret < 0 || ret >= end - pos) {
|
||||
ret = pos - buf;
|
||||
goto _out;
|
||||
}
|
||||
pos += ret;
|
||||
}
|
||||
|
||||
if (attr->dev_name) {
|
||||
char *str = (char *)os_malloc(attr->dev_name_len + 1);
|
||||
size_t i;
|
||||
if (str == NULL) {
|
||||
ret = pos - buf;
|
||||
goto _out;
|
||||
}
|
||||
for (i = 0; i < attr->dev_name_len; i++) {
|
||||
if (attr->dev_name[i] < 32)
|
||||
str[i] = '_';
|
||||
else
|
||||
str[i] = attr->dev_name[i];
|
||||
}
|
||||
str[i] = '\0';
|
||||
ret = snprintf(pos, end - pos, "wps_device_name=%s\n", str);
|
||||
os_free(str);
|
||||
if (ret < 0 || ret >= end - pos) {
|
||||
ret = pos - buf;
|
||||
goto _out;
|
||||
}
|
||||
pos += ret;
|
||||
}
|
||||
|
||||
if (attr->config_methods) {
|
||||
ret = snprintf(pos, end - pos,
|
||||
"wps_config_methods=0x%04x\n",
|
||||
WPA_GET_BE16(attr->config_methods));
|
||||
if (ret < 0 || ret >= end - pos) {
|
||||
ret = pos - buf;
|
||||
goto _out;
|
||||
}
|
||||
pos += ret;
|
||||
}
|
||||
|
||||
ret = pos - buf;
|
||||
_out:
|
||||
if (attr)
|
||||
os_free(attr);
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,438 @@
|
|||
/*
|
||||
* Wi-Fi Protected Setup - attribute building
|
||||
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
#include "wpa/includes.h"
|
||||
#include "wpa/common.h"
|
||||
#include "wpa/wpa_debug.h"
|
||||
|
||||
#include "crypto/aes_wrap.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "crypto/dh_group5.h"
|
||||
#include "crypto/sha256.h"
|
||||
#include "crypto/random.h"
|
||||
|
||||
#include "wpa/ieee802_11_defs.h"
|
||||
#include "wps/wps_i.h"
|
||||
#include "soc/dport_reg.h"
|
||||
|
||||
int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg, wps_key_mode_t mode)
|
||||
{
|
||||
struct wpabuf *pubkey;
|
||||
|
||||
if (mode != WPS_CALC_KEY_NO_CALC) {
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Public Key");
|
||||
wpabuf_free(wps->dh_privkey);
|
||||
if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->dh_privkey) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Using pre-configured DH keys");
|
||||
wps->dh_privkey = wpabuf_dup(wps->wps->dh_privkey);
|
||||
wps->dh_ctx = wps->wps->dh_ctx;
|
||||
wps->wps->dh_ctx = NULL;
|
||||
pubkey = wpabuf_dup(wps->wps->dh_pubkey);
|
||||
#ifdef CONFIG_WPS_NFC
|
||||
} else if (wps->dev_pw_id >= 0x10 && wps->wps->ap &&
|
||||
wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Using NFC password token DH keys");
|
||||
wps->dh_privkey = wpabuf_dup(wps->wps->ap_nfc_dh_privkey);
|
||||
pubkey = wpabuf_dup(wps->wps->ap_nfc_dh_pubkey);
|
||||
wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, pubkey);
|
||||
#endif /* CONFIG_WPS_NFC */
|
||||
} else {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys");
|
||||
wps->dh_privkey = NULL;
|
||||
dh5_free(wps->dh_ctx);
|
||||
|
||||
wpa_printf(MSG_DEBUG, "build public key start\n");
|
||||
|
||||
wps->dh_ctx = dh5_init(&wps->dh_privkey, &pubkey);
|
||||
|
||||
wpa_printf(MSG_DEBUG, "build public key finish\n");
|
||||
|
||||
pubkey = wpabuf_zeropad(pubkey, 192);
|
||||
}
|
||||
if (wps->dh_ctx == NULL || wps->dh_privkey == NULL || pubkey == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Failed to initialize "
|
||||
"Diffie-Hellman handshake");
|
||||
wpabuf_free(pubkey);
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
|
||||
wpa_hexdump_buf(MSG_DEBUG, "WPS: DH own Public Key", pubkey);
|
||||
|
||||
if (wps->registrar) {
|
||||
wpabuf_free(wps->dh_pubkey_r);
|
||||
wps->dh_pubkey_r = pubkey;
|
||||
} else {
|
||||
wpabuf_free(wps->dh_pubkey_e);
|
||||
wps->dh_pubkey_e = pubkey;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (mode != WPS_CALC_KEY_PRE_CALC) {
|
||||
if (wps->registrar)
|
||||
pubkey = wps->dh_pubkey_r;
|
||||
else
|
||||
pubkey = wps->dh_pubkey_e;
|
||||
|
||||
wpabuf_put_be16(msg, ATTR_PUBLIC_KEY);
|
||||
wpabuf_put_be16(msg, wpabuf_len(pubkey));
|
||||
wpabuf_put_buf(msg, pubkey);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Request Type");
|
||||
wpabuf_put_be16(msg, ATTR_REQUEST_TYPE);
|
||||
wpabuf_put_be16(msg, 1);
|
||||
wpabuf_put_u8(msg, type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Response Type (%d)", type);
|
||||
wpabuf_put_be16(msg, ATTR_RESPONSE_TYPE);
|
||||
wpabuf_put_be16(msg, 1);
|
||||
wpabuf_put_u8(msg, type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_config_methods(struct wpabuf *msg, u16 methods)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods);
|
||||
wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
|
||||
wpabuf_put_be16(msg, 2);
|
||||
wpabuf_put_be16(msg, methods);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "WPS: * UUID-E");
|
||||
wpabuf_put_be16(msg, ATTR_UUID_E);
|
||||
wpabuf_put_be16(msg, WPS_UUID_LEN);
|
||||
wpabuf_put_data(msg, uuid, WPS_UUID_LEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_dev_password_id(struct wpabuf *msg, u16 id)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Device Password ID (%d)", id);
|
||||
wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
|
||||
wpabuf_put_be16(msg, 2);
|
||||
wpabuf_put_be16(msg, id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_config_error(struct wpabuf *msg, u16 err)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Configuration Error (%d)", err);
|
||||
wpabuf_put_be16(msg, ATTR_CONFIG_ERROR);
|
||||
wpabuf_put_be16(msg, 2);
|
||||
wpabuf_put_be16(msg, err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg)
|
||||
{
|
||||
u8 hash[SHA256_MAC_LEN];
|
||||
const u8 *addr[2];
|
||||
size_t len[2];
|
||||
|
||||
if (wps->last_msg == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
|
||||
"building authenticator");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
|
||||
* (M_curr* is M_curr without the Authenticator attribute)
|
||||
*/
|
||||
addr[0] = wpabuf_head(wps->last_msg);
|
||||
len[0] = wpabuf_len(wps->last_msg);
|
||||
addr[1] = wpabuf_head(msg);
|
||||
len[1] = wpabuf_len(msg);
|
||||
if (wps_crypto_funcs.hmac_sha256_vector) {
|
||||
wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, (int *)len, hash);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "Fail to register hmac sha256 vector!\r\n");
|
||||
return -1;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Authenticator");
|
||||
wpabuf_put_be16(msg, ATTR_AUTHENTICATOR);
|
||||
wpabuf_put_be16(msg, WPS_AUTHENTICATOR_LEN);
|
||||
wpabuf_put_data(msg, hash, WPS_AUTHENTICATOR_LEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_version(struct wpabuf *msg)
|
||||
{
|
||||
/*
|
||||
* Note: This attribute is deprecated and set to hardcoded 0x10 for
|
||||
* backwards compatibility reasons. The real version negotiation is
|
||||
* done with Version2.
|
||||
*/
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Version (hardcoded 0x10)");
|
||||
wpabuf_put_be16(msg, ATTR_VERSION);
|
||||
wpabuf_put_be16(msg, 1);
|
||||
wpabuf_put_u8(msg, 0x10);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
|
||||
const u8 *auth_macs, size_t auth_macs_count)
|
||||
{
|
||||
#ifdef CONFIG_WPS2
|
||||
u8 *len;
|
||||
|
||||
wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
|
||||
len = wpabuf_put(msg, 2); /* to be filled */
|
||||
wpabuf_put_be24(msg, WPS_VENDOR_ID_WFA);
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Version2 (0x%x)", WPS_VERSION);
|
||||
wpabuf_put_u8(msg, WFA_ELEM_VERSION2);
|
||||
wpabuf_put_u8(msg, 1);
|
||||
wpabuf_put_u8(msg, WPS_VERSION);
|
||||
|
||||
if (req_to_enroll) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Request to Enroll (1)");
|
||||
wpabuf_put_u8(msg, WFA_ELEM_REQUEST_TO_ENROLL);
|
||||
wpabuf_put_u8(msg, 1);
|
||||
wpabuf_put_u8(msg, 1);
|
||||
}
|
||||
|
||||
if (auth_macs && auth_macs_count) {
|
||||
size_t i;
|
||||
wpa_printf(MSG_DEBUG, "WPS: * AuthorizedMACs (count=%d)",
|
||||
(int) auth_macs_count);
|
||||
wpabuf_put_u8(msg, WFA_ELEM_AUTHORIZEDMACS);
|
||||
wpabuf_put_u8(msg, auth_macs_count * ETH_ALEN);
|
||||
wpabuf_put_data(msg, auth_macs, auth_macs_count * ETH_ALEN);
|
||||
for (i = 0; i < auth_macs_count; i++)
|
||||
wpa_printf(MSG_DEBUG, "WPS: AuthorizedMAC: " MACSTR,
|
||||
MAC2STR(&auth_macs[i * ETH_ALEN]));
|
||||
}
|
||||
|
||||
WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2);
|
||||
#endif /* CONFIG_WPS2 */
|
||||
|
||||
#ifdef CONFIG_WPS_TESTING
|
||||
if (WPS_VERSION > 0x20) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Extensibility Testing - extra "
|
||||
"attribute");
|
||||
wpabuf_put_be16(msg, ATTR_EXTENSIBILITY_TEST);
|
||||
wpabuf_put_be16(msg, 1);
|
||||
wpabuf_put_u8(msg, 42);
|
||||
}
|
||||
#endif /* CONFIG_WPS_TESTING */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Message Type (%d)", msg_type);
|
||||
wpabuf_put_be16(msg, ATTR_MSG_TYPE);
|
||||
wpabuf_put_be16(msg, 1);
|
||||
wpabuf_put_u8(msg, msg_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Enrollee Nonce");
|
||||
wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
|
||||
wpabuf_put_be16(msg, WPS_NONCE_LEN);
|
||||
wpabuf_put_data(msg, wps->nonce_e, WPS_NONCE_LEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Registrar Nonce");
|
||||
wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
|
||||
wpabuf_put_be16(msg, WPS_NONCE_LEN);
|
||||
wpabuf_put_data(msg, wps->nonce_r, WPS_NONCE_LEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
|
||||
{
|
||||
u16 auth_types = WPS_AUTH_TYPES;
|
||||
#ifdef CONFIG_WPS2
|
||||
auth_types &= ~WPS_AUTH_SHARED;
|
||||
#endif /* CONFIG_WPS2 */
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Authentication Type Flags");
|
||||
wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
|
||||
wpabuf_put_be16(msg, 2);
|
||||
wpabuf_put_be16(msg, auth_types);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg)
|
||||
{
|
||||
u16 encr_types = WPS_ENCR_TYPES;
|
||||
#ifdef CONFIG_WPS2
|
||||
encr_types &= ~WPS_ENCR_WEP;
|
||||
#endif /* CONFIG_WPS2 */
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Encryption Type Flags");
|
||||
wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
|
||||
wpabuf_put_be16(msg, 2);
|
||||
wpabuf_put_be16(msg, encr_types);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Connection Type Flags");
|
||||
wpabuf_put_be16(msg, ATTR_CONN_TYPE_FLAGS);
|
||||
wpabuf_put_be16(msg, 1);
|
||||
wpabuf_put_u8(msg, WPS_CONN_ESS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Association State");
|
||||
wpabuf_put_be16(msg, ATTR_ASSOC_STATE);
|
||||
wpabuf_put_be16(msg, 2);
|
||||
wpabuf_put_be16(msg, WPS_ASSOC_NOT_ASSOC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg)
|
||||
{
|
||||
u8 hash[SHA256_MAC_LEN];
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Key Wrap Authenticator");
|
||||
if (wps_crypto_funcs.hmac_sha256) {
|
||||
wps_crypto_funcs.hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, wpabuf_head(msg),
|
||||
wpabuf_len(msg), hash);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "Fail to register hmac sha256 function!\r\n");
|
||||
return -1;
|
||||
}
|
||||
wpabuf_put_be16(msg, ATTR_KEY_WRAP_AUTH);
|
||||
wpabuf_put_be16(msg, WPS_KWA_LEN);
|
||||
wpabuf_put_data(msg, hash, WPS_KWA_LEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
|
||||
struct wpabuf *plain)
|
||||
{
|
||||
size_t pad_len;
|
||||
const size_t block_size = 16;
|
||||
u8 *iv, *data;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Encrypted Settings");
|
||||
|
||||
/* PKCS#5 v2.0 pad */
|
||||
pad_len = block_size - wpabuf_len(plain) % block_size;
|
||||
os_memset(wpabuf_put(plain, pad_len), pad_len, pad_len);
|
||||
|
||||
wpabuf_put_be16(msg, ATTR_ENCR_SETTINGS);
|
||||
wpabuf_put_be16(msg, block_size + wpabuf_len(plain));
|
||||
|
||||
iv = wpabuf_put(msg, block_size);
|
||||
if (random_get_bytes(iv, block_size) < 0)
|
||||
return -1;
|
||||
|
||||
data = wpabuf_put(msg, 0);
|
||||
wpabuf_put_buf(msg, plain);
|
||||
wpa_printf(MSG_DEBUG, "WPS: * AES 128 Encrypted Settings");
|
||||
if (wps_crypto_funcs.aes_128_encrypt) {
|
||||
if (wps_crypto_funcs.aes_128_encrypt(wps->keywrapkey, iv, data, wpabuf_len(plain)))
|
||||
return -1;
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "Fail to register aes_128_encrypt function!\r\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_WPS_OOB
|
||||
int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id,
|
||||
const struct wpabuf *pubkey, const u8 *dev_pw,
|
||||
size_t dev_pw_len)
|
||||
{
|
||||
size_t hash_len;
|
||||
const u8 *addr[1];
|
||||
u8 pubkey_hash[WPS_HASH_LEN];
|
||||
|
||||
addr[0] = wpabuf_head(pubkey);
|
||||
hash_len = wpabuf_len(pubkey);
|
||||
if (wps_crypto_funcs.sha256_vector) {
|
||||
wps_crypto_funcs.sha256_vector(1, addr, &hash_len, pubkey_hash);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "Fail to register sha256 vector function!\r\n");
|
||||
return -1;
|
||||
}
|
||||
wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
|
||||
wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len);
|
||||
wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
|
||||
wpabuf_put_be16(msg, dev_pw_id);
|
||||
wpabuf_put_data(msg, dev_pw, dev_pw_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_WPS_OOB */
|
||||
|
||||
|
||||
/* Encapsulate WPS IE data with one (or more, if needed) IE headers */
|
||||
struct wpabuf * wps_ie_encapsulate(struct wpabuf *data)
|
||||
{
|
||||
struct wpabuf *ie;
|
||||
const u8 *pos, *end;
|
||||
|
||||
ie = wpabuf_alloc(wpabuf_len(data) + 100);
|
||||
if (ie == NULL) {
|
||||
wpabuf_free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pos = wpabuf_head(data);
|
||||
end = pos + wpabuf_len(data);
|
||||
|
||||
while (end > pos) {
|
||||
size_t frag_len = end - pos;
|
||||
if (frag_len > 251)
|
||||
frag_len = 251;
|
||||
wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
|
||||
wpabuf_put_u8(ie, 4 + frag_len);
|
||||
wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
|
||||
wpabuf_put_data(ie, pos, frag_len);
|
||||
pos += frag_len;
|
||||
}
|
||||
|
||||
wpabuf_free(data);
|
||||
|
||||
return ie;
|
||||
}
|
|
@ -0,0 +1,640 @@
|
|||
/*
|
||||
* Wi-Fi Protected Setup - attribute parsing
|
||||
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "wps/wps_defs.h"
|
||||
#include "wps/wps_attr_parse.h"
|
||||
|
||||
#ifndef CONFIG_WPS_STRICT
|
||||
#define WPS_WORKAROUNDS
|
||||
#endif /* CONFIG_WPS_STRICT */
|
||||
|
||||
|
||||
static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
|
||||
u8 id, u8 len, const u8 *pos)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "WPS: WFA subelement id=%u len=%u",
|
||||
id, len);
|
||||
switch (id) {
|
||||
case WFA_ELEM_VERSION2:
|
||||
if (len != 1) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Version2 length "
|
||||
"%u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->version2 = pos;
|
||||
break;
|
||||
case WFA_ELEM_AUTHORIZEDMACS:
|
||||
attr->authorized_macs = pos;
|
||||
attr->authorized_macs_len = len;
|
||||
break;
|
||||
case WFA_ELEM_NETWORK_KEY_SHAREABLE:
|
||||
if (len != 1) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key "
|
||||
"Shareable length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->network_key_shareable = pos;
|
||||
break;
|
||||
case WFA_ELEM_REQUEST_TO_ENROLL:
|
||||
if (len != 1) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Request to Enroll "
|
||||
"length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->request_to_enroll = pos;
|
||||
break;
|
||||
case WFA_ELEM_SETTINGS_DELAY_TIME:
|
||||
if (len != 1) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Settings Delay "
|
||||
"Time length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->settings_delay_time = pos;
|
||||
break;
|
||||
default:
|
||||
wpa_printf(MSG_DEBUG, "WPS: Skipped unknown WFA Vendor "
|
||||
"Extension subelement %u", id);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
|
||||
u16 len)
|
||||
{
|
||||
const u8 *end = pos + len;
|
||||
u8 id, elen;
|
||||
|
||||
while (pos + 2 < end) {
|
||||
id = *pos++;
|
||||
elen = *pos++;
|
||||
if (pos + elen > end)
|
||||
break;
|
||||
if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
|
||||
return -1;
|
||||
pos += elen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos,
|
||||
u16 len)
|
||||
{
|
||||
u32 vendor_id;
|
||||
|
||||
if (len < 3) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension");
|
||||
return 0;
|
||||
}
|
||||
|
||||
vendor_id = WPA_GET_BE24(pos);
|
||||
switch (vendor_id) {
|
||||
case WPS_VENDOR_ID_WFA:
|
||||
return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3);
|
||||
}
|
||||
|
||||
/* Handle unknown vendor extensions */
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: Unknown Vendor Extension (Vendor ID %u)",
|
||||
vendor_id);
|
||||
|
||||
if (len > WPS_MAX_VENDOR_EXT_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)",
|
||||
len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension "
|
||||
"attribute (max %d vendor extensions)",
|
||||
MAX_WPS_PARSE_VENDOR_EXT);
|
||||
return -1;
|
||||
}
|
||||
attr->vendor_ext[attr->num_vendor_ext] = pos;
|
||||
attr->vendor_ext_len[attr->num_vendor_ext] = len;
|
||||
attr->num_vendor_ext++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
|
||||
const u8 *pos, u16 len)
|
||||
{
|
||||
switch (type) {
|
||||
case ATTR_VERSION:
|
||||
if (len != 1) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
|
||||
len);
|
||||
return -1;
|
||||
}
|
||||
attr->version = pos;
|
||||
break;
|
||||
case ATTR_MSG_TYPE:
|
||||
if (len != 1) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
|
||||
"length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->msg_type = pos;
|
||||
break;
|
||||
case ATTR_ENROLLEE_NONCE:
|
||||
if (len != WPS_NONCE_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
|
||||
"length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->enrollee_nonce = pos;
|
||||
break;
|
||||
case ATTR_REGISTRAR_NONCE:
|
||||
if (len != WPS_NONCE_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
|
||||
"length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->registrar_nonce = pos;
|
||||
break;
|
||||
case ATTR_UUID_E:
|
||||
if (len != WPS_UUID_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
|
||||
len);
|
||||
return -1;
|
||||
}
|
||||
attr->uuid_e = pos;
|
||||
break;
|
||||
case ATTR_UUID_R:
|
||||
if (len != WPS_UUID_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
|
||||
len);
|
||||
return -1;
|
||||
}
|
||||
attr->uuid_r = pos;
|
||||
break;
|
||||
case ATTR_AUTH_TYPE_FLAGS:
|
||||
if (len != 2) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
|
||||
"Type Flags length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->auth_type_flags = pos;
|
||||
break;
|
||||
case ATTR_ENCR_TYPE_FLAGS:
|
||||
if (len != 2) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
|
||||
"Flags length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->encr_type_flags = pos;
|
||||
break;
|
||||
case ATTR_CONN_TYPE_FLAGS:
|
||||
if (len != 1) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
|
||||
"Flags length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->conn_type_flags = pos;
|
||||
break;
|
||||
case ATTR_CONFIG_METHODS:
|
||||
if (len != 2) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
|
||||
"length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->config_methods = pos;
|
||||
break;
|
||||
case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
|
||||
if (len != 2) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
|
||||
"Registrar Config Methods length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->sel_reg_config_methods = pos;
|
||||
break;
|
||||
case ATTR_PRIMARY_DEV_TYPE:
|
||||
if (len != WPS_DEV_TYPE_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
|
||||
"Type length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->primary_dev_type = pos;
|
||||
break;
|
||||
case ATTR_RF_BANDS:
|
||||
if (len != 1) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
|
||||
"%u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->rf_bands = pos;
|
||||
break;
|
||||
case ATTR_ASSOC_STATE:
|
||||
if (len != 2) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
|
||||
"length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->assoc_state = pos;
|
||||
break;
|
||||
case ATTR_CONFIG_ERROR:
|
||||
if (len != 2) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
|
||||
"Error length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->config_error = pos;
|
||||
break;
|
||||
case ATTR_DEV_PASSWORD_ID:
|
||||
if (len != 2) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
|
||||
"ID length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->dev_password_id = pos;
|
||||
break;
|
||||
case ATTR_OOB_DEVICE_PASSWORD:
|
||||
if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
|
||||
WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
|
||||
len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
|
||||
WPS_OOB_DEVICE_PASSWORD_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
|
||||
"Password length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->oob_dev_password = pos;
|
||||
attr->oob_dev_password_len = len;
|
||||
break;
|
||||
case ATTR_OS_VERSION:
|
||||
if (len != 4) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
|
||||
"%u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->os_version = pos;
|
||||
break;
|
||||
case ATTR_WPS_STATE:
|
||||
if (len != 1) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
|
||||
"Setup State length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->wps_state = pos;
|
||||
break;
|
||||
case ATTR_AUTHENTICATOR:
|
||||
if (len != WPS_AUTHENTICATOR_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
|
||||
"length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->authenticator = pos;
|
||||
break;
|
||||
case ATTR_R_HASH1:
|
||||
if (len != WPS_HASH_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
|
||||
len);
|
||||
return -1;
|
||||
}
|
||||
attr->r_hash1 = pos;
|
||||
break;
|
||||
case ATTR_R_HASH2:
|
||||
if (len != WPS_HASH_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
|
||||
len);
|
||||
return -1;
|
||||
}
|
||||
attr->r_hash2 = pos;
|
||||
break;
|
||||
case ATTR_E_HASH1:
|
||||
if (len != WPS_HASH_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
|
||||
len);
|
||||
return -1;
|
||||
}
|
||||
attr->e_hash1 = pos;
|
||||
break;
|
||||
case ATTR_E_HASH2:
|
||||
if (len != WPS_HASH_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
|
||||
len);
|
||||
return -1;
|
||||
}
|
||||
attr->e_hash2 = pos;
|
||||
break;
|
||||
case ATTR_R_SNONCE1:
|
||||
if (len != WPS_SECRET_NONCE_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
|
||||
"%u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->r_snonce1 = pos;
|
||||
break;
|
||||
case ATTR_R_SNONCE2:
|
||||
if (len != WPS_SECRET_NONCE_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
|
||||
"%u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->r_snonce2 = pos;
|
||||
break;
|
||||
case ATTR_E_SNONCE1:
|
||||
if (len != WPS_SECRET_NONCE_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
|
||||
"%u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->e_snonce1 = pos;
|
||||
break;
|
||||
case ATTR_E_SNONCE2:
|
||||
if (len != WPS_SECRET_NONCE_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
|
||||
"%u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->e_snonce2 = pos;
|
||||
break;
|
||||
case ATTR_KEY_WRAP_AUTH:
|
||||
if (len != WPS_KWA_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
|
||||
"Authenticator length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->key_wrap_auth = pos;
|
||||
break;
|
||||
case ATTR_AUTH_TYPE:
|
||||
if (len != 2) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
|
||||
"Type length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->auth_type = pos;
|
||||
break;
|
||||
case ATTR_ENCR_TYPE:
|
||||
if (len != 2) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
|
||||
"Type length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->encr_type = pos;
|
||||
break;
|
||||
case ATTR_NETWORK_INDEX:
|
||||
if (len != 1) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
|
||||
"length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->network_idx = pos;
|
||||
break;
|
||||
case ATTR_NETWORK_KEY_INDEX:
|
||||
if (len != 1) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
|
||||
"length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->network_key_idx = pos;
|
||||
break;
|
||||
case ATTR_MAC_ADDR:
|
||||
if (len != ETH_ALEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
|
||||
"length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->mac_addr = pos;
|
||||
break;
|
||||
case ATTR_KEY_PROVIDED_AUTO:
|
||||
if (len != 1) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided "
|
||||
"Automatically length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->key_prov_auto = pos;
|
||||
break;
|
||||
case ATTR_802_1X_ENABLED:
|
||||
if (len != 1) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled "
|
||||
"length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->dot1x_enabled = pos;
|
||||
break;
|
||||
case ATTR_SELECTED_REGISTRAR:
|
||||
if (len != 1) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
|
||||
" length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->selected_registrar = pos;
|
||||
break;
|
||||
case ATTR_REQUEST_TYPE:
|
||||
if (len != 1) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type "
|
||||
"length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->request_type = pos;
|
||||
break;
|
||||
case ATTR_RESPONSE_TYPE:
|
||||
if (len != 1) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type "
|
||||
"length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->response_type = pos;
|
||||
break;
|
||||
case ATTR_MANUFACTURER:
|
||||
attr->manufacturer = pos;
|
||||
attr->manufacturer_len = len;
|
||||
break;
|
||||
case ATTR_MODEL_NAME:
|
||||
attr->model_name = pos;
|
||||
attr->model_name_len = len;
|
||||
break;
|
||||
case ATTR_MODEL_NUMBER:
|
||||
attr->model_number = pos;
|
||||
attr->model_number_len = len;
|
||||
break;
|
||||
case ATTR_SERIAL_NUMBER:
|
||||
attr->serial_number = pos;
|
||||
attr->serial_number_len = len;
|
||||
break;
|
||||
case ATTR_DEV_NAME:
|
||||
attr->dev_name = pos;
|
||||
attr->dev_name_len = len;
|
||||
break;
|
||||
case ATTR_PUBLIC_KEY:
|
||||
attr->public_key = pos;
|
||||
attr->public_key_len = len;
|
||||
break;
|
||||
case ATTR_ENCR_SETTINGS:
|
||||
attr->encr_settings = pos;
|
||||
attr->encr_settings_len = len;
|
||||
break;
|
||||
case ATTR_CRED:
|
||||
if (attr->num_cred >= MAX_CRED_COUNT) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
|
||||
"attribute (max %d credentials)",
|
||||
MAX_CRED_COUNT);
|
||||
break;
|
||||
}
|
||||
attr->cred[attr->num_cred] = pos;
|
||||
attr->cred_len[attr->num_cred] = len;
|
||||
attr->num_cred++;
|
||||
break;
|
||||
case ATTR_SSID:
|
||||
attr->ssid = pos;
|
||||
attr->ssid_len = len;
|
||||
break;
|
||||
case ATTR_NETWORK_KEY:
|
||||
attr->network_key = pos;
|
||||
attr->network_key_len = len;
|
||||
break;
|
||||
case ATTR_EAP_TYPE:
|
||||
attr->eap_type = pos;
|
||||
attr->eap_type_len = len;
|
||||
break;
|
||||
case ATTR_EAP_IDENTITY:
|
||||
attr->eap_identity = pos;
|
||||
attr->eap_identity_len = len;
|
||||
break;
|
||||
case ATTR_AP_SETUP_LOCKED:
|
||||
if (len != 1) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
|
||||
"length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->ap_setup_locked = pos;
|
||||
break;
|
||||
case ATTR_REQUESTED_DEV_TYPE:
|
||||
if (len != WPS_DEV_TYPE_LEN) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device "
|
||||
"Type length %u", len);
|
||||
return -1;
|
||||
}
|
||||
if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device "
|
||||
"Type attribute (max %u types)",
|
||||
MAX_REQ_DEV_TYPE_COUNT);
|
||||
break;
|
||||
}
|
||||
attr->req_dev_type[attr->num_req_dev_type] = pos;
|
||||
attr->num_req_dev_type++;
|
||||
break;
|
||||
case ATTR_SECONDARY_DEV_TYPE_LIST:
|
||||
if (len > WPS_SEC_DEV_TYPE_MAX_LEN ||
|
||||
(len % WPS_DEV_TYPE_LEN) > 0) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device "
|
||||
"Type length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->sec_dev_type_list = pos;
|
||||
attr->sec_dev_type_list_len = len;
|
||||
break;
|
||||
case ATTR_VENDOR_EXT:
|
||||
if (wps_parse_vendor_ext(attr, pos, len) < 0)
|
||||
return -1;
|
||||
break;
|
||||
case ATTR_AP_CHANNEL:
|
||||
if (len != 2) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel "
|
||||
"length %u", len);
|
||||
return -1;
|
||||
}
|
||||
attr->ap_channel = pos;
|
||||
break;
|
||||
default:
|
||||
wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
|
||||
"len=%u", type, len);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
|
||||
{
|
||||
const u8 *pos, *end;
|
||||
u16 type, len;
|
||||
#ifdef WPS_WORKAROUNDS
|
||||
u16 prev_type = 0;
|
||||
#endif /* WPS_WORKAROUNDS */
|
||||
|
||||
os_memset(attr, 0, sizeof(*attr));
|
||||
pos = wpabuf_head(msg);
|
||||
end = pos + wpabuf_len(msg);
|
||||
|
||||
while (pos < end) {
|
||||
if (end - pos < 4) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
|
||||
"%lu bytes remaining",
|
||||
(unsigned long) (end - pos));
|
||||
return -1;
|
||||
}
|
||||
|
||||
type = WPA_GET_BE16(pos);
|
||||
pos += 2;
|
||||
len = WPA_GET_BE16(pos);
|
||||
pos += 2;
|
||||
wpa_printf(MSG_DEBUG, "WPS: attr type=0x%x len=%u",
|
||||
type, len);
|
||||
if (len > end - pos) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
|
||||
wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg);
|
||||
#ifdef WPS_WORKAROUNDS
|
||||
/*
|
||||
* Some deployed APs seem to have a bug in encoding of
|
||||
* Network Key attribute in the Credential attribute
|
||||
* where they add an extra octet after the Network Key
|
||||
* attribute at least when open network is being
|
||||
* provisioned.
|
||||
*/
|
||||
if ((type & 0xff00) != 0x1000 &&
|
||||
prev_type == ATTR_NETWORK_KEY) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Workaround - try "
|
||||
"to skip unexpected octet after "
|
||||
"Network Key");
|
||||
pos -= 3;
|
||||
continue;
|
||||
}
|
||||
#endif /* WPS_WORKAROUNDS */
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef WPS_WORKAROUNDS
|
||||
if (type == 0 && len == 0) {
|
||||
/*
|
||||
* Mac OS X 10.6 seems to be adding 0x00 padding to the
|
||||
* end of M1. Skip those to avoid interop issues.
|
||||
*/
|
||||
int i;
|
||||
for (i = 0; i < end - pos; i++) {
|
||||
if (pos[i])
|
||||
break;
|
||||
}
|
||||
if (i == end - pos) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Workaround - skip "
|
||||
"unexpected message padding");
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* WPS_WORKAROUNDS */
|
||||
|
||||
if (wps_set_attr(attr, type, pos, len) < 0)
|
||||
return -1;
|
||||
|
||||
#ifdef WPS_WORKAROUNDS
|
||||
prev_type = type;
|
||||
#endif /* WPS_WORKAROUNDS */
|
||||
pos += len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,351 @@
|
|||
/*
|
||||
* Wi-Fi Protected Setup - attribute processing
|
||||
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
#include "wpa/includes.h"
|
||||
|
||||
#include "wpa/common.h"
|
||||
#include "crypto/sha256.h"
|
||||
#include "wps/wps_i.h"
|
||||
|
||||
|
||||
int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
|
||||
const struct wpabuf *msg)
|
||||
{
|
||||
u8 hash[SHA256_MAC_LEN];
|
||||
const u8 *addr[2];
|
||||
size_t len[2];
|
||||
|
||||
if (authenticator == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: No Authenticator attribute "
|
||||
"included");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (wps->last_msg == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
|
||||
"validating authenticator");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
|
||||
* (M_curr* is M_curr without the Authenticator attribute)
|
||||
*/
|
||||
addr[0] = wpabuf_head(wps->last_msg);
|
||||
len[0] = wpabuf_len(wps->last_msg);
|
||||
addr[1] = wpabuf_head(msg);
|
||||
len[1] = wpabuf_len(msg) - 4 - WPS_AUTHENTICATOR_LEN;
|
||||
if (wps_crypto_funcs.hmac_sha256_vector) {
|
||||
wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, (int *)len, hash);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "Fail to register hmac_sha256_vector function!\r\n");
|
||||
return -1;
|
||||
}
|
||||
if (os_memcmp(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Incorrect Authenticator");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
|
||||
const u8 *key_wrap_auth)
|
||||
{
|
||||
u8 hash[SHA256_MAC_LEN];
|
||||
const u8 *head;
|
||||
size_t len;
|
||||
|
||||
if (key_wrap_auth == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: No KWA in decrypted attribute");
|
||||
return -1;
|
||||
}
|
||||
|
||||
head = wpabuf_head(msg);
|
||||
len = wpabuf_len(msg) - 4 - WPS_KWA_LEN;
|
||||
if (head + len != key_wrap_auth - 4) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: KWA not in the end of the "
|
||||
"decrypted attribute");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (wps_crypto_funcs.hmac_sha256) {
|
||||
wps_crypto_funcs.hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, head, len, hash);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "Fail to register hmac sha256 function!\r\n");
|
||||
return -1;
|
||||
}
|
||||
if (os_memcmp(hash, key_wrap_auth, WPS_KWA_LEN) != 0) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid KWA");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_process_cred_network_idx(struct wps_credential *cred,
|
||||
const u8 *idx)
|
||||
{
|
||||
if (idx == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
|
||||
"Network Index");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: Network Index: %d", *idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_process_cred_ssid(struct wps_credential *cred, const u8 *ssid,
|
||||
size_t ssid_len)
|
||||
{
|
||||
if (ssid == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Credential did not include SSID");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Remove zero-padding since some Registrar implementations seem to use
|
||||
* hardcoded 32-octet length for this attribute */
|
||||
while (ssid_len > 0 && ssid[ssid_len - 1] == 0)
|
||||
ssid_len--;
|
||||
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", ssid, ssid_len);
|
||||
if (ssid_len <= sizeof(cred->ssid)) {
|
||||
os_memcpy(cred->ssid, ssid, ssid_len);
|
||||
cred->ssid_len = ssid_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_process_cred_auth_type(struct wps_credential *cred,
|
||||
const u8 *auth_type)
|
||||
{
|
||||
if (auth_type == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
|
||||
"Authentication Type");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cred->auth_type = WPA_GET_BE16(auth_type);
|
||||
wpa_printf(MSG_DEBUG, "WPS: Authentication Type: 0x%x",
|
||||
cred->auth_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_process_cred_encr_type(struct wps_credential *cred,
|
||||
const u8 *encr_type)
|
||||
{
|
||||
if (encr_type == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
|
||||
"Encryption Type");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cred->encr_type = WPA_GET_BE16(encr_type);
|
||||
wpa_printf(MSG_DEBUG, "WPS: Encryption Type: 0x%x",
|
||||
cred->encr_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_process_cred_network_key_idx(struct wps_credential *cred,
|
||||
const u8 *key_idx)
|
||||
{
|
||||
if (key_idx == NULL)
|
||||
return 0; /* optional attribute */
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: Network Key Index: %d", *key_idx);
|
||||
cred->key_idx = *key_idx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_process_cred_network_key(struct wps_credential *cred,
|
||||
const u8 *key, size_t key_len)
|
||||
{
|
||||
if (key == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
|
||||
"Network Key");
|
||||
if (cred->auth_type == WPS_WIFI_AUTH_OPEN &&
|
||||
cred->encr_type == WPS_ENCR_NONE) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Workaround - Allow "
|
||||
"missing mandatory Network Key attribute "
|
||||
"for open network");
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", key, key_len);
|
||||
if (key_len <= sizeof(cred->key)) {
|
||||
os_memcpy(cred->key, key, key_len);
|
||||
cred->key_len = key_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_process_cred_mac_addr(struct wps_credential *cred,
|
||||
const u8 *mac_addr)
|
||||
{
|
||||
if (mac_addr == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
|
||||
"MAC Address");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, MAC2STR(mac_addr));
|
||||
os_memcpy(cred->mac_addr, mac_addr, ETH_ALEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_process_cred_eap_type(struct wps_credential *cred,
|
||||
const u8 *eap_type, size_t eap_type_len)
|
||||
{
|
||||
if (eap_type == NULL)
|
||||
return 0; /* optional attribute */
|
||||
|
||||
wpa_hexdump(MSG_DEBUG, "WPS: EAP Type", eap_type, eap_type_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_process_cred_eap_identity(struct wps_credential *cred,
|
||||
const u8 *identity,
|
||||
size_t identity_len)
|
||||
{
|
||||
if (identity == NULL)
|
||||
return 0; /* optional attribute */
|
||||
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "WPS: EAP Identity",
|
||||
identity, identity_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_process_cred_key_prov_auto(struct wps_credential *cred,
|
||||
const u8 *key_prov_auto)
|
||||
{
|
||||
if (key_prov_auto == NULL)
|
||||
return 0; /* optional attribute */
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: Key Provided Automatically: %d",
|
||||
*key_prov_auto);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_process_cred_802_1x_enabled(struct wps_credential *cred,
|
||||
const u8 *dot1x_enabled)
|
||||
{
|
||||
if (dot1x_enabled == NULL)
|
||||
return 0; /* optional attribute */
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: 802.1X Enabled: %d", *dot1x_enabled);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_process_cred_ap_channel(struct wps_credential *cred,
|
||||
const u8 *ap_channel)
|
||||
{
|
||||
if (ap_channel == NULL)
|
||||
return 0; /* optional attribute */
|
||||
|
||||
cred->ap_channel = WPA_GET_BE16(ap_channel);
|
||||
wpa_printf(MSG_DEBUG, "WPS: AP Channel: %u", cred->ap_channel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_workaround_cred_key(struct wps_credential *cred)
|
||||
{
|
||||
if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) &&
|
||||
cred->key_len > 8 && cred->key_len < 64 &&
|
||||
cred->key[cred->key_len - 1] == 0) {
|
||||
#ifdef CONFIG_WPS_STRICT
|
||||
wpa_printf(MSG_INFO, "WPS: WPA/WPA2-Personal passphrase uses "
|
||||
"forbidden NULL termination");
|
||||
wpa_hexdump_ascii_key(MSG_INFO, "WPS: Network Key",
|
||||
cred->key, cred->key_len);
|
||||
return -1;
|
||||
#else /* CONFIG_WPS_STRICT */
|
||||
/*
|
||||
* A deployed external registrar is known to encode ASCII
|
||||
* passphrases incorrectly. Remove the extra NULL termination
|
||||
* to fix the encoding.
|
||||
*/
|
||||
wpa_printf(MSG_DEBUG, "WPS: Workaround - remove NULL "
|
||||
"termination from ASCII passphrase");
|
||||
cred->key_len--;
|
||||
#endif /* CONFIG_WPS_STRICT */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_process_cred(struct wps_parse_attr *attr,
|
||||
struct wps_credential *cred)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "WPS: Process Credential");
|
||||
|
||||
/* TODO: support multiple Network Keys */
|
||||
if (wps_process_cred_network_idx(cred, attr->network_idx) ||
|
||||
wps_process_cred_ssid(cred, attr->ssid, attr->ssid_len) ||
|
||||
wps_process_cred_auth_type(cred, attr->auth_type) ||
|
||||
wps_process_cred_encr_type(cred, attr->encr_type) ||
|
||||
wps_process_cred_network_key_idx(cred, attr->network_key_idx) ||
|
||||
wps_process_cred_network_key(cred, attr->network_key,
|
||||
attr->network_key_len) ||
|
||||
wps_process_cred_mac_addr(cred, attr->mac_addr) ||
|
||||
wps_process_cred_eap_type(cred, attr->eap_type,
|
||||
attr->eap_type_len) ||
|
||||
wps_process_cred_eap_identity(cred, attr->eap_identity,
|
||||
attr->eap_identity_len) ||
|
||||
wps_process_cred_key_prov_auto(cred, attr->key_prov_auto) ||
|
||||
wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled) ||
|
||||
wps_process_cred_ap_channel(cred, attr->ap_channel))
|
||||
return -1;
|
||||
|
||||
return wps_workaround_cred_key(cred);
|
||||
}
|
||||
|
||||
|
||||
int wps_process_ap_settings(struct wps_parse_attr *attr,
|
||||
struct wps_credential *cred)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "WPS: Processing AP Settings");
|
||||
os_memset(cred, 0, sizeof(*cred));
|
||||
/* TODO: optional attributes New Password and Device Password ID */
|
||||
if (wps_process_cred_ssid(cred, attr->ssid, attr->ssid_len) ||
|
||||
wps_process_cred_auth_type(cred, attr->auth_type) ||
|
||||
wps_process_cred_encr_type(cred, attr->encr_type) ||
|
||||
wps_process_cred_network_key_idx(cred, attr->network_key_idx) ||
|
||||
wps_process_cred_network_key(cred, attr->network_key,
|
||||
attr->network_key_len) ||
|
||||
wps_process_cred_mac_addr(cred, attr->mac_addr))
|
||||
return -1;
|
||||
|
||||
return wps_workaround_cred_key(cred);
|
||||
}
|
|
@ -0,0 +1,671 @@
|
|||
/*
|
||||
* Wi-Fi Protected Setup - common functionality
|
||||
* Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
#include "wpa/includes.h"
|
||||
#include "wpa/common.h"
|
||||
|
||||
#include "crypto/aes_wrap.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "crypto/dh_group5.h"
|
||||
#include "crypto/sha1.h"
|
||||
#include "crypto/sha256.h"
|
||||
#include "crypto/random.h"
|
||||
|
||||
#include "wps/wps_i.h"
|
||||
|
||||
void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
|
||||
const char *label, u8 *res, size_t res_len)
|
||||
{
|
||||
u8 i_buf[4], key_bits[4];
|
||||
const u8 *addr[4];
|
||||
size_t len[4];
|
||||
int i, iter;
|
||||
u8 hash[SHA256_MAC_LEN], *opos;
|
||||
size_t left;
|
||||
|
||||
WPA_PUT_BE32(key_bits, res_len * 8);
|
||||
|
||||
addr[0] = i_buf;
|
||||
len[0] = sizeof(i_buf);
|
||||
addr[1] = label_prefix;
|
||||
len[1] = label_prefix_len;
|
||||
addr[2] = (const u8 *) label;
|
||||
len[2] = os_strlen(label);
|
||||
addr[3] = key_bits;
|
||||
len[3] = sizeof(key_bits);
|
||||
|
||||
iter = (res_len + SHA256_MAC_LEN - 1) / SHA256_MAC_LEN;
|
||||
opos = res;
|
||||
left = res_len;
|
||||
|
||||
for (i = 1; i <= iter; i++) {
|
||||
WPA_PUT_BE32(i_buf, i);
|
||||
if (wps_crypto_funcs.hmac_sha256_vector) {
|
||||
wps_crypto_funcs.hmac_sha256_vector(key, SHA256_MAC_LEN, 4, addr, (int *)len, hash);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "In function %s, fail to reigster hmac sha256 vector function!\r\n", __FUNCTION__);
|
||||
return ;
|
||||
}
|
||||
if (i < iter) {
|
||||
os_memcpy(opos, hash, SHA256_MAC_LEN);
|
||||
opos += SHA256_MAC_LEN;
|
||||
left -= SHA256_MAC_LEN;
|
||||
} else
|
||||
os_memcpy(opos, hash, left);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int wps_derive_keys(struct wps_data *wps)
|
||||
{
|
||||
struct wpabuf *pubkey, *dh_shared;
|
||||
u8 dhkey[SHA256_MAC_LEN], kdk[SHA256_MAC_LEN];
|
||||
const u8 *addr[3];
|
||||
size_t len[3];
|
||||
u8 keys[WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN + WPS_EMSK_LEN];
|
||||
|
||||
if (wps->dh_privkey == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Own DH private key not available");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pubkey = wps->registrar ? wps->dh_pubkey_e : wps->dh_pubkey_r;
|
||||
if (pubkey == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Peer DH public key not available");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
|
||||
wpa_hexdump_buf(MSG_DEBUG, "WPS: DH peer Public Key", pubkey);
|
||||
dh_shared = dh5_derive_shared(wps->dh_ctx, pubkey, wps->dh_privkey);
|
||||
dh5_free(wps->dh_ctx);
|
||||
wps->dh_ctx = NULL;
|
||||
dh_shared = wpabuf_zeropad(dh_shared, 192);
|
||||
if (dh_shared == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Failed to derive DH shared key");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Own DH private key is not needed anymore */
|
||||
/*
|
||||
* due to the public key calculated when wps start, it will not calculate anymore even when we build M1 message, also calculate the key need take a long time
|
||||
* which would cause WPS fail, so we clean the key after WPS finished .
|
||||
*/
|
||||
#ifndef ESP32_WORKAROUND
|
||||
wpabuf_free(wps->dh_privkey);
|
||||
wps->dh_privkey = NULL;
|
||||
#endif //ESP32_WORKAROUND
|
||||
|
||||
wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH shared key", dh_shared);
|
||||
|
||||
/* DHKey = SHA-256(g^AB mod p) */
|
||||
addr[0] = wpabuf_head(dh_shared);
|
||||
len[0] = wpabuf_len(dh_shared);
|
||||
|
||||
if (wps_crypto_funcs.sha256_vector) {
|
||||
wps_crypto_funcs.sha256_vector(1, addr, (int *)len, dhkey);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "In function %s, Fail to register sha256 vector function!\r\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump_key(MSG_DEBUG, "WPS: DHKey", dhkey, sizeof(dhkey));
|
||||
wpabuf_free(dh_shared);
|
||||
|
||||
/* KDK = HMAC-SHA-256_DHKey(N1 || EnrolleeMAC || N2) */
|
||||
addr[0] = wps->nonce_e;
|
||||
len[0] = WPS_NONCE_LEN;
|
||||
addr[1] = wps->mac_addr_e;
|
||||
len[1] = ETH_ALEN;
|
||||
addr[2] = wps->nonce_r;
|
||||
len[2] = WPS_NONCE_LEN;
|
||||
if (wps_crypto_funcs.hmac_sha256_vector) {
|
||||
wps_crypto_funcs.hmac_sha256_vector(dhkey, sizeof(dhkey), 3, addr, (int *)len, kdk);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "In function %s, Fail to register hmac sha256 vector function!\r\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump_key(MSG_DEBUG, "WPS: KDK", kdk, sizeof(kdk));
|
||||
|
||||
wps_kdf(kdk, NULL, 0, "Wi-Fi Easy and Secure Key Derivation",
|
||||
keys, sizeof(keys));
|
||||
os_memcpy(wps->authkey, keys, WPS_AUTHKEY_LEN);
|
||||
os_memcpy(wps->keywrapkey, keys + WPS_AUTHKEY_LEN, WPS_KEYWRAPKEY_LEN);
|
||||
os_memcpy(wps->emsk, keys + WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN,
|
||||
WPS_EMSK_LEN);
|
||||
|
||||
wpa_hexdump_key(MSG_DEBUG, "WPS: AuthKey",
|
||||
wps->authkey, WPS_AUTHKEY_LEN);
|
||||
wpa_hexdump_key(MSG_DEBUG, "WPS: KeyWrapKey",
|
||||
wps->keywrapkey, WPS_KEYWRAPKEY_LEN);
|
||||
wpa_hexdump_key(MSG_DEBUG, "WPS: EMSK", wps->emsk, WPS_EMSK_LEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
|
||||
size_t dev_passwd_len)
|
||||
{
|
||||
u8 hash[SHA256_MAC_LEN];
|
||||
|
||||
if (wps_crypto_funcs.hmac_sha256) {
|
||||
wps_crypto_funcs.hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd,
|
||||
(dev_passwd_len + 1) / 2, hash);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256 function!\r\n", __FUNCTION__);
|
||||
return ;
|
||||
}
|
||||
os_memcpy(wps->psk1, hash, WPS_PSK_LEN);
|
||||
if (wps_crypto_funcs.hmac_sha256) {
|
||||
wps_crypto_funcs.hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN,
|
||||
dev_passwd + (dev_passwd_len + 1) / 2,
|
||||
dev_passwd_len / 2, hash);
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256 function!\r\n", __FUNCTION__);
|
||||
return ;
|
||||
}
|
||||
os_memcpy(wps->psk2, hash, WPS_PSK_LEN);
|
||||
|
||||
wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Device Password",
|
||||
dev_passwd, dev_passwd_len);
|
||||
wpa_hexdump_key(MSG_DEBUG, "WPS: PSK1", wps->psk1, WPS_PSK_LEN);
|
||||
wpa_hexdump_key(MSG_DEBUG, "WPS: PSK2", wps->psk2, WPS_PSK_LEN);
|
||||
}
|
||||
|
||||
|
||||
struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
|
||||
size_t encr_len)
|
||||
{
|
||||
struct wpabuf *decrypted;
|
||||
const size_t block_size = 16;
|
||||
size_t i;
|
||||
u8 pad;
|
||||
const u8 *pos;
|
||||
|
||||
/* AES-128-CBC */
|
||||
if (encr == NULL || encr_len < 2 * block_size || encr_len % block_size)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "WPS: No Encrypted Settings received");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
decrypted = wpabuf_alloc(encr_len - block_size);
|
||||
if (decrypted == NULL)
|
||||
return NULL;
|
||||
|
||||
wpa_hexdump(MSG_MSGDUMP, "WPS: Encrypted Settings", encr, encr_len);
|
||||
wpabuf_put_data(decrypted, encr + block_size, encr_len - block_size);
|
||||
wpa_printf(MSG_DEBUG, "WPS: AES Decrypt setting");
|
||||
if (wps_crypto_funcs.aes_128_decrypt) {
|
||||
if (wps_crypto_funcs.aes_128_decrypt(wps->keywrapkey, encr, wpabuf_mhead(decrypted),
|
||||
wpabuf_len(decrypted))) {
|
||||
wpabuf_free(decrypted);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
wpa_printf(MSG_ERROR, "In function %s, fail to register aes 128 decrypt function!\r\n", __FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wpa_hexdump_buf_key(MSG_MSGDUMP, "WPS: Decrypted Encrypted Settings",
|
||||
decrypted);
|
||||
|
||||
pos = wpabuf_head_u8(decrypted) + wpabuf_len(decrypted) - 1;
|
||||
pad = *pos;
|
||||
if (pad > wpabuf_len(decrypted)) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad value");
|
||||
wpabuf_free(decrypted);
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < pad; i++) {
|
||||
if (*pos-- != pad) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad "
|
||||
"string");
|
||||
wpabuf_free(decrypted);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
decrypted->used -= pad;
|
||||
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_WPS_PIN
|
||||
/**
|
||||
* wps_pin_checksum - Compute PIN checksum
|
||||
* @pin: Seven digit PIN (i.e., eight digit PIN without the checksum digit)
|
||||
* Returns: Checksum digit
|
||||
*/
|
||||
unsigned int wps_pin_checksum(unsigned int pin)
|
||||
{
|
||||
unsigned int accum = 0;
|
||||
while (pin) {
|
||||
accum += 3 * (pin % 10);
|
||||
pin /= 10;
|
||||
accum += pin % 10;
|
||||
pin /= 10;
|
||||
}
|
||||
|
||||
return (10 - accum % 10) % 10;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wps_pin_valid - Check whether a PIN has a valid checksum
|
||||
* @pin: Eight digit PIN (i.e., including the checksum digit)
|
||||
* Returns: 1 if checksum digit is valid, or 0 if not
|
||||
*/
|
||||
unsigned int wps_pin_valid(unsigned int pin)
|
||||
{
|
||||
return wps_pin_checksum(pin / 10) == (pin % 10);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wps_generate_pin - Generate a random PIN
|
||||
* Returns: Eight digit PIN (i.e., including the checksum digit)
|
||||
*/
|
||||
unsigned int wps_generate_pin(void)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
/* Generate seven random digits for the PIN */
|
||||
if (random_get_bytes((unsigned char *) &val, sizeof(val)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
val %= 10000000;
|
||||
|
||||
/* Append checksum digit */
|
||||
return val * 10 + wps_pin_checksum(val);
|
||||
}
|
||||
|
||||
|
||||
int wps_pin_str_valid(const char *pin)
|
||||
{
|
||||
const char *p;
|
||||
size_t len;
|
||||
|
||||
p = pin;
|
||||
while (*p >= '0' && *p <= '9')
|
||||
p++;
|
||||
if (*p != '\0')
|
||||
return 0;
|
||||
|
||||
len = p - pin;
|
||||
return len == 4 || len == 8;
|
||||
}
|
||||
#endif
|
||||
|
||||
void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
|
||||
u16 config_error, u16 error_indication)
|
||||
{
|
||||
union wps_event_data *data;
|
||||
|
||||
data = (union wps_event_data *)os_zalloc(sizeof(union wps_event_data));
|
||||
if (data == NULL)
|
||||
return;
|
||||
|
||||
if (wps->event_cb == NULL) {
|
||||
os_free(data);
|
||||
return;
|
||||
}
|
||||
|
||||
os_memset(data, 0, sizeof(union wps_event_data));
|
||||
data->fail.msg = msg;
|
||||
data->fail.config_error = config_error;
|
||||
data->fail.error_indication = error_indication;
|
||||
wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, data);
|
||||
os_free(data);
|
||||
}
|
||||
|
||||
|
||||
void wps_success_event(struct wps_context *wps)
|
||||
{
|
||||
if (wps->event_cb == NULL)
|
||||
return;
|
||||
|
||||
wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
|
||||
void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part)
|
||||
{
|
||||
union wps_event_data *data;
|
||||
|
||||
data = (union wps_event_data *)os_zalloc(sizeof(union wps_event_data));
|
||||
if (data == NULL)
|
||||
return;
|
||||
|
||||
if (wps->event_cb == NULL) {
|
||||
os_free(data);
|
||||
return;
|
||||
}
|
||||
|
||||
os_memset(data, 0, sizeof(union wps_event_data));
|
||||
data->pwd_auth_fail.enrollee = enrollee;
|
||||
data->pwd_auth_fail.part = part;
|
||||
wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, data);
|
||||
os_free(data);
|
||||
}
|
||||
|
||||
|
||||
void wps_pbc_overlap_event(struct wps_context *wps)
|
||||
{
|
||||
if (wps->event_cb == NULL)
|
||||
return;
|
||||
|
||||
wps->event_cb(wps->cb_ctx, WPS_EV_PBC_OVERLAP, NULL);
|
||||
}
|
||||
|
||||
|
||||
void wps_pbc_timeout_event(struct wps_context *wps)
|
||||
{
|
||||
if (wps->event_cb == NULL)
|
||||
return;
|
||||
|
||||
wps->event_cb(wps->cb_ctx, WPS_EV_PBC_TIMEOUT, NULL);
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_WPS_OOB
|
||||
|
||||
struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
|
||||
{
|
||||
struct wps_data *data;
|
||||
struct wpabuf *plain;
|
||||
|
||||
data = (struct wps_data *)os_zalloc(sizeof(struct wps_data));
|
||||
if (data == NULL)
|
||||
return NULL;
|
||||
|
||||
plain = wpabuf_alloc(500);
|
||||
if (plain == NULL) {
|
||||
os_free(data);
|
||||
wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
|
||||
"credential");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
os_memset(data, 0, sizeof(struct wps_data));
|
||||
data->wps = wps;
|
||||
data->auth_type = wps->auth_types;
|
||||
data->encr_type = wps->encr_types;
|
||||
if (wps_build_version(plain) ||
|
||||
wps_build_cred(data, plain) ||
|
||||
wps_build_wfa_ext(plain, 0, NULL, 0)) {
|
||||
wpabuf_free(plain);
|
||||
os_free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
os_free(data);
|
||||
return plain;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_WPS_NFC
|
||||
|
||||
struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
|
||||
const struct wpabuf *pubkey,
|
||||
const struct wpabuf *dev_pw)
|
||||
{
|
||||
struct wpabuf *data;
|
||||
|
||||
data = wpabuf_alloc(200);
|
||||
if (data == NULL)
|
||||
return NULL;
|
||||
|
||||
if (wps_build_version(data) ||
|
||||
wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
|
||||
wpabuf_head(dev_pw), wpabuf_len(dev_pw)) ||
|
||||
wps_build_wfa_ext(data, 0, NULL, 0)) {
|
||||
wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password "
|
||||
"token");
|
||||
wpabuf_free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr)
|
||||
{
|
||||
struct wpabuf msg;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < attr->num_cred; i++) {
|
||||
struct wps_credential local_cred;
|
||||
struct wps_parse_attr cattr;
|
||||
|
||||
os_memset(&local_cred, 0, sizeof(local_cred));
|
||||
wpabuf_set(&msg, attr->cred[i], attr->cred_len[i]);
|
||||
if (wps_parse_msg(&msg, &cattr) < 0 ||
|
||||
wps_process_cred(&cattr, &local_cred)) {
|
||||
wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB "
|
||||
"credential");
|
||||
return -1;
|
||||
}
|
||||
wps->cred_cb(wps->cb_ctx, &local_cred);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#endif /* CONFIG_WPS_OOB */
|
||||
|
||||
|
||||
int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN])
|
||||
{
|
||||
const char *pos;
|
||||
|
||||
/* <categ>-<OUI>-<subcateg> */
|
||||
WPA_PUT_BE16(dev_type, atoi(str));
|
||||
pos = (char *)os_strchr(str, '-');
|
||||
if (pos == NULL)
|
||||
return -1;
|
||||
pos++;
|
||||
if (hexstr2bin(pos, &dev_type[2], 4))
|
||||
return -1;
|
||||
pos = (char *)os_strchr(pos, '-');
|
||||
if (pos == NULL)
|
||||
return -1;
|
||||
pos++;
|
||||
WPA_PUT_BE16(&dev_type[6], atoi(pos));
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
|
||||
size_t buf_len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = snprintf(buf, buf_len, "%u-%08X-%u",
|
||||
WPA_GET_BE16(dev_type), WPA_GET_BE32(&dev_type[2]),
|
||||
WPA_GET_BE16(&dev_type[6]));
|
||||
if (ret < 0 || (unsigned int) ret >= buf_len)
|
||||
return NULL;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid)
|
||||
{
|
||||
const u8 *addr[2];
|
||||
size_t len[2];
|
||||
u8 hash[SHA1_MAC_LEN];
|
||||
u8 nsid[16] = {
|
||||
0x52, 0x64, 0x80, 0xf8,
|
||||
0xc9, 0x9b,
|
||||
0x4b, 0xe5,
|
||||
0xa6, 0x55,
|
||||
0x58, 0xed, 0x5f, 0x5d, 0x60, 0x84
|
||||
};
|
||||
|
||||
addr[0] = nsid;
|
||||
len[0] = sizeof(nsid);
|
||||
addr[1] = mac_addr;
|
||||
len[1] = 6;
|
||||
sha1_vector(2, addr, len, hash);
|
||||
os_memcpy(uuid, hash, 16);
|
||||
|
||||
/* Version: 5 = named-based version using SHA-1 */
|
||||
uuid[6] = (5 << 4) | (uuid[6] & 0x0f);
|
||||
|
||||
/* Variant specified in RFC 4122 */
|
||||
uuid[8] = 0x80 | (uuid[8] & 0x3f);
|
||||
}
|
||||
|
||||
|
||||
u16 wps_config_methods_str2bin(const char *str)
|
||||
{
|
||||
u16 methods = 0;
|
||||
|
||||
if (str == NULL) {
|
||||
/* Default to enabling methods based on build configuration */
|
||||
methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
|
||||
#ifdef CONFIG_WPS2
|
||||
methods |= WPS_CONFIG_VIRT_DISPLAY;
|
||||
#endif /* CONFIG_WPS2 */
|
||||
#ifdef CONFIG_WPS_NFC
|
||||
methods |= WPS_CONFIG_NFC_INTERFACE;
|
||||
#endif /* CONFIG_WPS_NFC */
|
||||
} else {
|
||||
if (os_strstr(str, "ethernet"))
|
||||
methods |= WPS_CONFIG_ETHERNET;
|
||||
if (os_strstr(str, "label"))
|
||||
methods |= WPS_CONFIG_LABEL;
|
||||
if (os_strstr(str, "display"))
|
||||
methods |= WPS_CONFIG_DISPLAY;
|
||||
if (os_strstr(str, "ext_nfc_token"))
|
||||
methods |= WPS_CONFIG_EXT_NFC_TOKEN;
|
||||
if (os_strstr(str, "int_nfc_token"))
|
||||
methods |= WPS_CONFIG_INT_NFC_TOKEN;
|
||||
if (os_strstr(str, "nfc_interface"))
|
||||
methods |= WPS_CONFIG_NFC_INTERFACE;
|
||||
if (os_strstr(str, "push_button"))
|
||||
methods |= WPS_CONFIG_PUSHBUTTON;
|
||||
if (os_strstr(str, "keypad"))
|
||||
methods |= WPS_CONFIG_KEYPAD;
|
||||
#ifdef CONFIG_WPS2
|
||||
if (os_strstr(str, "virtual_display"))
|
||||
methods |= WPS_CONFIG_VIRT_DISPLAY;
|
||||
if (os_strstr(str, "physical_display"))
|
||||
methods |= WPS_CONFIG_PHY_DISPLAY;
|
||||
if (os_strstr(str, "virtual_push_button"))
|
||||
methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
|
||||
if (os_strstr(str, "physical_push_button"))
|
||||
methods |= WPS_CONFIG_PHY_PUSHBUTTON;
|
||||
#endif /* CONFIG_WPS2 */
|
||||
}
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
|
||||
struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
|
||||
{
|
||||
struct wpabuf *msg;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK");
|
||||
|
||||
msg = wpabuf_alloc(1000);
|
||||
if (msg == NULL)
|
||||
return NULL;
|
||||
|
||||
if (wps_build_version(msg) ||
|
||||
wps_build_msg_type(msg, WPS_WSC_ACK) ||
|
||||
wps_build_enrollee_nonce(wps, msg) ||
|
||||
wps_build_registrar_nonce(wps, msg) ||
|
||||
wps_build_wfa_ext(msg, 0, NULL, 0)) {
|
||||
wpabuf_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
|
||||
{
|
||||
struct wpabuf *msg;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK");
|
||||
|
||||
msg = wpabuf_alloc(1000);
|
||||
if (msg == NULL)
|
||||
return NULL;
|
||||
|
||||
if (wps_build_version(msg) ||
|
||||
wps_build_msg_type(msg, WPS_WSC_NACK) ||
|
||||
wps_build_enrollee_nonce(wps, msg) ||
|
||||
wps_build_registrar_nonce(wps, msg) ||
|
||||
wps_build_config_error(msg, wps->config_error) ||
|
||||
wps_build_wfa_ext(msg, 0, NULL, 0)) {
|
||||
wpabuf_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_WPS_NFC
|
||||
struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
|
||||
struct wpabuf **privkey,
|
||||
struct wpabuf **dev_pw)
|
||||
{
|
||||
struct wpabuf *priv = NULL, *pub = NULL, *pw, *ret;
|
||||
void *dh_ctx;
|
||||
u16 val;
|
||||
|
||||
pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN);
|
||||
if (pw == NULL)
|
||||
return NULL;
|
||||
|
||||
if (random_get_bytes(wpabuf_put(pw, WPS_OOB_DEVICE_PASSWORD_LEN),
|
||||
WPS_OOB_DEVICE_PASSWORD_LEN) ||
|
||||
random_get_bytes((u8 *) &val, sizeof(val))) {
|
||||
wpabuf_free(pw);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dh_ctx = dh5_init(&priv, &pub);
|
||||
if (dh_ctx == NULL) {
|
||||
wpabuf_free(pw);
|
||||
return NULL;
|
||||
}
|
||||
dh5_free(dh_ctx);
|
||||
|
||||
*id = 0x10 + val % 0xfff0;
|
||||
wpabuf_free(*pubkey);
|
||||
*pubkey = pub;
|
||||
wpabuf_free(*privkey);
|
||||
*privkey = priv;
|
||||
wpabuf_free(*dev_pw);
|
||||
*dev_pw = pw;
|
||||
|
||||
ret = wps_build_nfc_pw_token(*id, *pubkey, *dev_pw);
|
||||
if (ndef && ret) {
|
||||
struct wpabuf *tmp;
|
||||
tmp = ndef_build_wifi(ret);
|
||||
wpabuf_free(ret);
|
||||
if (tmp == NULL)
|
||||
return NULL;
|
||||
ret = tmp;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_WPS_NFC */
|
|
@ -0,0 +1,451 @@
|
|||
/*
|
||||
* Wi-Fi Protected Setup - device attributes
|
||||
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
#include "wpa/includes.h"
|
||||
#include "wpa/common.h"
|
||||
|
||||
#include "wps/wps_i.h"
|
||||
#include "wps/wps_dev_attr.h"
|
||||
|
||||
int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg)
|
||||
{
|
||||
size_t len;
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Manufacturer");
|
||||
wpabuf_put_be16(msg, ATTR_MANUFACTURER);
|
||||
len = dev->manufacturer ? os_strlen(dev->manufacturer) : 0;
|
||||
#ifndef CONFIG_WPS_STRICT
|
||||
if (len == 0) {
|
||||
/*
|
||||
* Some deployed WPS implementations fail to parse zero-length
|
||||
* attributes. As a workaround, send a space character if the
|
||||
* device attribute string is empty.
|
||||
*/
|
||||
wpabuf_put_be16(msg, 1);
|
||||
wpabuf_put_u8(msg, ' ');
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_WPS_STRICT */
|
||||
wpabuf_put_be16(msg, len);
|
||||
wpabuf_put_data(msg, dev->manufacturer, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg)
|
||||
{
|
||||
size_t len;
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Model Name");
|
||||
wpabuf_put_be16(msg, ATTR_MODEL_NAME);
|
||||
len = dev->model_name ? os_strlen(dev->model_name) : 0;
|
||||
#ifndef CONFIG_WPS_STRICT
|
||||
if (len == 0) {
|
||||
/*
|
||||
* Some deployed WPS implementations fail to parse zero-length
|
||||
* attributes. As a workaround, send a space character if the
|
||||
* device attribute string is empty.
|
||||
*/
|
||||
wpabuf_put_be16(msg, 1);
|
||||
wpabuf_put_u8(msg, ' ');
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_WPS_STRICT */
|
||||
wpabuf_put_be16(msg, len);
|
||||
wpabuf_put_data(msg, dev->model_name, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg)
|
||||
{
|
||||
size_t len;
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Model Number");
|
||||
wpabuf_put_be16(msg, ATTR_MODEL_NUMBER);
|
||||
len = dev->model_number ? os_strlen(dev->model_number) : 0;
|
||||
#ifndef CONFIG_WPS_STRICT
|
||||
if (len == 0) {
|
||||
/*
|
||||
* Some deployed WPS implementations fail to parse zero-length
|
||||
* attributes. As a workaround, send a space character if the
|
||||
* device attribute string is empty.
|
||||
*/
|
||||
wpabuf_put_be16(msg, 1);
|
||||
wpabuf_put_u8(msg, ' ');
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_WPS_STRICT */
|
||||
wpabuf_put_be16(msg, len);
|
||||
wpabuf_put_data(msg, dev->model_number, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_build_serial_number(struct wps_device_data *dev,
|
||||
struct wpabuf *msg)
|
||||
{
|
||||
size_t len;
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Serial Number");
|
||||
wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER);
|
||||
len = dev->serial_number ? os_strlen(dev->serial_number) : 0;
|
||||
#ifndef CONFIG_WPS_STRICT
|
||||
if (len == 0) {
|
||||
/*
|
||||
* Some deployed WPS implementations fail to parse zero-length
|
||||
* attributes. As a workaround, send a space character if the
|
||||
* device attribute string is empty.
|
||||
*/
|
||||
wpabuf_put_be16(msg, 1);
|
||||
wpabuf_put_u8(msg, ' ');
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_WPS_STRICT */
|
||||
wpabuf_put_be16(msg, len);
|
||||
wpabuf_put_data(msg, dev->serial_number, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Primary Device Type");
|
||||
wpabuf_put_be16(msg, ATTR_PRIMARY_DEV_TYPE);
|
||||
wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN);
|
||||
wpabuf_put_data(msg, dev->pri_dev_type, WPS_DEV_TYPE_LEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_secondary_dev_type(struct wps_device_data *dev,
|
||||
struct wpabuf *msg)
|
||||
{
|
||||
if (!dev->num_sec_dev_types)
|
||||
return 0;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Secondary Device Type");
|
||||
wpabuf_put_be16(msg, ATTR_SECONDARY_DEV_TYPE_LIST);
|
||||
wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
|
||||
wpabuf_put_data(msg, dev->sec_dev_type,
|
||||
WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
|
||||
unsigned int num_req_dev_types,
|
||||
const u8 *req_dev_types)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < num_req_dev_types; i++) {
|
||||
wpa_hexdump(MSG_DEBUG, "WPS: * Requested Device Type",
|
||||
req_dev_types + i * WPS_DEV_TYPE_LEN,
|
||||
WPS_DEV_TYPE_LEN);
|
||||
wpabuf_put_be16(msg, ATTR_REQUESTED_DEV_TYPE);
|
||||
wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN);
|
||||
wpabuf_put_data(msg, req_dev_types + i * WPS_DEV_TYPE_LEN,
|
||||
WPS_DEV_TYPE_LEN);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg)
|
||||
{
|
||||
size_t len;
|
||||
wpa_printf(MSG_DEBUG, "WPS: * Device Name");
|
||||
wpabuf_put_be16(msg, ATTR_DEV_NAME);
|
||||
len = dev->device_name ? os_strlen(dev->device_name) : 0;
|
||||
#ifndef CONFIG_WPS_STRICT
|
||||
if (len == 0) {
|
||||
/*
|
||||
* Some deployed WPS implementations fail to parse zero-length
|
||||
* attributes. As a workaround, send a space character if the
|
||||
* device attribute string is empty.
|
||||
*/
|
||||
wpabuf_put_be16(msg, 1);
|
||||
wpabuf_put_u8(msg, ' ');
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_WPS_STRICT */
|
||||
wpabuf_put_be16(msg, len);
|
||||
wpabuf_put_data(msg, dev->device_name, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg)
|
||||
{
|
||||
if (wps_build_manufacturer(dev, msg) ||
|
||||
wps_build_model_name(dev, msg) ||
|
||||
wps_build_model_number(dev, msg) ||
|
||||
wps_build_serial_number(dev, msg) ||
|
||||
wps_build_primary_dev_type(dev, msg) ||
|
||||
wps_build_dev_name(dev, msg))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "WPS: * OS Version");
|
||||
wpabuf_put_be16(msg, ATTR_OS_VERSION);
|
||||
wpabuf_put_be16(msg, 4);
|
||||
wpabuf_put_be32(msg, 0x80000000 | dev->os_version);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg)
|
||||
{
|
||||
if (dev->vendor_ext_m1 != NULL) {
|
||||
wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension M1",
|
||||
wpabuf_head_u8(dev->vendor_ext_m1),
|
||||
wpabuf_len(dev->vendor_ext_m1));
|
||||
wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
|
||||
wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext_m1));
|
||||
wpabuf_put_buf(msg, dev->vendor_ext_m1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", dev->rf_bands);
|
||||
wpabuf_put_be16(msg, ATTR_RF_BANDS);
|
||||
wpabuf_put_be16(msg, 1);
|
||||
wpabuf_put_u8(msg, dev->rf_bands);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
|
||||
if (dev->vendor_ext[i] == NULL)
|
||||
continue;
|
||||
wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension",
|
||||
wpabuf_head_u8(dev->vendor_ext[i]),
|
||||
wpabuf_len(dev->vendor_ext[i]));
|
||||
wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
|
||||
wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext[i]));
|
||||
wpabuf_put_buf(msg, dev->vendor_ext[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str,
|
||||
size_t str_len)
|
||||
{
|
||||
if (str == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: No Manufacturer received");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer", str, str_len);
|
||||
|
||||
os_free(dev->manufacturer);
|
||||
dev->manufacturer = (char *)os_malloc(str_len + 1);
|
||||
if (dev->manufacturer == NULL)
|
||||
return -1;
|
||||
os_memcpy(dev->manufacturer, str, str_len);
|
||||
dev->manufacturer[str_len] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_process_model_name(struct wps_device_data *dev, const u8 *str,
|
||||
size_t str_len)
|
||||
{
|
||||
if (str == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: No Model Name received");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name", str, str_len);
|
||||
|
||||
os_free(dev->model_name);
|
||||
dev->model_name = (char *)os_malloc(str_len + 1);
|
||||
if (dev->model_name == NULL)
|
||||
return -1;
|
||||
os_memcpy(dev->model_name, str, str_len);
|
||||
dev->model_name[str_len] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_process_model_number(struct wps_device_data *dev, const u8 *str,
|
||||
size_t str_len)
|
||||
{
|
||||
if (str == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: No Model Number received");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number", str, str_len);
|
||||
|
||||
os_free(dev->model_number);
|
||||
dev->model_number = (char *)os_malloc(str_len + 1);
|
||||
if (dev->model_number == NULL)
|
||||
return -1;
|
||||
os_memcpy(dev->model_number, str, str_len);
|
||||
dev->model_number[str_len] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_process_serial_number(struct wps_device_data *dev,
|
||||
const u8 *str, size_t str_len)
|
||||
{
|
||||
if (str == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: No Serial Number received");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number", str, str_len);
|
||||
|
||||
os_free(dev->serial_number);
|
||||
dev->serial_number = (char *)os_malloc(str_len + 1);
|
||||
if (dev->serial_number == NULL)
|
||||
return -1;
|
||||
os_memcpy(dev->serial_number, str, str_len);
|
||||
dev->serial_number[str_len] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_process_dev_name(struct wps_device_data *dev, const u8 *str,
|
||||
size_t str_len)
|
||||
{
|
||||
if (str == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: No Device Name received");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name", str, str_len);
|
||||
|
||||
os_free(dev->device_name);
|
||||
dev->device_name = (char *)os_malloc(str_len + 1);
|
||||
if (dev->device_name == NULL)
|
||||
return -1;
|
||||
os_memcpy(dev->device_name, str, str_len);
|
||||
dev->device_name[str_len] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int wps_process_primary_dev_type(struct wps_device_data *dev,
|
||||
const u8 *dev_type)
|
||||
{
|
||||
#if 0
|
||||
#ifndef CONFIG_NO_STDOUT_DEBUG
|
||||
char devtype[WPS_DEV_TYPE_BUFSIZE];
|
||||
#endif /* CONFIG_NO_STDOUT_DEBUG */
|
||||
#endif
|
||||
if (dev_type == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: No Primary Device Type received");
|
||||
return -1;
|
||||
}
|
||||
|
||||
os_memcpy(dev->pri_dev_type, dev_type, WPS_DEV_TYPE_LEN);
|
||||
//wpa_printf(MSG_DEBUG, "WPS: Primary Device Type: %s",
|
||||
// wps_dev_type_bin2str(dev->pri_dev_type, devtype,
|
||||
// sizeof(devtype)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_process_device_attrs(struct wps_device_data *dev,
|
||||
struct wps_parse_attr *attr)
|
||||
{
|
||||
if (wps_process_manufacturer(dev, attr->manufacturer,
|
||||
attr->manufacturer_len) ||
|
||||
wps_process_model_name(dev, attr->model_name,
|
||||
attr->model_name_len) ||
|
||||
wps_process_model_number(dev, attr->model_number,
|
||||
attr->model_number_len) ||
|
||||
wps_process_serial_number(dev, attr->serial_number,
|
||||
attr->serial_number_len) ||
|
||||
wps_process_primary_dev_type(dev, attr->primary_dev_type) ||
|
||||
wps_process_dev_name(dev, attr->dev_name, attr->dev_name_len))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_process_os_version(struct wps_device_data *dev, const u8 *ver)
|
||||
{
|
||||
if (ver == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: No OS Version received");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev->os_version = WPA_GET_BE32(ver);
|
||||
wpa_printf(MSG_DEBUG, "WPS: OS Version %08x", dev->os_version);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands)
|
||||
{
|
||||
if (bands == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "WPS: No RF Bands received");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev->rf_bands = *bands;
|
||||
wpa_printf(MSG_DEBUG, "WPS: Enrollee RF Bands 0x%x", dev->rf_bands);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void wps_device_data_dup(struct wps_device_data *dst,
|
||||
const struct wps_device_data *src)
|
||||
{
|
||||
if (src->device_name)
|
||||
dst->device_name = os_strdup(src->device_name);
|
||||
if (src->manufacturer)
|
||||
dst->manufacturer = os_strdup(src->manufacturer);
|
||||
if (src->model_name)
|
||||
dst->model_name = os_strdup(src->model_name);
|
||||
if (src->model_number)
|
||||
dst->model_number = os_strdup(src->model_number);
|
||||
if (src->serial_number)
|
||||
dst->serial_number = os_strdup(src->serial_number);
|
||||
os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN);
|
||||
dst->os_version = src->os_version;
|
||||
dst->rf_bands = src->rf_bands;
|
||||
}
|
||||
|
||||
|
||||
void wps_device_data_free(struct wps_device_data *dev)
|
||||
{
|
||||
os_free(dev->device_name);
|
||||
dev->device_name = NULL;
|
||||
os_free(dev->manufacturer);
|
||||
dev->manufacturer = NULL;
|
||||
os_free(dev->model_name);
|
||||
dev->model_name = NULL;
|
||||
os_free(dev->model_number);
|
||||
dev->model_number = NULL;
|
||||
os_free(dev->serial_number);
|
||||
dev->serial_number = NULL;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue