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:
Jiang Jiang Jian 2018-05-09 15:19:37 +08:00
commit b9aee83d9b
64 changed files with 30799 additions and 105 deletions

View file

@ -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

View file

@ -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

View file

@ -15,6 +15,8 @@
#ifndef LIST_H
#define LIST_H
#include <stddef.h>
/**
* struct dl_list - Doubly-linked list
*/

View file

@ -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;
@ -96,20 +86,16 @@ struct wpa_sm {
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 */

View file

@ -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)
{

View file

@ -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 */

View file

@ -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

View file

@ -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 */

View file

@ -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 */

View file

@ -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 */

View file

@ -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 */

View file

@ -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 */

View file

@ -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 */

View file

@ -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;

View file

@ -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 */

View file

@ -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 */

View file

@ -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

View file

@ -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 */

View file

@ -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 */

View file

@ -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 */

View file

@ -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 */

View file

@ -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

View file

@ -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 */

View file

@ -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;
}

View file

@ -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)];
}

View file

@ -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

View file

@ -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 */

View file

@ -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

View file

@ -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 */

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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, &params) < 0) {
wpa_printf(MSG_DEBUG, "PKCS #5: Unsupported parameters");
return NULL;
}
ctx = pkcs5_crypto_init(&params, 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;
}

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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 */

View file

@ -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)];
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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 */

View file

@ -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