fix: use the card's own SimCardCommands for ADM and AUTHENTICATE (v2.7.20)

/api/verify-adm sent VERIFY with CLA A0 on a UICC (SW 6E00) while
pySim-shell's verify_adm worked: fast init builds the card on its own
SimCardCommands instance, but __main__ kept server.scc at the startup
placeholder left at the SIM defaults; only an equip repointed it.

- __main__ adopts card._scc after init (cat_cla still set on it), so
  server.scc carries the card's cla_byte/sel_ctrl from startup on.
- _verify_adm prefers app.rs.lchan[0].scc / app.card._scc, exactly like
  pySim-shell's verify_adm, independent of server.scc.
- netsim AUTHENTICATE follows the card class: a UICC gets 00 88 00 81 22
  (RAND+AUTN, DB/DC response), a SIM gets A0 88 00 00 10 (RAND only,
  SRES+Kc); the 61xx GET RESPONSE uses the same CLA.
- tests: ADM with a stale placeholder scc; 2G builder/parser; SIM-CLA
  runner case; help EN/RU and AGENTS updated.
This commit is contained in:
2026-09-21 22:13:52 +03:00
parent 0446d2a93c
commit f8ba5dea2c
10 changed files with 92 additions and 12 deletions
+27 -6
View File
@@ -336,15 +336,30 @@ def build_auth_apdu(rand_hex, autn_hex, cla='00'):
rand = bytes.fromhex(_norm_hex(rand_hex, 16))
autn = bytes.fromhex(_norm_hex(autn_hex, 16))
body = bytes([len(rand)]) + rand + bytes([len(autn)]) + autn
return '%s880081%02X%s' % (cla, len(body), body.hex().upper())
return '%s880081%02X%s' % (cla.upper(), len(body), body.hex().upper())
def parse_auth_response(data_hex):
"""Parse the AUTHENTICATE response: DB (success) or DC (sync failure)."""
def build_auth_apdu_gsm(rand_hex, cla='a0'):
"""2G AUTHENTICATE (GSM 11.11 8.8): P1/P2 00, Lc 0x10, RAND only.
The response is SRES (4 B) followed by Kc (8 B).
"""
rand = bytes.fromhex(_norm_hex(rand_hex, 16))
return '%s88000010%s' % (cla.upper(), rand.hex().upper())
def parse_auth_response(data_hex, gsm=False):
"""Parse the AUTHENTICATE response: DB (success) or DC (sync failure) for
3G/EPS/5G, SRES||Kc for the 2G SIM command."""
try:
data = bytes.fromhex(_norm_hex(data_hex or ''))
except ValueError:
return None
if gsm:
if len(data) < 12:
return None
return {'type': 'gsm', 'sres': data[0:4].hex().upper(),
'kc': data[4:12].hex().upper()}
if not data or data[0] not in (0xDB, 0xDC):
return None
out = {'type': 'success' if data[0] == 0xDB else 'synchronisation_failure'}
@@ -815,11 +830,17 @@ class NetSimRunner:
autn = self.p('autn') or rand_hex(16)
if not self.scc:
raise StepError('card session not available')
apdu = build_auth_apdu(rand, autn)
# The command follows the card's class: a UICC gets the 3G/EPS/5G
# AUTHENTICATE (P2 81, RAND + AUTN), a SIM the 2G one (P1/P2 00,
# RAND only, SRES + Kc in the response).
cla = (getattr(self.scc, 'cla_byte', None) or '00').upper()
gsm = cla == 'A0'
apdu = (build_auth_apdu_gsm(rand, cla) if gsm
else build_auth_apdu(rand, autn, cla))
data, sw = self.scc._tp.send_apdu(apdu)
if sw.startswith('61'):
data, sw = self.scc._tp.send_apdu('00C00000' + sw[2:4])
parsed = parse_auth_response(data)
data, sw = self.scc._tp.send_apdu(cla + 'C00000' + sw[2:4])
parsed = parse_auth_response(data, gsm=gsm)
self._add('authenticate', data=apdu, response=(data or '').upper(),
parsed=parsed, sw=sw, ok=True)