Introducción
El desarrollo de las redes móviles ha estado intrínsecamente ligado a la evolución de la seguridad de los usuarios. En las arquitecturas de generaciones previas (2G, 3G y 4G), la gestión de la identidad del abonado se basaba en la transmisión del IMSI (International Mobile Subscriber Identity) en texto plano durante ciertas fases del protocolo de registro. Este diseño, si bien funcional para la interconexión de la red, creó una vulnerabilidad criptográfica de naturaleza física. La exposición del IMSI permite la ejecución de ataques de captura pasiva, comúnmente denominados IMSI catchers. Estos dispositivos operan como estaciones base falsas, cuyo objetivo no es descifrar tráfico cifrado, sino simplemente interceptar la identidad en claro, lo que habilita el rastreo y la correlación de datos a lo largo del tiempo y el espacio.
La plataforma 5G fue concebida como una respuesta arquitectónica a este problema. El diseño incorpora el SUCI (Subscription Concealed Identifier) para mitigar el riesgo de identificación pasiva. Sin embargo, la transición de una especificación protocolaria a una funcionalidad operacional efectiva es un proceso de gran complejidad, donde la seguridad no es un atributo único, sino una función emergente de la interacción de múltiples módulos: la tarjeta USIM, el terminal (UE/ME), el núcleo de red (AMF, UDM) y la interfaz radio.
Este análisis se centra en describir los axiomas técnicos y las metodologías de verificación que determinan si la privacidad del SUPI (Subscription Permanent Identifier) es lograda en un entorno operativo. Se abordarán los mecanismos de la tarjeta, la lógica de la decisión de ocultación, y los métodos de auditoría que contrastan la capacidad declarada con el comportamiento real del mensaje de registro.
Fundamentos de la seguridad de protocolo
El SUPI es el identificador único y permanente del abonado, compuesto por el código de red y el MSIN, es decir, es equivalente al IMSI de las redes 2G/3G/4G. Su función es el anclaje de la identidad en el sistema.
El SUCI es la implementación de la función de ocultación. La clave de este proceso reside en el uso de la criptografía de clave pública, específicamente mediante el esquema ECIES (Elliptic Curve Integrated Encryption Scheme). La arquitectura establece que el MSIN debe ser cifrado con la clave pública del operador. Esta configuración asegura que, aunque el paquete de identidad viaje por el aire, la decodificación sea inviable para cualquier entidad externa, limitando la capacidad de identificación solo al núcleo de red legítimo.
La especificación 3GPP define distintos esquemas de protección para esta transformación:
- Null Scheme (ID 0): Representa la ausencia de cifrado. El MSIN se transmite sin cifrar, manteniendo la exposición.
- Profile A (ID 1) y Profile B (ID 2): Estos esquemas implementan la ocultación mediante ECIES. El éxito de la privacidad se fundamenta en que el MSIN es cifrado antes de salir del terminal, cumpliendo con el objetivo de mitigación de IMSI catchers.
Es imperativo establecer que el campo de la identidad puede llevar el formato SUCI (indicado por el Type of identity, pero si el campo Protection scheme Id indica null scheme (0), la identidad se considera completamente expuesta a la observación pasiva. Esto implica que la mera adición del contenedor SUCI no confiere automáticamente privacidad; la protección se materializa en la ejecución de un esquema criptográfico no nulo.
Gestión del proceso
La gestión del proceso de ocultación es una función de software que reside en la tarjeta USIM y se rige por la tabla de servicios (EF.UST).
Funcionalidad de la tabla de servicios
La EF.UST es un registro de funcionalidad que actúa como un conjunto de interruptores. Para el soporte de SUCI, los servicios clave son:
- Servicio 124 (Subscription identifier privacy support): Este servicio es el axioma decisorio primario. Su activación es el requisito mínimo para declarar el soporte de privacidad. Si este bit está desactivado, el terminal opera bajo la presunción de que no se requiere la ocultación, lo que conduce al
null schemecomo resultado operativo. - Servicio 125 (SUCI calculation by the USIM): Este servicio define la responsabilidad del cálculo. Si está activo, la USIM se encarga de ejecutar el proceso criptográfico de ocultación. Si está inactivo, pero el servicio 124 está activo, la responsabilidad de calcular el SUCI recae en el Módem/Terminal (ME).
Dependecia de la aplicación
El soporte de SUCI requiere más que solo el interruptor 124. La capacidad criptográfica debe estar aprovisionada. El fichero EF.SUCI_Calc_Info contiene la matriz de información necesaria para este proceso: la lista de esquemas de protección disponibles (con su ID y prioridad), y las identificaciones de las claves públicas de la red doméstica.
La lógica protocolaria establece que la privacidad es efectiva solo si, y solo si, el servicio 124 está activo, si EF.SUCI_Calc_Info contiene esquemas no nulos, y si la clave pública de la red correspondiente está disponible para su uso. Si estos elementos están presentes, el terminal está instruido a cifrar; si solo están presentes los ficheros pero el interruptor 124 está inactivo, el mandato de ocultación es nulo. El interruptor 125 requiere que la tarjeta USIM disponga de la funcionalidad programada para generar los identificadores mediante software.
Auditoría de las implementaciones
Para validar la implementación del SUCI, se emplea una metodología dual que compara la capacidad teórica (lo que la tarjeta declara poder hacer) con el comportamiento real (lo que la tarjeta hace en la red).
Verificación de la capacidad mediante la lectura de ficheros
La inspección de la capacidad se realiza mediante protocolos de comunicación estándar de UICC (ISO/IEC 7816), utilizando herramientas de emulación de interfaz, como la librería pySim. Este enfoque permite la lectura y decodificación precisa de los registros del sistema de archivos de la tarjeta.
La aplicación Python check_suci.py está diseñada para interactuar con la USIM mediante comandos APDU. Su función no es solo leer datos, sino aplicar la lógica de interpretación de los ficheros, como la decodificación de la EF.UST.
Al ejecutar el script con un dispositivo lector, se obtiene un veredicto formal sobre la capacidad de la tarjeta. Admite las opciones estándar de pySim, es importante mencionar que si la tarjeta SIM dispone de código PIN hay que especificarlo con el argumento --pin. La salida del script, al mostrar el estado de los servicios 124 y 125, cuantifica la capacidad de privacidad.
La herramienta Python únicamente requiere de la biblioteca pySim, por se crea un entorno virtual y se instala.
$ python3 -m venv suci_env
$ source suci_env/bin/activate
$ git clone https://github.com/osmocom/pysim
$ pip install .
A continuación se puede ejecutar la herramienta, este sería un ejemplo de ejecución para una tarjeta SIM que no soporta la funcionalidad SUCI.
$ python ../check_suci.py -p 0
================================================================
SUCI Support on the (U)SIM
================================================================
EF.UST - USIM Service Table:
[✗] Service 122 5GS Mobility Management Information
[✗] Service 123 5G Security Parameters
[✗] Service 124 Subscription identifier privacy support
[✗] Service 125 SUCI calculation by the USIM
DF.5GS / EF.SUCI_Calc_Info:
[✗] DF.5GS (5FC0) present
[✗] EF.SUCI_Calc_Info (4F07) present
----------------------------------------------------------------
Verdict : NO_SUCI_SUPPORT
SUPI Privacy: NO
Summary : The USIM does NOT declare identifier privacy support (service 124 inactive): SUCI is not supported. The SUPI/IMSI may be sent unencrypted.
----------------------------------------------------------------
Y en esta ejecución se soporta la funcionalidad SUCI, pero el cálculo se realiza mediante el uso del dispositivo móvil (servicio 125 desactivado).
================================================================
SUCI Support on the (U)SIM
================================================================
EF.UST - USIM Service Table:
[✓] Service 122 5GS Mobility Management Information
[✓] Service 123 5G Security Parameters
[✓] Service 124 Subscription identifier privacy support
[✗] Service 125 SUCI calculation by the USIM
DF.5GS / EF.SUCI_Calc_Info:
[✓] DF.5GS (5FC0) present
[✓] EF.SUCI_Calc_Info (4F07) present
Protection schemes (by priority):
- priority 0: Profile B - ECIES with secp256r1 (key_index=1)
Home network public keys:
- id 2: 65 bytes
----------------------------------------------------------------
Verdict : SUCI_ME_PROTECTED
SUPI Privacy: YES
Summary : The USIM supports SUCI with EFFECTIVE SUPI concealment (non-null scheme + network public key). SUCI calculation is handled by: ME (terminal).
----------------------------------------------------------------
Verificación operacional mediante una captura de la señalización
La prueba definitiva es la observación del tráfico. El proceso requiere de un entorno de prueba controlado utilizando un dispositivo Android con permisos para acceder al modo de diagnóstico o un módem 5G, utilizando la herramienta SCAT:
- Aislamiento: Se desactiva la radio (comando AT
AT+CFUN=0o activación del modo avión) para un control de estado determinístico. - Captura: Se inicia la herramienta para volcar los registros de la interfaz aire durante la fase de registro (
scat -t qc -s /dev/ttyUSB0 -F capture.pcap). - Activación: Se reactiva la radio (comando AT
AT+CFUN=0o desactivación del modo avión), forzando la ejecución del procedimiento de registro. - Análisis: Se examinan los mensajes NAS, específicamente el
Registration Request. La decodificación del campo5GS mobile identityrevela elProtection scheme Idutilizado por el terminal.
El resultado de esta captura es el dato operacional más fidedigno. Si el proceso de registro se completa, el análisis de la traza proporciona la evidencia final.
5GS mobile identity
Length: 13
0... .... = Spare: 0
.000 .... = SUPI format: IMSI (0)
.... 0... = Spare: 0
.... .001 = Type of identity: SUCI (1)
Mobile Country Code (MCC): Private network (...)
Mobile Network Code (MNC): Unknown (...)
Routing indicator: 0
.... 0000 = Protection scheme Id: NULL scheme (0)
Home network public key identifier: 0
MSIN: <MSIN numérico>
Conclusión
El análisis de la privacidad del identificador en 5G demuestra que la seguridad no es una función inherente del nombre SUCI, sino la consecuencia de la convergencia exitosa de axiomas protocolarios, desde la integración en la tarjeta USIM hasta el soporte en la propia red 5G.
Código fuente
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
check_suci.py - Checks if a (U)SIM supports SUCI in 5G.
Criteria (3GPP TS 31.102 / TS 33.501), with NUANCED logic:
* EF.UST (6F38) - USIM Service Table:
- Service 124 "Subscription identifier privacy support"
-> MINIMUM indicator of SUCI support (SUPI concealment).
- Service 125 "SUCI calculation by the USIM"
-> if active, the USIM calculates the SUCI;
if NOT active (but 124 is), the ME (the terminal) calculates it.
Therefore, 124 AND 125 are NOT required for "SUCI support".
* DF.5GS (5FC0) / EF.SUCI_Calc_Info (4F07):
- Presence of the file + protection schemes and public keys.
- Distinguishes "effective privacy" (there exists a non-null scheme
-> Profile A/ECIES-Curve25519 or Profile B/ECIES-secp256r1 with key)
from the case where only the null scheme is present (the SUPI is not concealed).
Communication with the card via pySim (osmocom). Requires a compatible reader.
Note: by default, pySim's native output (the tracing of T=0 TPDU of
the T0: case #N TPDU: ... => ... SW protocol, which pySim activates
upon import by setting its logging to DEBUG) is SILENCED. It is only shown
when passing --debug.
Examples:
python3 check_suci.py --pcsc-device 0
python3 check_suci.py --pcsc-device 0 --pin 1234
python3 check_suci.py --pcsc-device 0 --json
python3 check_suci.py --pcsc-device 0 --debug # shows pySim trace
(--pcsc-device, --device/-d for series, --modem-device, etc. are provided by pySim.)
"""
import argparse
import json
import logging
import sys
from pySim.transport import init_reader, argparse_add_reader_args
from pySim.transport import NoCardError, ReaderError
from pySim.commands import SimCardCommands
from pySim.exceptions import SwMatchError
from pySim.ts_31_102 import EF_UST, EF_SUCI_Calc_Info
# pySim, upon import, executes logging.root.setLevel(DEBUG) and attaches a
# handler that prints every TPDU ("T0: case #N TPDU: ..."). We silence this
# native output by default; with --debug it is reactivated (see main()).
logging.getLogger().setLevel(logging.WARNING)
# ----------------------------------------------------------------------------
# File Constants (TS 31.102 / TS 102 221)
# ----------------------------------------------------------------------------
USIM_AID_PREFIX = "a0000000871002" # RID + USIM application code
EF_DIR_FID = "2f00"
EF_UST_FID = "6f38"
DF_5GS_FID = "5fc0"
EF_SUCI_FID = "4f07"
# Protection scheme identifiers (TS 24.501)
SCHEME_NAMES = {
0: "null scheme (SUPI unencrypted)",
1: "Profile A - ECIES with Curve25519 (X25519)",
2: "Profile B - ECIES with secp256r1",
}
# ----------------------------------------------------------------------------
# Card Layer
# ----------------------------------------------------------------------------
def open_card(opts):
"""Initializes the reader and leaves the USIM ready (CLA/sel_ctrl of UICC)."""
tp = init_reader(opts)
tp.connect()
tp.reset_card()
scc = SimCardCommands(tp)
# By default SimCardCommands uses 2G values (cla=a0, sel=0000).
# For a UICC/USIM, we must use class 00 and SELECT by FID with FCP.
scc.cla_byte = "00"
scc.sel_ctrl = "0004"
return tp, scc
def _extract_aid_from_dir_record(rec_hex):
"""Extracts the AID (tag 4F) from an EF.DIR record (application template, tag 61)."""
try:
b = bytes.fromhex(rec_hex)
except ValueError:
return None
i = 0
while i + 1 < len(b):
tag = b[i]
if tag in (0x00, 0xFF): # padding
i += 1
continue
length = b[i + 1]
value = b[i + 2:i + 2 + length]
if tag == 0x61: # Application Template
j = 0
while j + 1 < len(value):
t2 = value[j]
l2 = value[j + 1]
v2 = value[j + 2:j + 2 + l2]
if t2 == 0x4F: # AID
return v2.hex()
j += 2 + l2
i += 2 + length
return None
def _fcp_file_size(fcp_hex):
"""Extracts the body size of a transparent EF (tag 80 inside the
FCP 62 template), tolerating padding bytes before tag 62. Returns None if
it cannot be determined. Does not use the strict pySim parser."""
try:
b = bytes.fromhex(fcp_hex or "")
except (ValueError, TypeError):
return None
i = 0
while i < len(b) and b[i] != 0x62: # skip padding (00/FF...) until tag 62
i += 1
if i + 1 >= len(b):
return None
ln = b[i + 1]
j = i + 2
if ln == 0x81: # length in BER format of 2+ bytes
ln = b[j]; j += 1
elif ln == 0x82:
ln = (b[j] << 8) | b[j + 1]; j += 2
template = b[j:j + ln]
k = 0
while k + 1 < len(template): # traverse TLVs looking for 80 (File size)
tag = template[k]
l = template[k + 1]
val = template[k + 2:k + 2 + l]
if tag == 0x80 and val:
return int.from_bytes(val, "big")
k += 2 + l
return None
def _read_binary_raw(scc):
"""READ BINARY from offset 0 without relying on FCP. Reads in blocks up to
256 bytes and handles the T=0 '6CXX' case (card indicates length)."""
out = ""
offset = 0
while offset <= 0xFFFF:
# Le=00 -> 256
data, sw = scc.send_apdu(scc.cla_byte + "b0%04x00" % offset)
sw = sw.lower()
if sw.startswith("6c"):
# Card specifies length (n)
n = int(sw[2:4], 16)
data, sw = scc.send_apdu(scc.cla_byte + "b0%04x%02x" % (offset, n))
sw = sw.lower()
if sw != "9000" or not data:
break
out += data
nbytes = len(data) // 2
if nbytes < 256: # last block
break
offset += nbytes
return out or None
def read_transparent_ef(scc, fid):
"""Selects a transparent EF and returns its content (hex), robustly
handling non-standard FCP responses."""
fcp, _ = scc.select_file(fid) # Raw FCP (select_file does NOT parse it)
size = _fcp_file_size(fcp)
if size:
try:
raw, _ = scc.read_binary(fid, length=size) # Explicit length: ignores FCP
if raw:
return raw
except SwMatchError:
pass
return _read_binary_raw(scc) # fallback: file is already selected
def find_usim_aid(scc):
"""Reads EF.DIR and returns the USIM AID, or None if not found or if
the card returns a non-standard FCP (in which case, a partial AID is used)."""
try:
scc.select_file(EF_DIR_FID)
for rec_no in range(1, 16):
try:
data, _ = scc.read_record(EF_DIR_FID, rec_no)
except SwMatchError:
break
if not data:
continue
aid = _extract_aid_from_dir_record(data.lower())
if aid and aid.startswith(USIM_AID_PREFIX):
return aid
except Exception:
# EF.DIR absent or non-standard FCP -> fallback: known partial AID
return None
return None
def select_usim(scc, forced_aid=None):
"""Selects ADF.USIM. Returns the AID used. Raises SwMatchError if not found."""
if forced_aid:
aid = forced_aid.lower().replace(" ", "")
else:
aid = find_usim_aid(scc) or USIM_AID_PREFIX # fallback: partial AID
scc.select_adf(aid)
return aid
def read_ust(scc):
"""Reads and decodes EF.UST. Returns (dict_services, raw_hex)."""
raw = read_transparent_ef(scc, EF_UST_FID)
return EF_UST().decode_hex(raw), raw
def read_suci_calc_info(scc):
"""Returns (df5gs_present, suci_present, decoded|None, raw|None)."""
try:
scc.select_file(DF_5GS_FID)
except SwMatchError:
return False, False, None, None
try:
scc.select_file(EF_SUCI_FID)
except SwMatchError:
return True, False, None, None
raw = read_transparent_ef(scc, EF_SUCI_FID)
try:
dec = EF_SUCI_Calc_Info().decode_hex(raw)
except Exception:
dec = None
return True, True, dec, raw
# ----------------------------------------------------------------------------
# Analysis (pure functions, testable without card)
# ----------------------------------------------------------------------------
def service_active(ust_dec, n):
"""True if service n is active in the dict returned by EF_UST.decode_hex."""
entry = ust_dec.get(n)
if entry is None:
entry = ust_dec.get(str(n))
return bool(entry and entry.get("activated"))
def _scheme_to_int(v):
"""Normalizes the scheme identifier (can come as int or name)."""
if isinstance(v, int):
return v
return {"null": 0, "A": 1, "B": 2}.get(str(v), -1)
def analyze(ust_dec, suci_dec, df5gs_present, suci_present):
"""Applies the verdict logic and returns a structured dict."""
s122 = service_active(ust_dec, 122)
s123 = service_active(ust_dec, 123)
s124 = service_active(ust_dec, 124)
s125 = service_active(ust_dec, 125)
# Schemes and keys from EF.SUCI_Calc_Info
schemes = []
if suci_dec:
for e in suci_dec.get("prot_scheme_id_list", []):
sid = _scheme_to_int(e.get("identifier"))
schemes.append({
"priority": e.get("priority"),
"id": sid,
"key_index": e.get("key_index"),
"name": SCHEME_NAMES.get(sid, "unknown (%s)" % sid),
})
pubkeys = []
if suci_dec:
for k in suci_dec.get("hnet_pubkey_list", []):
pk = k.get("hnet_pubkey")
length = len(pk) if isinstance(pk, (bytes, bytearray)) else None
pubkeys.append({"id": k.get("hnet_pubkey_identifier"), "length_bytes": length})
non_null = [s for s in schemes if s["id"] not in (0, -1)]
has_key = len(pubkeys) > 0
file_protects = bool(non_null) and has_key # the file offers real concealment
# ---- Verdict (nuanced logic) ----
if not s124:
verdict = "NO_SUCI_SUPPORT"
privacy = False
summary = ("The USIM does NOT declare identifier privacy support "
"(service 124 inactive): SUCI is not supported. The SUPI/IMSI "
"may be sent unencrypted.")
else:
calc_by = "USIM" if s125 else "ME (terminal)"
if suci_present:
if file_protects:
verdict = ("SUCI_USIM_PROTECTED" if s125 else "SUCI_ME_PROTECTED")
privacy = True
summary = ("The USIM supports SUCI with EFFECTIVE SUPI concealment "
"(non-null scheme + network public key). "
"SUCI calculation is handled by: %s." % calc_by)
else:
verdict = "SUCI_ONLY_NULL"
privacy = False
summary = ("The USIM supports SUCI (service 124), but EF.SUCI_Calc_Info "
"only offers the null scheme or lacks a public key: "
"the SUPI is NOT effectively concealed. "
"Calculation handled by: %s." % calc_by)
else:
# 124 active but no legible EF.SUCI_Calc_Info
if s125:
verdict = "SUCI_USIM_UNDETERMINABLE"
privacy = None
summary = ("The USIM supports SUCI and calculates it itself (service 125). "
"EF.SUCI_Calc_Info is not present/legible, so the "
"scheme used is internal and cannot be verified from the "
"file system.")
else:
verdict = "SUCI_SUPPORTED_WITHOUT_CALC_INFO"
privacy = False
summary = ("The USIM declares SUCI support (service 124) and the calculation "
"falls to the ME, but EF.SUCI_Calc_Info is missing: the terminal "
"would not have schemes/keys -> likely null scheme.")
return {
"services": {"122": s122, "123": s123, "124": s124, "125": s125},
"df_5gs_present": df5gs_present,
"suci_calc_info_present": suci_present,
"schemes": schemes,
"pubkeys": pubkeys,
"effective_privacy": privacy,
"verdict": verdict,
"summary": summary,
}
# ----------------------------------------------------------------------------
# Presentation
# ----------------------------------------------------------------------------
def _mark(b):
return "\u2713" if b else "\u2717" # checks / cross
def print_human(result):
s = result["services"]
print("=" * 64)
print(" SUCI Support on the (U)SIM")
print("=" * 64)
print("\n EF.UST - USIM Service Table:")
print(" [%s] Service 122 5GS Mobility Management Information" % _mark(s["122"]))
print(" [%s] Service 123 5G Security Parameters" % _mark(s["123"]))
print(" [%s] Service 124 Subscription identifier privacy support" % _mark(s["124"]))
print(" [%s] Service 125 SUCI calculation by the USIM" % _mark(s["125"]))
print("\n DF.5GS / EF.SUCI_Calc_Info:")
print(" [%s] DF.5GS (5FC0) present" % _mark(result["df_5gs_present"]))
print(" [%s] EF.SUCI_Calc_Info (4F07) present" % _mark(result["suci_calc_info_present"]))
if result["schemes"]:
print("\n Protection schemes (by priority):")
for e in result["schemes"]:
print(" - priority %s: %s (key_index=%s)"
% (e["priority"], e["name"], e["key_index"]))
if result["pubkeys"]:
print("\n Home network public keys:")
for k in result["pubkeys"]:
print(" - id %s: %s bytes" % (k["id"], k["length_bytes"]))
pe = result["effective_privacy"]
pe_txt = {True: "YES", False: "NO", None: "undeterminable"}[pe]
print("\n" + "-" * 64)
print(" Verdict : %s" % result["verdict"])
print(" SUPI Privacy: %s" % pe_txt)
print(" Summary : %s" % result["summary"])
print("-" * 64)
# ----------------------------------------------------------------------------
# Main
# ----------------------------------------------------------------------------
def build_parser():
p = argparse.ArgumentParser(
description="Checks SUCI (5G) support on a (U)SIM using pySim.")
argparse_add_reader_args(p) # --pcsc-device, --device, --modem-device, --apdu-trace...
p.add_argument("--pin", help="PIN1/CHV1, if EF.UST requires verification.")
p.add_argument("--aid", help="Force ADF.USIM AID (hex), skips EF.DIR reading.")
p.add_argument("--json", action="store_true", help="JSON output (machine readable).")
p.add_argument("--debug", action="store_true",
help="Shows pySim native output (TPDU trace 'T0: ...'), "
"which is silenced by default.")
return p
def main(argv=None):
opts = build_parser().parse_args(argv)
if opts.debug:
# Reactivate pySim native output (TPDU trace "T0: ...").
logging.getLogger().setLevel(logging.DEBUG)
# 1) Connection
try:
tp, scc = open_card(opts)
except (NoCardError, ReaderError) as e:
print("ERROR: could not access the card/reader: %s" % e, file=sys.stderr)
return 2
except Exception as e:
print("ERROR initializing reader: %s" % e, file=sys.stderr)
return 2
try:
# 2) Select ADF.USIM
try:
aid = select_usim(scc, forced_aid=opts.aid)
except SwMatchError:
print("ERROR: could not select ADF.USIM. Is this a USIM card?",
file=sys.stderr)
return 3
# 3) PIN (optional)
if opts.pin:
try:
scc.verify_chv(1, opts.pin.encode("ascii"))
except SwMatchError as e:
print("WARNING: PIN1 verification failed (%s). Continuing anyway."
% e, file=sys.stderr)
# 4) EF.UST
try:
ust_dec, _ = read_ust(scc)
except SwMatchError as e:
print("ERROR reading EF.UST (%s). Probably requires PIN1: use --pin."
% e, file=sys.stderr)
return 4
# 5) DF.5GS / EF.SUCI_Calc_Info
df5gs, suci_present, suci_dec, _ = read_suci_calc_info(scc)
# 6) Analysis + Output
result = analyze(ust_dec, suci_dec, df5gs, suci_present)
result["aid_usim"] = aid
if opts.json:
print(json.dumps(result, ensure_ascii=False, indent=2))
else:
print_human(result)
# Exit code: 0 if SUCI support, 1 if not.
return 0 if result["services"]["124"] else 1
finally:
try:
tp.disconnect()
except Exception:
pass
if __name__ == "__main__":
sys.exit(main())