From 54dfb2f6b608119af789db69d4ccfe6dfa323bc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD=20=D0=A2=D1=80=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Sun, 30 Aug 2026 14:13:13 +0300 Subject: [PATCH] =?UTF-8?q?v1.9.17:=20fix=20explore=20card=20=E2=80=94=20c?= =?UTF-8?q?hain=20GET=20STATUS=20+=20GET=20RESPONSE=20inside=20SCP80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Key fix: re-add chained C0000000 (GET RESPONSE Le=00) in paginate() so the full GP APDU inside SCP80 is 80F2p2024F0000C0000000, matching the working tool. The card's SCP80 layer executes both commands internally (GET STATUS → 61XX → GET RESPONSE) and puts the final 9000 + data in the PoR. Also: server logging improvements (RAM RESPONSE-PACKET label, no truncation of FETCH/PoR hex), docs for /api/ram-install endpoint, minor test fix. --- docs/api.md | 55 ++++ frontend/index.html | 556 +++++++++++++++++++++++++++++++++- frontend/sw.js | 2 +- frontend/tests/ram.test.js | 2 +- pyproject.toml | 2 +- pysim_otaman_server/server.py | 61 +++- 6 files changed, 654 insertions(+), 24 deletions(-) diff --git a/docs/api.md b/docs/api.md index 45775bf..4928dfd 100644 --- a/docs/api.md +++ b/docs/api.md @@ -30,6 +30,7 @@ connect and warns if versions are incompatible. | `/api/apdu` | POST | Raw APDU send | | `/api/help` | POST | pySim help for a given command | | `/api/send-ota` | POST | SCP80 OTA secured packet delivery | +| `/api/ram-install` | POST | Install a Java Card `.cap` file via SCP80 (INSTALL[for load] → LOAD ×N → INSTALL[for install]) | | `/api/sp-verify` | POST | Verify secured packet against pySim reference | | `/api/menu` | GET | Current STK menu (title + items + active) | | `/api/menu-select` | POST | ENVELOPE(Menu Selection) with item_id | @@ -140,6 +141,60 @@ same `por` structure if decoding succeeds. The SPI2 `por_in_submit` bit (0x20) selects submit-mode PoR. +### `POST /api/ram-install` + +Install a Java Card `.cap` file on the card via GlobalPlatform commands (INSTALL[for load] → LOAD ×N → INSTALL[for install (+ make selectable)]) wrapped in SCP80 secured packets. Each step is sent via ENVELOPE and its PoR is checked; the sequence aborts on the first PoR error. Requires pySim with `pySim.javacard.CapFile` and `pySim.global_platform` available on the server. + +**Request body:** +```json +{ + "cap_hex": "DECAFFED...", + "sd_aid": "A000000003000000", + "install_params": "C90000", + "stk_params": "", + "nv_quota": 0, + "volatile_quota": 0, + "make_selectable": true, + "spi1": "0E", "spi2": "01", + "kic": "15", "kid": "15", + "tar": "000000", + "cntr": "0000000001", + "kicKey": "D6FCC023...", + "kidKey": "1B07E7E0..." +} +``` + +| Field | Req | Description | +|---|---|---| +| `cap_hex` | yes | Even-length hex of the `.cap` file (zipped Java Card CAP), max 48 kB (98304 hex chars) | +| `sd_aid` | no | Security Domain AID for INSTALL[for load]; empty → default ISD `A000000003000000` | +| `install_params` | no | Hex C9 TLV install parameters; if empty, `gen_install_parameters()` is used with the quota/stk params | +| `stk_params` | no | Hex CA TLV (TS 102 226 §8.2.1.3.2.1) for SIM toolkit app-specific params | +| `nv_quota` / `volatile_quota` | no | Integer memory quotas (bytes) for `gen_install_parameters()` | +| `make_selectable` | no | If true (default), final INSTALL uses P1=`0C` (install + make selectable) | + +**Response (success):** +```json +{"success": true, "failed_step": null, + "steps": [{"name": "install_for_load", "apdu": "80E60200...", "por_status": "por_ok", "sw": "9000"}, + {"name": "load_0", "apdu": "80E80000...", "por_status": "por_ok", "sw": "9000"}, + {"name": "install_for_install", "apdu": "80E60C00...", "por_status": "por_ok", "sw": "9000"}], + "final_cntr": "0000000004", + "load_file_aid": "A000000003000000", + "module_aid": "A000000003000000", + "application_aid": "A000000003000000"} +``` + +**Response (failure):** +```json +{"success": false, "failed_step": "load_1", + "steps": [{"name": "install_for_load", "por_status": "por_ok", "sw": "9000"}, + {"name": "load_1", "por_status": "rc_error", "sw": null}], + "error": "..."} +``` + +The `steps` array contains one entry per GP command. `final_cntr` is the counter value after all successful steps (use it to update the card preset). The response is not streamed — all steps run server-side before the JSON is returned. + ### `POST /api/sp-verify` Cross-check a secured packet against pySim's `OtaDialectSms.encode_cmd` diff --git a/frontend/index.html b/frontend/index.html index c49f465..4e97f27 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -18,7 +18,7 @@
-

OTAMan SIM OTA with a Human Face v1.9.12

+

OTAMan SIM OTA with a Human Face v1.9.17

github @@ -616,8 +616,8 @@
- - + +