EAP-SIM/AKA peer: IMSI privacy
Add support for IMSI privacy in the EAP-SIM/AKA peer implementation. If the new wpa_supplicant network configuration parameter imsi_privacy_key is used to specify an RSA public key in a form of a PEM encoded X.509v3 certificate, that key will be used to encrypt the permanent identity (IMSI) in the transmitted EAP messages. Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
This commit is contained in:
parent
9dd2ea5368
commit
42871a5d25
8 changed files with 237 additions and 0 deletions
|
@ -1673,6 +1673,7 @@ struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted)
|
|||
struct wpabuf *resp;
|
||||
const u8 *identity;
|
||||
size_t identity_len;
|
||||
struct wpabuf *privacy_identity = NULL;
|
||||
|
||||
if (config == NULL) {
|
||||
wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration "
|
||||
|
@ -1695,6 +1696,30 @@ struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted)
|
|||
identity_len = config->machine_identity_len;
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "EAP: using machine identity",
|
||||
identity, identity_len);
|
||||
} else if (config->imsi_privacy_key && config->identity &&
|
||||
config->identity_len > 0) {
|
||||
const u8 *pos = config->identity;
|
||||
const u8 *end = config->identity + config->identity_len;
|
||||
|
||||
privacy_identity = wpabuf_alloc(9 + config->identity_len);
|
||||
if (!privacy_identity)
|
||||
return NULL;
|
||||
|
||||
/* Include method prefix */
|
||||
if (*pos == '0' || *pos == '1' || *pos == '6')
|
||||
wpabuf_put_u8(privacy_identity, *pos);
|
||||
wpabuf_put_str(privacy_identity, "anonymous");
|
||||
|
||||
/* Include realm */
|
||||
while (pos < end && *pos != '@')
|
||||
pos++;
|
||||
wpabuf_put_data(privacy_identity, pos, end - pos);
|
||||
|
||||
identity = wpabuf_head(privacy_identity);
|
||||
identity_len = wpabuf_len(privacy_identity);
|
||||
wpa_hexdump_ascii(MSG_DEBUG,
|
||||
"EAP: using IMSI privacy anonymous identity",
|
||||
identity, identity_len);
|
||||
} else {
|
||||
identity = config->identity;
|
||||
identity_len = config->identity_len;
|
||||
|
@ -1731,6 +1756,7 @@ struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted)
|
|||
return NULL;
|
||||
|
||||
wpabuf_put_data(resp, identity, identity_len);
|
||||
wpabuf_free(privacy_identity);
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "includes.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "utils/base64.h"
|
||||
#include "pcsc_funcs.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "crypto/sha1.h"
|
||||
|
@ -58,6 +59,7 @@ struct eap_aka_data {
|
|||
u16 last_kdf_attrs[EAP_AKA_PRIME_KDF_MAX];
|
||||
size_t last_kdf_count;
|
||||
int error_code;
|
||||
struct crypto_rsa_key *imsi_privacy_key;
|
||||
};
|
||||
|
||||
|
||||
|
@ -101,6 +103,25 @@ static void * eap_aka_init(struct eap_sm *sm)
|
|||
|
||||
data->eap_method = EAP_TYPE_AKA;
|
||||
|
||||
if (config && config->imsi_privacy_key) {
|
||||
#ifdef CRYPTO_RSA_OAEP_SHA256
|
||||
data->imsi_privacy_key = crypto_rsa_key_read(
|
||||
config->imsi_privacy_key, false);
|
||||
if (!data->imsi_privacy_key) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"EAP-AKA: Failed to read/parse IMSI privacy key %s",
|
||||
config->imsi_privacy_key);
|
||||
os_free(data);
|
||||
return NULL;
|
||||
}
|
||||
#else /* CRYPTO_RSA_OAEP_SHA256 */
|
||||
wpa_printf(MSG_ERROR,
|
||||
"EAP-AKA: No support for imsi_privacy_key in the build");
|
||||
os_free(data);
|
||||
return NULL;
|
||||
#endif /* CRYPTO_RSA_OAEP_SHA256 */
|
||||
}
|
||||
|
||||
/* Zero is a valid error code, so we need to initialize */
|
||||
data->error_code = NO_EAP_METHOD_ERROR;
|
||||
|
||||
|
@ -160,6 +181,9 @@ static void eap_aka_deinit(struct eap_sm *sm, void *priv)
|
|||
wpabuf_free(data->id_msgs);
|
||||
os_free(data->network_name);
|
||||
eap_aka_clear_keys(data, 0);
|
||||
#ifdef CRYPTO_RSA_OAEP_SHA256
|
||||
crypto_rsa_key_free(data->imsi_privacy_key);
|
||||
#endif /* CRYPTO_RSA_OAEP_SHA256 */
|
||||
os_free(data);
|
||||
}
|
||||
}
|
||||
|
@ -617,6 +641,47 @@ static struct wpabuf * eap_aka_synchronization_failure(
|
|||
}
|
||||
|
||||
|
||||
#ifdef CRYPTO_RSA_OAEP_SHA256
|
||||
static struct wpabuf *
|
||||
eap_aka_encrypt_identity(struct crypto_rsa_key *imsi_privacy_key,
|
||||
const u8 *identity, size_t identity_len)
|
||||
{
|
||||
struct wpabuf *imsi_buf, *enc;
|
||||
char *b64;
|
||||
size_t b64_len;
|
||||
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Encrypt permanent identity",
|
||||
identity, identity_len);
|
||||
|
||||
imsi_buf = wpabuf_alloc_copy(identity, identity_len);
|
||||
if (!imsi_buf)
|
||||
return NULL;
|
||||
enc = crypto_rsa_oaep_sha256_encrypt(imsi_privacy_key, imsi_buf);
|
||||
wpabuf_free(imsi_buf);
|
||||
if (!enc)
|
||||
return NULL;
|
||||
|
||||
b64 = base64_encode_no_lf(wpabuf_head(enc), wpabuf_len(enc), &b64_len);
|
||||
wpabuf_free(enc);
|
||||
if (!b64)
|
||||
return NULL;
|
||||
|
||||
enc = wpabuf_alloc(1 + b64_len);
|
||||
if (!enc) {
|
||||
os_free(b64);
|
||||
return NULL;
|
||||
}
|
||||
wpabuf_put_u8(enc, '\0');
|
||||
wpabuf_put_data(enc, b64, b64_len);
|
||||
os_free(b64);
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Encrypted permanent identity",
|
||||
wpabuf_head(enc), wpabuf_len(enc));
|
||||
|
||||
return enc;
|
||||
}
|
||||
#endif /* CRYPTO_RSA_OAEP_SHA256 */
|
||||
|
||||
|
||||
static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm,
|
||||
struct eap_aka_data *data,
|
||||
u8 id,
|
||||
|
@ -625,6 +690,7 @@ static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm,
|
|||
const u8 *identity = NULL;
|
||||
size_t identity_len = 0;
|
||||
struct eap_sim_msg *msg;
|
||||
struct wpabuf *enc_identity = NULL;
|
||||
|
||||
data->reauth = 0;
|
||||
if (id_req == ANY_ID && data->reauth_id) {
|
||||
|
@ -649,6 +715,22 @@ static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm,
|
|||
ids &= ~CLEAR_PSEUDONYM;
|
||||
eap_aka_clear_identities(sm, data, ids);
|
||||
}
|
||||
#ifdef CRYPTO_RSA_OAEP_SHA256
|
||||
if (identity && data->imsi_privacy_key) {
|
||||
enc_identity = eap_aka_encrypt_identity(
|
||||
data->imsi_privacy_key,
|
||||
identity, identity_len);
|
||||
if (!enc_identity) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"EAP-AKA: Failed to encrypt permanent identity");
|
||||
return eap_aka_client_error(
|
||||
data, id,
|
||||
EAP_AKA_UNABLE_TO_PROCESS_PACKET);
|
||||
}
|
||||
identity = wpabuf_head(enc_identity);
|
||||
identity_len = wpabuf_len(enc_identity);
|
||||
}
|
||||
#endif /* CRYPTO_RSA_OAEP_SHA256 */
|
||||
}
|
||||
if (id_req != NO_ID_REQ)
|
||||
eap_aka_clear_identities(sm, data, CLEAR_EAP_ID);
|
||||
|
@ -663,6 +745,7 @@ static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm,
|
|||
eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
|
||||
identity, identity_len);
|
||||
}
|
||||
wpabuf_free(enc_identity);
|
||||
|
||||
return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
|
||||
}
|
||||
|
|
|
@ -317,6 +317,16 @@ struct eap_peer_config {
|
|||
u8 *imsi_identity;
|
||||
size_t imsi_identity_len;
|
||||
|
||||
/**
|
||||
* imsi_privacy_key - IMSI privacy key (PEM encoded X.509v3 certificate)
|
||||
*
|
||||
* This field is used with EAP-SIM/AKA/AKA' to encrypt the permanent
|
||||
* identity (IMSI) to improve privacy. The X.509v3 certificate needs to
|
||||
* include a 2048-bit RSA public key and this is from the operator who
|
||||
* authenticates the SIM/USIM.
|
||||
*/
|
||||
char *imsi_privacy_key;
|
||||
|
||||
/**
|
||||
* machine_identity - EAP Identity for machine credential
|
||||
*
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
#include "includes.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "utils/base64.h"
|
||||
#include "pcsc_funcs.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "crypto/milenage.h"
|
||||
#include "crypto/random.h"
|
||||
#include "eap_peer/eap_i.h"
|
||||
|
@ -49,6 +51,7 @@ struct eap_sim_data {
|
|||
int result_ind, use_result_ind;
|
||||
int use_pseudonym;
|
||||
int error_code;
|
||||
struct crypto_rsa_key *imsi_privacy_key;
|
||||
};
|
||||
|
||||
|
||||
|
@ -98,6 +101,25 @@ static void * eap_sim_init(struct eap_sm *sm)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (config && config->imsi_privacy_key) {
|
||||
#ifdef CRYPTO_RSA_OAEP_SHA256
|
||||
data->imsi_privacy_key = crypto_rsa_key_read(
|
||||
config->imsi_privacy_key, false);
|
||||
if (!data->imsi_privacy_key) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"EAP-SIM: Failed to read/parse IMSI privacy key %s",
|
||||
config->imsi_privacy_key);
|
||||
os_free(data);
|
||||
return NULL;
|
||||
}
|
||||
#else /* CRYPTO_RSA_OAEP_SHA256 */
|
||||
wpa_printf(MSG_ERROR,
|
||||
"EAP-SIM: No support for imsi_privacy_key in the build");
|
||||
os_free(data);
|
||||
return NULL;
|
||||
#endif /* CRYPTO_RSA_OAEP_SHA256 */
|
||||
}
|
||||
|
||||
/* Zero is a valid error code, so we need to initialize */
|
||||
data->error_code = NO_EAP_METHOD_ERROR;
|
||||
|
||||
|
@ -162,6 +184,9 @@ static void eap_sim_deinit(struct eap_sm *sm, void *priv)
|
|||
os_free(data->reauth_id);
|
||||
os_free(data->last_eap_identity);
|
||||
eap_sim_clear_keys(data, 0);
|
||||
#ifdef CRYPTO_RSA_OAEP_SHA256
|
||||
crypto_rsa_key_free(data->imsi_privacy_key);
|
||||
#endif /* CRYPTO_RSA_OAEP_SHA256 */
|
||||
os_free(data);
|
||||
}
|
||||
}
|
||||
|
@ -481,6 +506,47 @@ static struct wpabuf * eap_sim_client_error(struct eap_sim_data *data, u8 id,
|
|||
}
|
||||
|
||||
|
||||
#ifdef CRYPTO_RSA_OAEP_SHA256
|
||||
static struct wpabuf *
|
||||
eap_sim_encrypt_identity(struct crypto_rsa_key *imsi_privacy_key,
|
||||
const u8 *identity, size_t identity_len)
|
||||
{
|
||||
struct wpabuf *imsi_buf, *enc;
|
||||
char *b64;
|
||||
size_t b64_len;
|
||||
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Encrypt permanent identity",
|
||||
identity, identity_len);
|
||||
|
||||
imsi_buf = wpabuf_alloc_copy(identity, identity_len);
|
||||
if (!imsi_buf)
|
||||
return NULL;
|
||||
enc = crypto_rsa_oaep_sha256_encrypt(imsi_privacy_key, imsi_buf);
|
||||
wpabuf_free(imsi_buf);
|
||||
if (!enc)
|
||||
return NULL;
|
||||
|
||||
b64 = base64_encode_no_lf(wpabuf_head(enc), wpabuf_len(enc), &b64_len);
|
||||
wpabuf_free(enc);
|
||||
if (!b64)
|
||||
return NULL;
|
||||
|
||||
enc = wpabuf_alloc(1 + b64_len);
|
||||
if (!enc) {
|
||||
os_free(b64);
|
||||
return NULL;
|
||||
}
|
||||
wpabuf_put_u8(enc, '\0');
|
||||
wpabuf_put_data(enc, b64, b64_len);
|
||||
os_free(b64);
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Encrypted permanent identity",
|
||||
wpabuf_head(enc), wpabuf_len(enc));
|
||||
|
||||
return enc;
|
||||
}
|
||||
#endif /* CRYPTO_RSA_OAEP_SHA256 */
|
||||
|
||||
|
||||
static struct wpabuf * eap_sim_response_start(struct eap_sm *sm,
|
||||
struct eap_sim_data *data, u8 id,
|
||||
enum eap_sim_id_req id_req)
|
||||
|
@ -489,6 +555,7 @@ static struct wpabuf * eap_sim_response_start(struct eap_sm *sm,
|
|||
size_t identity_len = 0;
|
||||
struct eap_sim_msg *msg;
|
||||
struct wpabuf *resp;
|
||||
struct wpabuf *enc_identity = NULL;
|
||||
|
||||
data->reauth = 0;
|
||||
if (id_req == ANY_ID && data->reauth_id) {
|
||||
|
@ -513,6 +580,22 @@ static struct wpabuf * eap_sim_response_start(struct eap_sm *sm,
|
|||
ids &= ~CLEAR_PSEUDONYM;
|
||||
eap_sim_clear_identities(sm, data, ids);
|
||||
}
|
||||
#ifdef CRYPTO_RSA_OAEP_SHA256
|
||||
if (identity && data->imsi_privacy_key) {
|
||||
enc_identity = eap_sim_encrypt_identity(
|
||||
data->imsi_privacy_key,
|
||||
identity, identity_len);
|
||||
if (!enc_identity) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"EAP-SIM: Failed to encrypt permanent identity");
|
||||
return eap_sim_client_error(
|
||||
data, id,
|
||||
EAP_SIM_UNABLE_TO_PROCESS_PACKET);
|
||||
}
|
||||
identity = wpabuf_head(enc_identity);
|
||||
identity_len = wpabuf_len(enc_identity);
|
||||
}
|
||||
#endif /* CRYPTO_RSA_OAEP_SHA256 */
|
||||
}
|
||||
if (id_req != NO_ID_REQ)
|
||||
eap_sim_clear_identities(sm, data, CLEAR_EAP_ID);
|
||||
|
@ -526,6 +609,7 @@ static struct wpabuf * eap_sim_response_start(struct eap_sm *sm,
|
|||
eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
|
||||
identity, identity_len);
|
||||
}
|
||||
wpabuf_free(enc_identity);
|
||||
if (!data->reauth) {
|
||||
wpa_hexdump(MSG_DEBUG, " AT_NONCE_MT",
|
||||
data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
|
||||
|
|
|
@ -168,6 +168,12 @@ Credentials can be pre-configured for automatic network selection:
|
|||
# milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN>
|
||||
# format
|
||||
#
|
||||
# imsi_privacy_key: IMSI privacy key (PEM encoded X.509v3 certificate)
|
||||
# This field is used with EAP-SIM/AKA/AKA' to encrypt the permanent
|
||||
# identity (IMSI) to improve privacy. The X.509v3 certificate needs to
|
||||
# include a 2048-bit RSA public key and this is from the operator who
|
||||
# authenticates the SIM/USIM.
|
||||
#
|
||||
# domain_suffix_match: Constraint for server domain name
|
||||
# If set, this FQDN is used as a suffix match requirement for the AAA
|
||||
# server certificate in SubjectAltName dNSName element(s). If a
|
||||
|
|
|
@ -2503,6 +2503,7 @@ static const struct parse_data ssid_fields[] = {
|
|||
{ INTe(machine_ocsp, machine_cert.ocsp) },
|
||||
{ INT(eapol_flags) },
|
||||
{ INTe(sim_num, sim_num) },
|
||||
{ STRe(imsi_privacy_key, imsi_privacy_key) },
|
||||
{ STRe(openssl_ciphers, openssl_ciphers) },
|
||||
{ INTe(erp, erp) },
|
||||
#endif /* IEEE8021X_EAPOL */
|
||||
|
@ -2770,6 +2771,7 @@ static void eap_peer_config_free(struct eap_peer_config *eap)
|
|||
bin_clear_free(eap->identity, eap->identity_len);
|
||||
os_free(eap->anonymous_identity);
|
||||
os_free(eap->imsi_identity);
|
||||
os_free(eap->imsi_privacy_key);
|
||||
os_free(eap->machine_identity);
|
||||
bin_clear_free(eap->password, eap->password_len);
|
||||
bin_clear_free(eap->machine_password, eap->machine_password_len);
|
||||
|
@ -2873,6 +2875,7 @@ void wpa_config_free_cred(struct wpa_cred *cred)
|
|||
os_free(cred->req_conn_capab_port[i]);
|
||||
os_free(cred->req_conn_capab_port);
|
||||
os_free(cred->req_conn_capab_proto);
|
||||
os_free(cred->imsi_privacy_key);
|
||||
os_free(cred);
|
||||
}
|
||||
|
||||
|
@ -3905,6 +3908,12 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (os_strcmp(var, "imsi_privacy_key") == 0) {
|
||||
os_free(cred->imsi_privacy_key);
|
||||
cred->imsi_privacy_key = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (line) {
|
||||
wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.",
|
||||
line, var);
|
||||
|
@ -4055,6 +4064,9 @@ char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var)
|
|||
if (os_strcmp(var, "imsi") == 0)
|
||||
return alloc_strdup(cred->imsi);
|
||||
|
||||
if (os_strcmp(var, "imsi_privacy_key") == 0)
|
||||
return alloc_strdup(cred->imsi_privacy_key);
|
||||
|
||||
if (os_strcmp(var, "milenage") == 0) {
|
||||
if (!(cred->milenage))
|
||||
return NULL;
|
||||
|
|
|
@ -180,6 +180,16 @@ struct wpa_cred {
|
|||
*/
|
||||
char *milenage;
|
||||
|
||||
/**
|
||||
* imsi_privacy_key - IMSI privacy key (PEM encoded X.509v3 certificate)
|
||||
*
|
||||
* This field is used with EAP-SIM/AKA/AKA' to encrypt the permanent
|
||||
* identity (IMSI) to improve privacy. The X.509v3 certificate needs to
|
||||
* include a 2048-bit RSA public key and this is from the operator who
|
||||
* authenticates the SIM/USIM.
|
||||
*/
|
||||
char *imsi_privacy_key;
|
||||
|
||||
/**
|
||||
* engine - Use an engine for private key operations
|
||||
*/
|
||||
|
|
|
@ -1065,6 +1065,12 @@ static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (cred->imsi_privacy_key && cred->imsi_privacy_key[0]) {
|
||||
if (wpa_config_set_quoted(ssid, "imsi_privacy_key",
|
||||
cred->imsi_privacy_key) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
wpa_s->next_ssid = ssid;
|
||||
wpa_config_update_prio_list(wpa_s->conf);
|
||||
if (!only_add)
|
||||
|
|
Loading…
Reference in a new issue