-
OTAMan SIM OTA with a Human Face v1.9.4
+
OTAMan SIM OTA with a Human Face v1.9.5
github
@@ -812,9 +812,10 @@
+
-
+
@@ -2180,7 +2181,9 @@ function genSp() {
packet.set(encrypted, 10);
}
- resultEl.value = bytesToHex(packet);
+ // pySim encode_cmd parity: the 2-byte CPL prefix is transmitted only when
+ // ciphering is applied; unciphered packets start at CHL (v1.9.5).
+ resultEl.value = bytesToHex(ciphering ? packet : packet.subarray(2));
}
// ===== BER-TLV =====
@@ -4036,7 +4039,9 @@ async function pysimSendOta() {
if (!sp) { alert('No secured packet to send. Generate a secure packet first.'); return; }
const statusEl = document.getElementById('pysim-status');
const sendResultEl = document.getElementById('sp-send-result');
+ const porStatusEl = document.getElementById('sp-por-status');
sendResultEl.classList.add('hidden');
+ porStatusEl.classList.add('hidden');
statusEl.innerHTML = '' + t('Sending OTA...') + '';
try {
const data = await pysimFetch('/api/send-ota', { sp, ...spParams() });
@@ -4052,6 +4057,10 @@ async function pysimSendOta() {
msg += ' | PoR status: ' + por.response_status + ' (TAR ' + por.tar + ')';
if (por.cntr) msg += ' | PoR CNTR: ' + por.cntr;
if (por.decoded && por.decoded.last_status_word) msg += ' | last SW ' + por.decoded.last_status_word + ' ' + (por.decoded.last_response_data || '');
+ const okPor = por.response_status === 'por_ok';
+ porStatusEl.textContent = 'PoR: ' + por.response_status;
+ porStatusEl.classList.remove('hidden', okPor ? 'text-red-600' : 'text-green-600');
+ porStatusEl.classList.add(okPor ? 'text-green-600' : 'text-red-600');
}
sendResultEl.textContent = msg;
if (por && por.raw) {
diff --git a/frontend/sw.js b/frontend/sw.js
index 5763878..bea221c 100644
--- a/frontend/sw.js
+++ b/frontend/sw.js
@@ -1,4 +1,4 @@
-const CACHE = 'otaman-v15';
+const CACHE = 'otaman-v16';
const URLS = [
'index.html',
'help.html',
diff --git a/frontend/tests/sp.test.js b/frontend/tests/sp.test.js
index 5ddad3c..a81b451 100644
--- a/frontend/tests/sp.test.js
+++ b/frontend/tests/sp.test.js
@@ -93,13 +93,13 @@ test('ciphered + CC SPI 16/01 (counter_must_be_higher, plaintext PoR)', () => {
test('unciphered + CC SPI 02/09', () => {
assert.strictEqual(
makeRun({ 'sp-spi1': '02', 'sp-spi2-hex': '09' }),
- '001D1502091515B0000000000000010085A8CA1A9828B0BB00A40000023F00');
+ '1502091515B0000000000000010085A8CA1A9828B0BB00A40000023F00');
});
-test('unciphered packet uses CPL = octets from CHL to end (0x001d)', () => {
+test('unciphered packet starts at CHL, no CPL prefix (pySim parity)', () => {
const out = makeRun({ 'sp-spi1': '02', 'sp-spi2-hex': '09' });
- assert.strictEqual(out.slice(0, 4), '001D');
- assert.strictEqual(out.length, 62);
+ assert.strictEqual(out.slice(0, 2), '15');
+ assert.strictEqual(out.length, 58);
});
test('sysmocom public reference vector (spi1 04 / spi2 19, cntr=0)', () => {
@@ -169,7 +169,7 @@ test('AES unciphered + CC SPI 12/09 (counter higher)', () => {
'sp-kic-key': KIC_AES,
'sp-kid-key': KID_AES,
}),
- '001D1512092222B0001100000000110029826122C7A0B79500A40004023F00');
+ '1512092222B0001100000000110029826122C7A0B79500A40004023F00');
});
test('AES ciphered + CC SPI 1E/19 (counter +1)', () => {
diff --git a/pyproject.toml b/pyproject.toml
index 29ced13..d9e89e5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "pysim-otaman-server"
-version = "1.9.4"
+version = "1.9.5"
description = "HTTP REST server wrapping pysim for the OTAMan PWA"
requires-python = ">=3.8"
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
diff --git a/pysim_otaman_server/server.py b/pysim_otaman_server/server.py
index d93e794..58143a0 100644
--- a/pysim_otaman_server/server.py
+++ b/pysim_otaman_server/server.py
@@ -18,7 +18,7 @@ from osmocom.construct import GsmOrUcs2Adapter
from osmocom.tlv import BER_TLV_IE
-VERSION = '1.9.4'
+VERSION = '1.9.5'
# Static file serving (the PWA lives in /frontend, served by this server