A
470498bf07
The SAIP profile now personalizes EF.IMSI via a PE-USIM referencing the standard "created by default" USIM template (OID 2.23.143.1.2.4), so the package carries ICCID + IMSI + Ki/OPc. IMSI content uses the existing TS 31.102 enc_imsi. Add decode_saip_profile(): splits the concatenated ProfileElements by DER TLV boundaries and decodes each (asn1tools decodes only one value at a time). Tests: full-sequence decode ([header, usim, akaParameter, end]) and IMSI-in-USIM round-trip; 93 pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5.3 KiB
5.3 KiB
Changelog
All notable changes to this project are documented here.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
Added
- Real SGP.22 SAIP profile generation (
profile.format: saip).server/saip_profile.pyencodes a DERProfileElementsequence — ProfileHeader (ICCID), PE-USIM personalizing EF.IMSI against the standard USIM template (2.23.143.1.2.4), and Milenage AKA parameters (Ki/OPc) — withasn1toolsagainst the officialPE_DefinitionsASN.1 schema. No pySim Python imports (the schema is located from an installed pySim, or viaSAIP_ASN_PATH), so it avoids the pyscard/smpp card-reader stack; the only runtime dep isasn1tools. Self-contained and optional:saip_available()gates it and the app falls back toprofile-package-v2when prerequisites are missing.decode_saip_profile()parses a package back into its ProfileElements. Optional deps inrequirements-saip.txt. A fully installable profile (remaining mandatory PEs + card validation) is the documented next layer. pytest.inito silence asn1tools/pyparsing deprecation warnings.
Security
/api/export(dumps every subscriber's Ki/OPc) now requires an admin token (server.admin_token, sent asX-Admin-Token, constant-time compared) and is disabled by default. Previously any WiFi client could pull all credentials unauthenticated.- Profile download URLs now use an unguessable capability token
(
secrets.token_urlsafe) instead of the sequential subscriber id. Profiles contain Ki/OPc, so the old/api/profile/<int>scheme let anyone on the WiFi enumerate and harvest every subscriber's keys. Unknown tokens return a plain 404 (indistinguishable from evicted) so tokens cannot be probed.
Hardened
- Core adapters set short MongoDB timeouts (
serverSelectionTimeoutMSetc.) so an unreachable core fails provisioning fast instead of hanging ~30s. - In-memory profile cache is now a size-capped LRU (
_MAX_CACHED_PROFILES) and can no longer grow without bound. load_configrejects an empty/non-mapping YAML file and validatesnetwork.op_keyis a 16-byte hex string, with clear error messages.- Fixed
FileNotFoundErrorwhendatabase.pathis a bare filename with no directory component:app.pyandscripts/init_db.pynow use a sharedensure_parent_dirhelper that no-ops on an empty parent. - Open5GS/free5gc adapters now use the configured
profile.apnfor the core session/DNN name instead of a hardcoded"internet". - Connections set
PRAGMA busy_timeout(5s) so concurrent writers under a threaded server wait for the lock instead of raising "database is locked". - Provisioning retries on the (astronomically rare) IMSI/ICCID collision instead of returning a 500.
- The in-memory profile cache is guarded by a lock (thread-safe under a threaded server), and the captive portal disables the activate button while a request is in flight to prevent double provisioning.
setup_wifi_ap.sh: the deployedconfig.yaml(holds the OP key) is nowchmod 600and owned by the service user instead of world-readable, anddata/is chowned afterinit_dbso the root-created SQLite file is writable by theesimservice (previously it was root-owned and the service could not write to it).- Added
test_app.py(API-level: provision, rate-limit, token download, 404 on unknown/enumerated ids, collision retry/give-up),test_utils.py(MAC/IMSI/op_key/config/ensure_parent_dir), and adapter APN coverage.
Added
- Phase-2 profile generation.
create_esim_profilenow emits a structured profile package with real 3GPP encodings instead of a loose JSON mock:enc_iccid— EF_ICCID encoding (nibble-swapped BCD, 10-byte, 0xF padded) per 3GPP TS 31.102 §4.2.1.enc_imsi— EF_IMSI encoding (length octet + parity nibble + nibble-swapped BCD) per 3GPP TS 31.102 §4.2.2.build_lpa_activation_code— SGP.22 LPA activation string (LPA:1$<smdp>$<matching-id>) so the profile carries a device-consumable activation reference.
CHANGELOG.md.
Changed
- Profile package
typeis nowprofile-package-v2; it embeds the raw credentials plus the encoded EF byte values and the LPA activation code.
Fixed
generate_imsihardcoded a 10-digit MSIN, producing invalid 16-digit IMSIs for a 3-digit MNC (e.g. MCC 302 / MNC 720). MSIN length is now15 - len(mcc) - len(mnc).check_rate_limitcompared timestamps as strings across mismatched formats (SQLiteCURRENT_TIMESTAMPvs Pythonisoformat), making the rate-limit window unreliable. Both sides are now normalized via SQLitedatetime().
Notes
- Full SGP.22 SIMalliance Interoperable Profile (SAIP / ASN.1 UPP) generation via osmocom pySim remains the follow-up step; it needs an SM-DP+ / LPA to be installed onto consumer devices over the air. The current package targets programmable SIMs and core-network sync.
[0.1.0] — 2026-07-01
Added
- Initial Phase-1 closed-loop eSIM provisioning stack: Flask API, SQLite subscriber store, credential generator (IMSI/Ki/OPc/ICCID), Open5GS and free5gc core adapters, captive portal, operational scripts, systemd units, network configs, and a test suite.