laforge submitted this change.

View Change


Approvals: Jenkins Builder: Verified laforge: Looks good to me, approved
pySim.esim: Add class for parsing/encoding eSIM activation codes

Change-Id: I2256722c04b56e8d9c16a65e3cd94f6a46f4ed85
---
M pySim/esim/__init__.py
M tests/test_esim.py
2 files changed, 99 insertions(+), 0 deletions(-)

diff --git a/pySim/esim/__init__.py b/pySim/esim/__init__.py
index 4d6c609..dfacb83 100644
--- a/pySim/esim/__init__.py
+++ b/pySim/esim/__init__.py
@@ -1,4 +1,5 @@
import sys
+from typing import Optional
from importlib import resources

import asn1tools
@@ -14,3 +15,79 @@
#else:
#print(resources.read_text(__name__, 'asn1/rsp.asn'))
return asn1tools.compile_string(asn_txt, codec='der')
+
+
+# SGP.22 section 4.1 Activation Code
+class ActivationCode:
+ def __init__(self, hostname:str, token:str, oid: Optional[str] = None, cc_required: Optional[bool] = False):
+ if '$' in hostname:
+ raise ValueError('$ sign not permitted in hostname')
+ self.hostname = hostname
+ if '$' in token:
+ raise ValueError('$ sign not permitted in token')
+ self.token = token
+ # TODO: validate OID
+ self.oid = oid
+ self.cc_required = cc_required
+ # only format 1 is specified and supported here
+ self.format = 1
+
+ @staticmethod
+ def decode_str(ac: str) -> dict:
+ if ac[0] != '1':
+ raise ValueError("Unsupported AC_Format '%s'!" % ac[0])
+ ac_elements = ac.split('$')
+ d = {
+ 'oid': None,
+ 'cc_required': False,
+ }
+ d['format'] = ac_elements.pop(0)
+ d['hostname'] = ac_elements.pop(0)
+ d['token'] = ac_elements.pop(0)
+ if len(ac_elements):
+ oid = ac_elements.pop(0)
+ if oid != '':
+ d['oid'] = oid
+ if len(ac_elements):
+ ccr = ac_elements.pop(0)
+ if ccr == '1':
+ d['cc_required'] = True
+ return d
+
+ @classmethod
+ def from_string(cls, ac: str) -> 'ActivationCode':
+ """Create new instance from SGP.22 section 4.1 string representation."""
+ d = cls.decode_str(ac)
+ return cls(d['hostname'], d['token'], d['oid'], d['cc_required'])
+
+ def to_string(self, for_qrcode:bool = False) -> str:
+ """Convert from internal representation to SGP.22 section 4.1 string representation."""
+ if for_qrcode:
+ ret = 'LPA:'
+ else:
+ ret = ''
+ ret += '%d$%s$%s' % (self.format, self.hostname, self.token)
+ if self.oid:
+ ret += '$%s' % (self.oid)
+ elif self.cc_required:
+ ret += '$'
+ if self.cc_required:
+ ret += '$1'
+ return ret
+
+ def __str__(self):
+ return self.to_string()
+
+ def to_qrcode(self):
+ """Encode internal representation to QR code."""
+ import qrcode
+ qr = qrcode.QRCode()
+ qr.add_data(self.to_string(for_qrcode=True))
+ return qr.make_image()
+
+ def __repr__(self):
+ return "ActivationCode(format=%u, hostname='%s', token='%s', oid=%s, cc_required=%s)" % (self.format,
+ self.hostname,
+ self.token,
+ self.oid,
+ self.cc_required)
diff --git a/tests/test_esim.py b/tests/test_esim.py
index d4b494e..81926e5 100755
--- a/tests/test_esim.py
+++ b/tests/test_esim.py
@@ -22,9 +22,22 @@
from pySim.utils import b2h, h2b
from pySim.esim.bsp import *
import pySim.esim.rsp as rsp
+from pySim.esim import ActivationCode

from cryptography.hazmat.primitives.asymmetric import ec

+class TestActivationCode(unittest.TestCase):
+ def test_de_encode(self):
+ STRS = ['1$SMDP.GSMA.COM$04386-AGYFT-A74Y8-3F815',
+ '1$SMDP.GSMA.COM$04386-AGYFT-A74Y8-3F815$$1',
+ '1$SMDP.GSMA.COM$04386-AGYFT-A74Y8-3F815$1.3.6.1.4.1.31746$1',
+ '1$SMDP.GSMA.COM$04386-AGYFT-A74Y8-3F815$1.3.6.1.4.1.31746',
+ '1$SMDP.GSMA.COM$$1.3.6.1.4.1.31746']
+ for s in STRS:
+ ac = ActivationCode.from_string(s)
+ self.assertEqual(s, ac.to_string())
+
+
class TestECKA(unittest.TestCase):
def test_mode51(self):
curve = ec.SECP256R1()

To view, visit change 35749. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I2256722c04b56e8d9c16a65e3cd94f6a46f4ed85
Gerrit-Change-Number: 35749
Gerrit-PatchSet: 2
Gerrit-Owner: laforge <laforge@osmocom.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge@osmocom.org>
Gerrit-MessageType: merged