tests: IMSI privacy with imsi_identity

Add RSA public key (in an X.509v3 certificate) and private key for IMSI
privacy. These were generated with
openssl req -new -x509 -sha256 -newkey rsa:2048 -nodes -days 7500 \
	-keyout imsi-privacy-key.pem -out imsi-privacy-cert.pem

Test the case where wpa_supplicant side RSA-OAEP operation for IMSI
privacy is done in an external component while the hostapd (EAP server)
processing of the encrypted identity is internal.

Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
This commit is contained in:
Jouni Malinen 2022-04-30 16:28:33 +03:00 committed by Jouni Malinen
parent 21098e39fe
commit 9dd2ea5368
5 changed files with 182 additions and 4 deletions

View file

@ -20,6 +20,7 @@ pac_opaque_encr_key=000102030405060708090a0b0c0d0e0f
eap_fast_a_id=101112131415161718191a1b1c1d1e1f
eap_fast_a_id_info=test server
eap_sim_aka_result_ind=1
imsi_privacy_key=auth_serv/imsi-privacy-key.pem
tls_flags=[ENABLE-TLSv1.3]
dump_msk_file=LOGDIR/as-msk.lst

View file

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDUzCCAjugAwIBAgIUegWQhMTybRQSmWSWMIrT+RqTrawwDQYJKoZIhvcNAQEL
BQAwOTELMAkGA1UEBhMCRkkxETAPBgNVBAcMCEhlbHNpbmtpMRcwFQYDVQQDDA5o
b3N0YXAgdGVzdGluZzAeFw0yMjA0MzAxMzAxMDFaFw00MjExMTExMzAxMDFaMDkx
CzAJBgNVBAYTAkZJMREwDwYDVQQHDAhIZWxzaW5raTEXMBUGA1UEAwwOaG9zdGFw
IHRlc3RpbmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCHnFl+bVA
9WiPdu+falDLhM3+wA6n5gtbPX90yVhl6yX5UAhwzeFgV1uVRqMv3LMbapGGkJYn
wUv1qjuloD8Qe+l6zmxhGaI0unjaRrosiWZf5XdSKc8O3YoYkZlPhlUreG6/0elD
Rnka9yBncxK/u3Qt2w+hDRc8sYLYkvfhGCoArFgt30kPg1+xdOBX97C0lOUJqgR6
mKl/D3pYBtDMEqJl7ZXWcKt23QVv/O3luEPTlY8uSuz2SNGoBKKX8ekTTdF9Vdhb
v3iber9ImLB0ZOb154u40pmxMitSpEoHqJGBsWfCiG/ghRd43njJm0e3mmsTDfUT
6y45oNIbtJFrAgMBAAGjUzBRMB0GA1UdDgQWBBTEttO3uwTr6dfAp9DbW1SiSOVV
EjAfBgNVHSMEGDAWgBTEttO3uwTr6dfAp9DbW1SiSOVVEjAPBgNVHRMBAf8EBTAD
AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB0uouWgGQgM5O8QJNBKK2+qypiTJgqSVpK
TbuOD9MCIC8ZPF1kTvRmMDn0Sa5vgaNoyD6dckj+MPUpMQ6srhRpgMWB4x7dUFB8
CkGIHI+XsdZ1ihlPWH5vuKOFtJjCKZESqpCwzWHU/HwTUriSIPJXJTfvrHaBoi2m
xjI7sd2sXCe2xYN9gWBP6wz+X1/qiLZvM5fzdJKWlBpo6qLL+C1tAB48PHwpFRU2
vWi+SMg38buYFyOn+x9yiAtB0SIKXnrTPRbFTUT8LuZVmG4FBCBS8+6qbg7qhR8U
VWzcib4UAjOI9fu8Yh/2cG8wRvUEv7kHPUup8XtL6IsMQ74bOn5l
-----END CERTIFICATE-----

View file

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDCHnFl+bVA9WiP
du+falDLhM3+wA6n5gtbPX90yVhl6yX5UAhwzeFgV1uVRqMv3LMbapGGkJYnwUv1
qjuloD8Qe+l6zmxhGaI0unjaRrosiWZf5XdSKc8O3YoYkZlPhlUreG6/0elDRnka
9yBncxK/u3Qt2w+hDRc8sYLYkvfhGCoArFgt30kPg1+xdOBX97C0lOUJqgR6mKl/
D3pYBtDMEqJl7ZXWcKt23QVv/O3luEPTlY8uSuz2SNGoBKKX8ekTTdF9Vdhbv3ib
er9ImLB0ZOb154u40pmxMitSpEoHqJGBsWfCiG/ghRd43njJm0e3mmsTDfUT6y45
oNIbtJFrAgMBAAECggEAZiNEgWaBEzvNF2d6L4PuHRe0l60QSRGGuixCiv2CEKZI
pcSRnGEHi/yLCKFRLdbPOsa46XfcOfC/+fPnTH9jj1XThLVmWzT0nw/alOcQAG0P
O7fuL4ImG2k/xyuc4jYJTEUi6LUXKjnDcQfEugnXPKDyQUp5D8Fmj53K/g9ec8Wp
WahjALrsFYDexHYnitle41uERSrdQysFoOLgiRoVFmCcEO7leEqb4M4rVLfPd/j4
pJB9sWKRh7P70FoY24Ro2XOuUcebVa8XXr1e2hI69T2WY2wrXGn47w3hp8zIMkyv
EXHHg+/qPA1hh59NuSZfFIg9P7CCZbTAiba1JqgTqQKBgQD/X29hpKlW504ZPO24
OVq6qe+JesNIcpeaCHeJiVmkrIXpbxb+BwLgV/L+xzWrst/pG03e00Z/A/Yb66/8
DFGDxyMg1UNYL2t714vON8IwFJj37apqGraDUgW+QjFznL09mRN68JihzMPbHFxi
AirYLKb2iy/LFgcYFIEs/onThQKBgQDCmH6jRolsE9CavoXlBZwjlQSUePhPq8vt
wHVCoHcYTEJLTRFUCwcaqGmqOXwiX665RlJJ0wYC68GqFcKcztz+of23rRlM14Gd
Uwgp8d1UzpZqz8Sig+phqGecpY4qRyiy5anNvtqfiWPgFOfJfg2CiLEWM9w340Zm
aMa26fKMLwKBgQCP4PkkDl3KNK/v0EAvF0FjAfOPhbcYzldT1Ylj2BrFiN450vkl
TlX0iBjEKwC2KCW3dEa/UFHbpiO3P2b7nwUeNcg4627x9GWedKa0HP4vkKtOpHzr
IvnJqyDJPQoXlSuZ1PEAxyV3o6KFhMkX/xiciyvWpDzdMx/0FTliXFbS8QKBgQC7
weuemraJo2zJgj8qxQjshCIRJ89fAAIZ+nKpwK5osVvd0BSCJMnL/OdHKYQOnoe1
mJZZSNUqCFOqSqimKCqvPZnSmaAptl4HcFAWOJo388TKdoHh1KpXY+flCxBq1pH8
WwBF1nqXKDdHxKQIlClw43cKVyuKeS46LapeXsh6XQKBgQDnr0rZo0iDoFM5QLua
78KMi6G+dJ8DvIELyhCljy6Bvs9tmaILdixlOtIxoDAfyws/ls9BxFTCgWOIud4v
UI4WCCIrc50cYnuTeWn/iIhNCSooD+S7m9iVbMhmLvXhxp0vpFghmQ5Bqf7Xi0Tr
iOMcKRNNgdYou4FwQ17P8FnqkQ==
-----END PRIVATE KEY-----

View file

@ -149,12 +149,12 @@ def read_pem(fname, decode=True):
return base64.b64decode(cert)
return cert.encode()
def eap_connect(dev, hapd, method, identity,
def eap_connect(dev, hapd, method, identity, raw_identity=None,
sha256=False, expect_failure=False, local_error_report=False,
maybe_local_error=False, report_failure=False,
expect_cert_error=None, **kwargs):
id = dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
eap=method, identity=identity,
eap=method, identity=identity, raw_identity=raw_identity,
wait_connect=False, scan_freq="2412", ieee80211w="1",
**kwargs)
eap_check_auth(dev, method, True, sha256=sha256,
@ -303,6 +303,48 @@ def test_ap_wpa2_eap_sim(dev, apdev):
eap_connect(dev[0], hapd, "SIM", "1232010000000000",
expect_failure=True)
def test_ap_wpa2_eap_sim_imsi_identity(dev, apdev, params):
"""WPA2-Enterprise connection using EAP-SIM and imsi_identity"""
check_hlr_auc_gw_support()
prefix = params['prefix']
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
tls = hapd.request("GET tls_library")
if not tls.startswith("OpenSSL"):
raise HwsimSkip("IMSI privacy not supported with this TLS library: " + tls)
imsi = "232010000000000"
realm = "wlan.mnc232.mcc02.3gppnetwork.org"
method_id = '1'
permanent_id = method_id + imsi + '@' + realm
# RSA-OAEP(permanent_id)
perm_id = prefix + '.permanent-id'
enc_id = prefix + '.enc-permanent-id'
with open(perm_id, 'w') as f:
f.write(permanent_id)
pubkey = prefix + ".cert-pub.pem"
subprocess.check_call(["openssl", "x509",
"-in", "auth_serv/imsi-privacy-cert.pem",
"-pubkey", "-noout",
"-out", pubkey])
subprocess.check_call(["openssl", "pkeyutl",
"-inkey", pubkey, "-pubin", "-in", perm_id,
"-pkeyopt", "rsa_padding_mode:oaep",
"-pkeyopt", "rsa_oaep_md:sha256",
"-encrypt",
"-out", enc_id])
with open(enc_id, 'rb') as f:
data = f.read()
encrypted_id = base64.b64encode(data).decode()
if len(encrypted_id) != 344:
raise Exception("Unexpected length of the base64 encoded identity: " + b64)
eap_connect(dev[0], hapd, "SIM", identity=None,
raw_identity='P"\\0' + encrypted_id + '"',
anonymous_identity=method_id + "anonymous@" + realm,
imsi_identity=permanent_id,
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
eap_reauth(dev[0], "SIM")
def test_ap_wpa2_eap_sim_sql(dev, apdev, params):
"""WPA2-Enterprise connection using EAP-SIM (SQL)"""
check_hlr_auc_gw_support()
@ -1028,6 +1070,48 @@ def test_ap_wpa2_eap_aka(dev, apdev):
eap_connect(dev[0], hapd, "AKA", "0232010000000000",
expect_failure=True)
def test_ap_wpa2_eap_aka_imsi_identity(dev, apdev, params):
"""WPA2-Enterprise connection using EAP-AKA and imsi_identity"""
check_hlr_auc_gw_support()
prefix = params['prefix']
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
tls = hapd.request("GET tls_library")
if not tls.startswith("OpenSSL"):
raise HwsimSkip("IMSI privacy not supported with this TLS library: " + tls)
imsi = "232010000000000"
realm = "wlan.mnc232.mcc02.3gppnetwork.org"
method_id = '0'
permanent_id = method_id + imsi + '@' + realm
# RSA-OAEP(permanent_id)
perm_id = prefix + '.permanent-id'
enc_id = prefix + '.enc-permanent-id'
with open(perm_id, 'w') as f:
f.write(permanent_id)
pubkey = prefix + ".cert-pub.pem"
subprocess.check_call(["openssl", "x509",
"-in", "auth_serv/imsi-privacy-cert.pem",
"-pubkey", "-noout",
"-out", pubkey])
subprocess.check_call(["openssl", "pkeyutl",
"-inkey", pubkey, "-pubin", "-in", perm_id,
"-pkeyopt", "rsa_padding_mode:oaep",
"-pkeyopt", "rsa_oaep_md:sha256",
"-encrypt",
"-out", enc_id])
with open(enc_id, 'rb') as f:
data = f.read()
encrypted_id = base64.b64encode(data).decode()
if len(encrypted_id) != 344:
raise Exception("Unexpected length of the base64 encoded identity: " + b64)
eap_connect(dev[0], hapd, "AKA", identity=None,
raw_identity='P"\\0' + encrypted_id + '"',
anonymous_identity=method_id + "anonymous@" + realm,
imsi_identity=permanent_id,
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
eap_reauth(dev[0], "AKA")
def test_ap_wpa2_eap_aka_sql(dev, apdev, params):
"""WPA2-Enterprise connection using EAP-AKA (SQL)"""
check_hlr_auc_gw_support()
@ -1241,6 +1325,48 @@ def test_ap_wpa2_eap_aka_prime(dev, apdev):
password="ff22250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
expect_failure=True)
def test_ap_wpa2_eap_aka_prime_imsi_identity(dev, apdev, params):
"""WPA2-Enterprise connection using EAP-AKA' and imsi_identity"""
check_hlr_auc_gw_support()
prefix = params['prefix']
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
tls = hapd.request("GET tls_library")
if not tls.startswith("OpenSSL"):
raise HwsimSkip("IMSI privacy not supported with this TLS library: " + tls)
imsi = "555444333222111"
realm = "wlan.mnc555.mcc44.3gppnetwork.org"
method_id = '6'
permanent_id = method_id + imsi + '@' + realm
# RSA-OAEP(permanent_id)
perm_id = prefix + '.permanent-id'
enc_id = prefix + '.enc-permanent-id'
with open(perm_id, 'w') as f:
f.write(permanent_id)
pubkey = prefix + ".cert-pub.pem"
subprocess.check_call(["openssl", "x509",
"-in", "auth_serv/imsi-privacy-cert.pem",
"-pubkey", "-noout",
"-out", pubkey])
subprocess.check_call(["openssl", "pkeyutl",
"-inkey", pubkey, "-pubin", "-in", perm_id,
"-pkeyopt", "rsa_padding_mode:oaep",
"-pkeyopt", "rsa_oaep_md:sha256",
"-encrypt",
"-out", enc_id])
with open(enc_id, 'rb') as f:
data = f.read()
encrypted_id = base64.b64encode(data).decode()
if len(encrypted_id) != 344:
raise Exception("Unexpected length of the base64 encoded identity: " + b64)
eap_connect(dev[0], hapd, "AKA'", identity=None,
raw_identity='P"\\0' + encrypted_id + '"',
anonymous_identity=method_id + "anonymous@" + realm,
imsi_identity=permanent_id,
password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
eap_reauth(dev[0], "AKA'")
def test_ap_wpa2_eap_aka_prime_sql(dev, apdev, params):
"""WPA2-Enterprise connection using EAP-AKA' (SQL)"""
check_hlr_auc_gw_support()

View file

@ -1082,7 +1082,8 @@ class WpaSupplicant:
"domain_match", "dpp_connector", "sae_password",
"sae_password_id", "check_cert_subject",
"machine_ca_cert", "machine_client_cert",
"machine_private_key", "machine_phase2"]
"machine_private_key", "machine_phase2",
"imsi_identity"]
for field in quoted:
if field in kwargs and kwargs[field]:
self.set_network_quoted(id, field, kwargs[field])
@ -1113,7 +1114,7 @@ class WpaSupplicant:
self.set_network(id, field, kwargs[field])
known_args = {"raw_psk", "password_hex", "peerkey", "okc", "ocsp",
"only_add_network", "wait_connect"}
"only_add_network", "wait_connect", "raw_identity"}
unknown = set(kwargs.keys())
unknown -= set(quoted)
unknown -= set(not_quoted)
@ -1121,6 +1122,8 @@ class WpaSupplicant:
if unknown:
raise Exception("Unknown WpaSupplicant::connect() arguments: " + str(unknown))
if "raw_identity" in kwargs and kwargs['raw_identity']:
self.set_network(id, "identity", kwargs['raw_identity'])
if "raw_psk" in kwargs and kwargs['raw_psk']:
self.set_network(id, "psk", kwargs['raw_psk'])
if "password_hex" in kwargs and kwargs['password_hex']: