BER-TLV EF support (command, filesystem, shell)
This adds support for a new EF file type: BER-TLV files. They are
different from transparent and linear fixed EFs in that they neither
operate on a byte stream nor fixed-sized records, but on BER-TLV encoded
objects. One can specify a tag value, and the card will return the
entire TLV for that tag.
As indicated in the spec, the magic tag value 0x5C (92) will return a
list of tags existing in the file.
Change-Id: Ibfcce757dcd477fd0d6857f64fbb4346d6d62e63
diff --git a/pySim/filesystem.py b/pySim/filesystem.py
index edfe85d..b3e28ef 100644
--- a/pySim/filesystem.py
+++ b/pySim/filesystem.py
@@ -34,7 +34,7 @@
from typing import cast, Optional, Iterable, List, Any, Dict, Tuple
-from pySim.utils import sw_match, h2b, b2h, is_hex
+from pySim.utils import sw_match, h2b, b2h, is_hex, auto_int, bertlv_parse_one
from pySim.construct import filter_dict
from pySim.exceptions import *
from pySim.jsonpath import js_path_find, js_path_modify
@@ -914,7 +914,7 @@
return b''.join(chunks)
-class BerTlvEF(TransparentEF):
+class BerTlvEF(CardEF):
"""BER-TLV EF (Entry File) in the smart card filesystem.
A BER-TLV EF is a binary file with a BER (Basic Encoding Rules) TLV structure
@@ -922,6 +922,61 @@
around TransparentEF as a place-holder, so we can already define EFs of BER-TLV
type without fully supporting them."""
+ @with_default_category('BER-TLV EF Commands')
+ class ShellCommands(CommandSet):
+ """Shell commands specific for BER-TLV EFs."""
+ def __init__(self):
+ super().__init__()
+
+ retrieve_data_parser = argparse.ArgumentParser()
+ retrieve_data_parser.add_argument('tag', type=auto_int, help='BER-TLV Tag of value to retrieve')
+ @cmd2.with_argparser(retrieve_data_parser)
+ def do_retrieve_data(self, opts):
+ """Retrieve (Read) data from a BER-TLV EF"""
+ (data, sw) = self._cmd.rs.retrieve_data(opts.tag)
+ self._cmd.poutput(data)
+
+ def do_retrieve_tags(self, opts):
+ """List tags available in a given BER-TLV EF"""
+ tags = self._cmd.rs.retrieve_tags()
+ self._cmd.poutput(tags)
+
+ set_data_parser = argparse.ArgumentParser()
+ set_data_parser.add_argument('tag', type=auto_int, help='BER-TLV Tag of value to set')
+ set_data_parser.add_argument('data', help='Data bytes (hex format) to write')
+ @cmd2.with_argparser(set_data_parser)
+ def do_set_data(self, opts):
+ """Set (Write) data for a given tag in a BER-TLV EF"""
+ (data, sw) = self._cmd.rs.set_data(opts.tag, opts.data)
+ if data:
+ self._cmd.poutput(data)
+
+ del_data_parser = argparse.ArgumentParser()
+ del_data_parser.add_argument('tag', type=auto_int, help='BER-TLV Tag of value to set')
+ @cmd2.with_argparser(del_data_parser)
+ def do_delete_data(self, opts):
+ """Delete data for a given tag in a BER-TLV EF"""
+ (data, sw) = self._cmd.rs.set_data(opts.tag, None)
+ if data:
+ self._cmd.poutput(data)
+
+
+ def __init__(self, fid:str, sfid:str=None, name:str=None, desc:str=None, parent:CardDF=None,
+ size={1,None}):
+ """
+ Args:
+ fid : File Identifier (4 hex digits)
+ sfid : Short File Identifier (2 hex digits, optional)
+ name : Brief name of the file, lik EF_ICCID
+ desc : Description of the file
+ parent : Parent CardFile object within filesystem hierarchy
+ size : tuple of (minimum_size, recommended_size)
+ """
+ super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent)
+ self._construct = None
+ self.size = size
+ self.shell_commands = [self.ShellCommands()]
+
class RuntimeState(object):
"""Represent the runtime state of a session with a card."""
@@ -1172,6 +1227,43 @@
data_hex = self.selected_file.encode_record_hex(data)
return self.update_record(rec_nr, data_hex)
+ def retrieve_data(self, tag:int=0):
+ """Read a DO/TLV as binary data.
+
+ Args:
+ tag : Tag of TLV/DO to read
+ Returns:
+ hex string of full BER-TLV DO including Tag and Length
+ """
+ if not isinstance(self.selected_file, BerTlvEF):
+ raise TypeError("Only works with BER-TLV EF")
+ # returns a string of hex nibbles
+ return self.card._scc.retrieve_data(self.selected_file.fid, tag)
+
+ def retrieve_tags(self):
+ """Retrieve tags available on BER-TLV EF.
+
+ Returns:
+ list of integer tags contained in EF
+ """
+ if not isinstance(self.selected_file, BerTlvEF):
+ raise TypeError("Only works with BER-TLV EF")
+ data, sw = self.card._scc.retrieve_data(self.selected_file.fid, 0x5c)
+ tag, length, value = bertlv_parse_one(h2b(data))
+ return list(value)
+
+ def set_data(self, tag:int, data_hex:str):
+ """Update a TLV/DO with given binary data
+
+ Args:
+ tag : Tag of TLV/DO to be written
+ data_hex : Hex string binary data to be written (value portion)
+ """
+ if not isinstance(self.selected_file, BerTlvEF):
+ raise TypeError("Only works with BER-TLV EF")
+ return self.card._scc.set_data(self.selected_file.fid, tag, data_hex, conserve=self.conserve_write)
+
+
class FileData(object):