lists.osmocom.org
Sign In Sign Up
Manage this list Sign In Sign Up

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview

gerrit-log

Thread Start a new thread
Download
Threads by month
  • ----- 2026 -----
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2025 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2024 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2023 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2022 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
gerrit-log@lists.osmocom.org

July 2023

  • 1 participants
  • 3051 discussions
[S] Change in pysim[master]: pySim-prog: Also accept 18-digit ICCIDs
by laforge 12 Jul '23

12 Jul '23
laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/33672 ) Change subject: pySim-prog: Also accept 18-digit ICCIDs ...................................................................... pySim-prog: Also accept 18-digit ICCIDs There are cards with 18-digit ICCIDs, so let's be a bit more tolerant. Change-Id: I5395daeb2e96987335f6f9bf540c28d516001394 --- M pySim-prog.py 1 file changed, 13 insertions(+), 2 deletions(-) Approvals: Jenkins Builder: Verified fixeria: Looks good to me, approved diff --git a/pySim-prog.py b/pySim-prog.py index 7c644b7..028db55 100755 --- a/pySim-prog.py +++ b/pySim-prog.py @@ -357,8 +357,8 @@ # ICCID (19 digits, E.118), though some phase1 vendors use 20 :( if opts.iccid is not None: iccid = opts.iccid - if not _isnum(iccid, 19) and not _isnum(iccid, 20): - raise ValueError('ICCID must be 19 or 20 digits !') + if not _isnum(iccid, 18) and not _isnum(iccid, 19) and not _isnum(iccid, 20): + raise ValueError('ICCID must be 18, 19 or 20 digits !') else: if opts.num is None: -- To view, visit https://gerrit.osmocom.org/c/pysim/+/33672 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: pysim Gerrit-Branch: master Gerrit-Change-Id: I5395daeb2e96987335f6f9bf540c28d516001394 Gerrit-Change-Number: 33672 Gerrit-PatchSet: 1 Gerrit-Owner: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: dexter <pmaier(a)sysmocom.de> Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-MessageType: merged
1 0
0 0
[S] Change in pysim[master]: pySim/utils: define 'Hexstr' using NewType
by laforge 12 Jul '23

12 Jul '23
laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/33656 ) Change subject: pySim/utils: define 'Hexstr' using NewType ...................................................................... pySim/utils: define 'Hexstr' using NewType This means Hexstr is no longer an alias for 'str', but a distinct new type, a sub-class of 'str'. Change-Id: Ifb787670ed0e149ae6fcd0e6c0626ddc68880068 --- M pySim/utils.py 1 file changed, 14 insertions(+), 2 deletions(-) Approvals: fixeria: Looks good to me, but someone else must approve Jenkins Builder: Verified laforge: Looks good to me, approved diff --git a/pySim/utils.py b/pySim/utils.py index c6362fa..b534580 100644 --- a/pySim/utils.py +++ b/pySim/utils.py @@ -7,7 +7,7 @@ import abc import string from io import BytesIO -from typing import Optional, List, Dict, Any, Tuple +from typing import Optional, List, Dict, Any, Tuple, NewType # Copyright (C) 2009-2010 Sylvain Munaut <tnt(a)246tNt.com> # Copyright (C) 2021 Harald Welte <laforge(a)osmocom.org> @@ -27,7 +27,7 @@ # # just to differentiate strings of hex nibbles from everything else -Hexstr = str +Hexstr = NewType('Hexstr', str) def h2b(s: Hexstr) -> bytearray: -- To view, visit https://gerrit.osmocom.org/c/pysim/+/33656 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: pysim Gerrit-Branch: master Gerrit-Change-Id: Ifb787670ed0e149ae6fcd0e6c0626ddc68880068 Gerrit-Change-Number: 33656 Gerrit-PatchSet: 4 Gerrit-Owner: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: dexter <pmaier(a)sysmocom.de> Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-MessageType: merged
1 0
0 0
[S] Change in pysim[master]: pySim/tlv.py: Fix TLV_IE_Collection from_dict with nested collections
by laforge 12 Jul '23

12 Jul '23
laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/33662 ) Change subject: pySim/tlv.py: Fix TLV_IE_Collection from_dict with nested collections ...................................................................... pySim/tlv.py: Fix TLV_IE_Collection from_dict with nested collections This is all quite complicated. In general, the TLV_IE.to_dict() method obviously is expected to return a dict (with key equal to the snake-case name of the class, value to the decode IE value). This single-entry dict can then be passed back to the from_dict() method to build the binary representation. However, with a TLV_IE_Collection, any TLV_IE can occur any number of times, so we need an array to represent it (dict would need unique key, which doesn't exist in multiple instances of same TLV IE). Hence, the TLV_IE_Collection.to_dict() method actually returns a list of dicts, rather than a dict itself. Each dict in the list represents one TLV_IE. When encoding such a TLV_IE_Collection back from the list-of-dicts, we so far didn't handle this special case and tried to de-serialize with a class-name-keyed dict, which doesn't work. This patch fixes a regression in the aram_store_ref_ar_do pySim-shell command which got introduced in Change-Id I3dd5204510e5c32ef1c4a999258d87cb3f1df8c8 While we're fixing it, add some additional comments to why things are how they are. Change-Id: Ibdd30cf1652c864f167b1b655b49a87941e15fd5 --- M pySim/tlv.py 1 file changed, 43 insertions(+), 1 deletion(-) Approvals: Jenkins Builder: Verified fixeria: Looks good to me, but someone else must approve laforge: Looks good to me, approved diff --git a/pySim/tlv.py b/pySim/tlv.py index 3e245b5..827972f 100644 --- a/pySim/tlv.py +++ b/pySim/tlv.py @@ -386,12 +386,21 @@ of dicts, where they key indicates the name of the TLV_IE subclass to use.""" # list of instances of TLV_IE collection member classes appearing in the data res = [] + # iterate over members of the list passed into "decoded" for i in decoded: + # iterate over all the keys (typically one!) within the current list item dict for k in i.keys(): + # check if we have a member identified by the dict key if k in self.members_by_name: + # resolve the class for that name; create an instance of it cls = self.members_by_name[k] inst = cls() - inst.from_dict({k: i[k]}) + if cls.nested_collection_cls: + # in case of collections, we want to pass the raw "value" portion to from_dict, + # as to_dict() below intentionally omits the collection-class-name as key + inst.from_dict(i[k]) + else: + inst.from_dict({k: i[k]}) res.append(inst) else: raise ValueError('%s: Unknown TLV Class %s in %s; expected %s' % @@ -400,6 +409,8 @@ return res def to_dict(self): + # we intentionally return not a dict, but a list of dicts. We could prefix by + # self.__class__.__name__, but that is usually some meaningless auto-generated collection name. return [x.to_dict() for x in self.children] def to_bytes(self): -- To view, visit https://gerrit.osmocom.org/c/pysim/+/33662 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: pysim Gerrit-Branch: master Gerrit-Change-Id: Ibdd30cf1652c864f167b1b655b49a87941e15fd5 Gerrit-Change-Number: 33662 Gerrit-PatchSet: 2 Gerrit-Owner: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: dexter <pmaier(a)sysmocom.de> Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: lynxis lazus <lynxis(a)fe80.eu> Gerrit-MessageType: merged
1 0
0 0
[M] Change in pysim[master]: pySim/transport: More type annotations
by laforge 12 Jul '23

12 Jul '23
laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/33657 ) Change subject: pySim/transport: More type annotations ...................................................................... pySim/transport: More type annotations Change-Id: I62e081271e3a579851a588a4ed7282017e56f852 --- M pySim/transport/__init__.py M pySim/transport/calypso.py M pySim/transport/modem_atcmd.py M pySim/transport/pcsc.py M pySim/transport/serial.py M pySim/utils.py 6 files changed, 45 insertions(+), 25 deletions(-) Approvals: fixeria: Looks good to me, but someone else must approve Jenkins Builder: Verified laforge: Looks good to me, approved diff --git a/pySim/transport/__init__.py b/pySim/transport/__init__.py index 09752ac..a16fdb3 100644 --- a/pySim/transport/__init__.py +++ b/pySim/transport/__init__.py @@ -6,10 +6,11 @@ import abc import argparse from typing import Optional, Tuple +from construct import Construct from pySim.exceptions import * from pySim.construct import filter_dict -from pySim.utils import sw_match, b2h, h2b, i2h, Hexstr +from pySim.utils import sw_match, b2h, h2b, i2h, Hexstr, SwHexstr, SwMatchstr from pySim.cat import ProactiveCommand, CommandDetails, DeviceIdentities, Result # @@ -60,14 +61,14 @@ class LinkBase(abc.ABC): """Base class for link/transport to card.""" - def __init__(self, sw_interpreter=None, apdu_tracer=None, + def __init__(self, sw_interpreter=None, apdu_tracer: Optional[ApduTracer]=None, proactive_handler: Optional[ProactiveHandler]=None): self.sw_interpreter = sw_interpreter self.apdu_tracer = apdu_tracer self.proactive_handler = proactive_handler @abc.abstractmethod - def _send_apdu_raw(self, pdu: str) -> Tuple[str, str]: + def _send_apdu_raw(self, pdu: Hexstr) -> Tuple[Hexstr, Hexstr]: """Implementation specific method for sending the PDU.""" def set_sw_interpreter(self, interp): @@ -75,7 +76,7 @@ self.sw_interpreter = interp @abc.abstractmethod - def wait_for_card(self, timeout: int = None, newcardonly: bool = False): + def wait_for_card(self, timeout: Optional[int] = None, newcardonly: bool = False): """Wait for a card and connect to it Args: @@ -98,7 +99,7 @@ """Resets the card (power down/up) """ - def send_apdu_raw(self, pdu: str): + def send_apdu_raw(self, pdu: Hexstr) -> Tuple[Hexstr, SwHexstr]: """Sends an APDU with minimal processing Args: @@ -115,7 +116,7 @@ self.apdu_tracer.trace_response(pdu, sw, data) return (data, sw) - def send_apdu(self, pdu): + def send_apdu(self, pdu: Hexstr) -> Tuple[Hexstr, SwHexstr]: """Sends an APDU and auto fetch response data Args: @@ -144,7 +145,7 @@ return data, sw - def send_apdu_checksw(self, pdu, sw="9000"): + def send_apdu_checksw(self, pdu: Hexstr, sw: SwMatchstr = "9000") -> Tuple[Hexstr, SwHexstr]: """Sends an APDU and check returned SW Args: @@ -212,7 +213,8 @@ raise SwMatchError(rv[1], sw.lower(), self.sw_interpreter) return rv - def send_apdu_constr(self, cla, ins, p1, p2, cmd_constr, cmd_data, resp_constr): + def send_apdu_constr(self, cla: Hexstr, ins: Hexstr, p1: Hexstr, p2: Hexstr, cmd_constr: Construct, + cmd_data: Hexstr, resp_constr: Construct) -> Tuple[dict, SwHexstr]: """Build and sends an APDU using a 'construct' definition; parses response. Args: @@ -237,8 +239,9 @@ rsp = None return (rsp, sw) - def send_apdu_constr_checksw(self, cla, ins, p1, p2, cmd_constr, cmd_data, resp_constr, - sw_exp="9000"): + def send_apdu_constr_checksw(self, cla: Hexstr, ins: Hexstr, p1: Hexstr, p2: Hexstr, + cmd_constr: Construct, cmd_data: Hexstr, resp_constr: Construct, + sw_exp: SwMatchstr="9000") -> Tuple[dict, SwHexstr]: """Build and sends an APDU using a 'construct' definition; parses response. Args: diff --git a/pySim/transport/calypso.py b/pySim/transport/calypso.py index e1326ac..4244b23 100644 --- a/pySim/transport/calypso.py +++ b/pySim/transport/calypso.py @@ -21,9 +21,11 @@ import socket import os +from typing import Optional, Tuple + from pySim.transport import LinkBase from pySim.exceptions import * -from pySim.utils import h2b, b2h +from pySim.utils import h2b, b2h, Hexstr, SwHexstr class L1CTLMessage: @@ -91,7 +93,7 @@ def __del__(self): self.sock.close() - def wait_for_rsp(self, exp_len=128): + def wait_for_rsp(self, exp_len: int = 128): # Wait for incoming data (timeout is 3 seconds) s, _, _ = select.select([self.sock], [], [], 3.0) if not s: @@ -118,10 +120,10 @@ def disconnect(self): pass # Nothing to do really ... - def wait_for_card(self, timeout=None, newcardonly=False): + def wait_for_card(self, timeout: Optional[int] = None, newcardonly: bool = False): pass # Nothing to do really ... - def _send_apdu_raw(self, pdu): + def _send_apdu_raw(self, pdu: Hexstr) -> Tuple[Hexstr, SwHexstr]: # Request FULL reset req_msg = L1CTLMessageSIM(h2b(pdu)) diff --git a/pySim/transport/modem_atcmd.py b/pySim/transport/modem_atcmd.py index ea50bc9..c6f6c57 100644 --- a/pySim/transport/modem_atcmd.py +++ b/pySim/transport/modem_atcmd.py @@ -20,7 +20,9 @@ import serial import time import re +from typing import Optional, Tuple +from pySim.utils import Hexstr, SwHexstr from pySim.transport import LinkBase from pySim.exceptions import * @@ -136,10 +138,10 @@ def disconnect(self): pass # Nothing to do really ... - def wait_for_card(self, timeout=None, newcardonly=False): + def wait_for_card(self, timeout: Optional[int] = None, newcardonly: bool = False): pass # Nothing to do really ... - def _send_apdu_raw(self, pdu): + def _send_apdu_raw(self, pdu: Hexstr) -> Tuple[Hexstr, SwHexstr]: # Make sure pdu has upper case hex digits [A-F] pdu = pdu.upper() diff --git a/pySim/transport/pcsc.py b/pySim/transport/pcsc.py index e3f2546..f0b3e67 100644 --- a/pySim/transport/pcsc.py +++ b/pySim/transport/pcsc.py @@ -17,6 +17,8 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # +from typing import Optional, Tuple + from smartcard.CardConnection import CardConnection from smartcard.CardRequest import CardRequest from smartcard.Exceptions import NoCardException, CardRequestTimeoutException, CardConnectionException, CardConnectionException @@ -24,7 +26,7 @@ from pySim.exceptions import NoCardError, ProtocolError, ReaderError from pySim.transport import LinkBase -from pySim.utils import h2i, i2h +from pySim.utils import h2i, i2h, Hexstr, SwHexstr class PcscSimLink(LinkBase): @@ -46,7 +48,7 @@ pass return - def wait_for_card(self, timeout: int = None, newcardonly: bool = False): + def wait_for_card(self, timeout: Optional[int] = None, newcardonly: bool = False): cr = CardRequest(readers=[self._reader], timeout=timeout, newcardonly=newcardonly) try: @@ -68,7 +70,7 @@ except NoCardException: raise NoCardError() - def get_atr(self): + def get_atr(self) -> Hexstr: return self._con.getATR() def disconnect(self): @@ -79,7 +81,7 @@ self.connect() return 1 - def _send_apdu_raw(self, pdu): + def _send_apdu_raw(self, pdu: Hexstr) -> Tuple[Hexstr, SwHexstr]: apdu = h2i(pdu) diff --git a/pySim/transport/serial.py b/pySim/transport/serial.py index 3313a5e..daf2eb8 100644 --- a/pySim/transport/serial.py +++ b/pySim/transport/serial.py @@ -19,10 +19,11 @@ import serial import time import os.path +from typing import Optional, Tuple from pySim.exceptions import NoCardError, ProtocolError from pySim.transport import LinkBase -from pySim.utils import h2b, b2h +from pySim.utils import h2b, b2h, Hexstr, SwHexstr class SerialSimLink(LinkBase): @@ -51,7 +52,7 @@ if (hasattr(self, "_sl")): self._sl.close() - def wait_for_card(self, timeout=None, newcardonly=False): + def wait_for_card(self, timeout: Optional[int] = None, newcardonly: bool = False): # Direct try existing = False @@ -92,7 +93,7 @@ def connect(self): self.reset_card() - def get_atr(self): + def get_atr(self) -> Hexstr: return self._atr def disconnect(self): @@ -184,7 +185,7 @@ def _rx_byte(self): return self._sl.read() - def _send_apdu_raw(self, pdu): + def _send_apdu_raw(self, pdu: Hexstr) -> Tuple[Hexstr, SwHexstr]: pdu = h2b(pdu) data_len = pdu[4] # P3 diff --git a/pySim/utils.py b/pySim/utils.py index b534580..22dcda3 100644 --- a/pySim/utils.py +++ b/pySim/utils.py @@ -28,7 +28,8 @@ # just to differentiate strings of hex nibbles from everything else Hexstr = NewType('Hexstr', str) - +SwHexstr = NewType('SwHexstr', str) +SwMatchstr = NewType('SwMatchstr', str) def h2b(s: Hexstr) -> bytearray: """convert from a string of hex nibbles to a sequence of bytes""" -- To view, visit https://gerrit.osmocom.org/c/pysim/+/33657 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: pysim Gerrit-Branch: master Gerrit-Change-Id: I62e081271e3a579851a588a4ed7282017e56f852 Gerrit-Change-Number: 33657 Gerrit-PatchSet: 4 Gerrit-Owner: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: dexter <pmaier(a)sysmocom.de> Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-MessageType: merged
1 0
0 0
[S] Change in pysim[master]: ts_102_222: Remove unneeded imports
by laforge 12 Jul '23

12 Jul '23
laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/33663 ) Change subject: ts_102_222: Remove unneeded imports ...................................................................... ts_102_222: Remove unneeded imports Change-Id: I0fc54a042f03ecf707fde81a859c7dd65a7009cc --- M pySim/ts_102_222.py 1 file changed, 9 insertions(+), 3 deletions(-) Approvals: Jenkins Builder: Verified fixeria: Looks good to me, approved diff --git a/pySim/ts_102_222.py b/pySim/ts_102_222.py index f8fdddb..a4ea453 100644 --- a/pySim/ts_102_222.py +++ b/pySim/ts_102_222.py @@ -23,9 +23,6 @@ from cmd2 import CommandSet, with_default_category, with_argparser import argparse -from pySim.ts_31_102 import EF_UST_map, EF_USIM_ADF_map -from pySim.ts_31_103 import EF_IST_map, EF_ISIM_ADF_map - from pySim.exceptions import * from pySim.utils import h2b, swap_nibbles, b2h, JsonEncoder -- To view, visit https://gerrit.osmocom.org/c/pysim/+/33663 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: pysim Gerrit-Branch: master Gerrit-Change-Id: I0fc54a042f03ecf707fde81a859c7dd65a7009cc Gerrit-Change-Number: 33663 Gerrit-PatchSet: 2 Gerrit-Owner: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: dexter <pmaier(a)sysmocom.de> Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-MessageType: merged
1 0
0 0
[S] Change in pysim[master]: pySim/transport: Use newly-defined ResTuple type
by laforge 12 Jul '23

12 Jul '23
laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/33659 ) Change subject: pySim/transport: Use newly-defined ResTuple type ...................................................................... pySim/transport: Use newly-defined ResTuple type Let's use the newly-added ResTuple type annotation rather than open-coding it everywhere. Change-Id: I122589e8aec4bf66dc2e86d7602ebecb771dcb93 --- M pySim/transport/__init__.py M pySim/transport/calypso.py M pySim/transport/modem_atcmd.py M pySim/transport/pcsc.py M pySim/transport/serial.py 5 files changed, 30 insertions(+), 18 deletions(-) Approvals: fixeria: Looks good to me, but someone else must approve Jenkins Builder: Verified laforge: Looks good to me, approved diff --git a/pySim/transport/__init__.py b/pySim/transport/__init__.py index a16fdb3..c8079f6 100644 --- a/pySim/transport/__init__.py +++ b/pySim/transport/__init__.py @@ -10,12 +10,12 @@ from pySim.exceptions import * from pySim.construct import filter_dict -from pySim.utils import sw_match, b2h, h2b, i2h, Hexstr, SwHexstr, SwMatchstr +from pySim.utils import sw_match, b2h, h2b, i2h, Hexstr, SwHexstr, SwMatchstr, ResTuple from pySim.cat import ProactiveCommand, CommandDetails, DeviceIdentities, Result # # Copyright (C) 2009-2010 Sylvain Munaut <tnt(a)246tNt.com> -# Copyright (C) 2021-2022 Harald Welte <laforge(a)osmocom.org> +# Copyright (C) 2021-2023 Harald Welte <laforge(a)osmocom.org> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -68,7 +68,7 @@ self.proactive_handler = proactive_handler @abc.abstractmethod - def _send_apdu_raw(self, pdu: Hexstr) -> Tuple[Hexstr, Hexstr]: + def _send_apdu_raw(self, pdu: Hexstr) -> ResTuple: """Implementation specific method for sending the PDU.""" def set_sw_interpreter(self, interp): @@ -99,7 +99,7 @@ """Resets the card (power down/up) """ - def send_apdu_raw(self, pdu: Hexstr) -> Tuple[Hexstr, SwHexstr]: + def send_apdu_raw(self, pdu: Hexstr) -> ResTuple: """Sends an APDU with minimal processing Args: @@ -116,7 +116,7 @@ self.apdu_tracer.trace_response(pdu, sw, data) return (data, sw) - def send_apdu(self, pdu: Hexstr) -> Tuple[Hexstr, SwHexstr]: + def send_apdu(self, pdu: Hexstr) -> ResTuple: """Sends an APDU and auto fetch response data Args: @@ -145,7 +145,7 @@ return data, sw - def send_apdu_checksw(self, pdu: Hexstr, sw: SwMatchstr = "9000") -> Tuple[Hexstr, SwHexstr]: + def send_apdu_checksw(self, pdu: Hexstr, sw: SwMatchstr = "9000") -> ResTuple: """Sends an APDU and check returned SW Args: diff --git a/pySim/transport/calypso.py b/pySim/transport/calypso.py index 4244b23..34fc646 100644 --- a/pySim/transport/calypso.py +++ b/pySim/transport/calypso.py @@ -21,11 +21,11 @@ import socket import os -from typing import Optional, Tuple +from typing import Optional from pySim.transport import LinkBase from pySim.exceptions import * -from pySim.utils import h2b, b2h, Hexstr, SwHexstr +from pySim.utils import h2b, b2h, Hexstr, ResTuple class L1CTLMessage: @@ -123,7 +123,7 @@ def wait_for_card(self, timeout: Optional[int] = None, newcardonly: bool = False): pass # Nothing to do really ... - def _send_apdu_raw(self, pdu: Hexstr) -> Tuple[Hexstr, SwHexstr]: + def _send_apdu_raw(self, pdu: Hexstr) -> ResTuple: # Request FULL reset req_msg = L1CTLMessageSIM(h2b(pdu)) diff --git a/pySim/transport/modem_atcmd.py b/pySim/transport/modem_atcmd.py index c6f6c57..e99762d 100644 --- a/pySim/transport/modem_atcmd.py +++ b/pySim/transport/modem_atcmd.py @@ -20,9 +20,9 @@ import serial import time import re -from typing import Optional, Tuple +from typing import Optional -from pySim.utils import Hexstr, SwHexstr +from pySim.utils import Hexstr, ResTuple from pySim.transport import LinkBase from pySim.exceptions import * @@ -141,7 +141,7 @@ def wait_for_card(self, timeout: Optional[int] = None, newcardonly: bool = False): pass # Nothing to do really ... - def _send_apdu_raw(self, pdu: Hexstr) -> Tuple[Hexstr, SwHexstr]: + def _send_apdu_raw(self, pdu: Hexstr) -> ResTuple: # Make sure pdu has upper case hex digits [A-F] pdu = pdu.upper() diff --git a/pySim/transport/pcsc.py b/pySim/transport/pcsc.py index f0b3e67..a01917f 100644 --- a/pySim/transport/pcsc.py +++ b/pySim/transport/pcsc.py @@ -17,7 +17,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -from typing import Optional, Tuple +from typing import Optional from smartcard.CardConnection import CardConnection from smartcard.CardRequest import CardRequest @@ -26,7 +26,7 @@ from pySim.exceptions import NoCardError, ProtocolError, ReaderError from pySim.transport import LinkBase -from pySim.utils import h2i, i2h, Hexstr, SwHexstr +from pySim.utils import h2i, i2h, Hexstr, ResTuple class PcscSimLink(LinkBase): @@ -81,7 +81,7 @@ self.connect() return 1 - def _send_apdu_raw(self, pdu: Hexstr) -> Tuple[Hexstr, SwHexstr]: + def _send_apdu_raw(self, pdu: Hexstr) -> ResTuple: apdu = h2i(pdu) diff --git a/pySim/transport/serial.py b/pySim/transport/serial.py index daf2eb8..998d1d8 100644 --- a/pySim/transport/serial.py +++ b/pySim/transport/serial.py @@ -19,11 +19,11 @@ import serial import time import os.path -from typing import Optional, Tuple +from typing import Optional from pySim.exceptions import NoCardError, ProtocolError from pySim.transport import LinkBase -from pySim.utils import h2b, b2h, Hexstr, SwHexstr +from pySim.utils import h2b, b2h, Hexstr, ResTuple class SerialSimLink(LinkBase): @@ -185,7 +185,7 @@ def _rx_byte(self): return self._sl.read() - def _send_apdu_raw(self, pdu: Hexstr) -> Tuple[Hexstr, SwHexstr]: + def _send_apdu_raw(self, pdu: Hexstr) -> ResTuple: pdu = h2b(pdu) data_len = pdu[4] # P3 -- To view, visit https://gerrit.osmocom.org/c/pysim/+/33659 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: pysim Gerrit-Branch: master Gerrit-Change-Id: I122589e8aec4bf66dc2e86d7602ebecb771dcb93 Gerrit-Change-Number: 33659 Gerrit-PatchSet: 4 Gerrit-Owner: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: dexter <pmaier(a)sysmocom.de> Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-MessageType: merged
1 0
0 0
[M] Change in pysim[master]: pySim/commands.py: Better type annotations
by laforge 12 Jul '23

12 Jul '23
laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/33658 ) Change subject: pySim/commands.py: Better type annotations ...................................................................... pySim/commands.py: Better type annotations Change-Id: I68081b5472188f80a964ca48d5ec1f03adc70c4a --- M pySim/commands.py M pySim/utils.py 2 files changed, 64 insertions(+), 45 deletions(-) Approvals: Jenkins Builder: Verified fixeria: Looks good to me, but someone else must approve laforge: Looks good to me, approved diff --git a/pySim/commands.py b/pySim/commands.py index 3c6ae49..ab66392 100644 --- a/pySim/commands.py +++ b/pySim/commands.py @@ -5,7 +5,7 @@ # # Copyright (C) 2009-2010 Sylvain Munaut <tnt(a)246tNt.com> -# Copyright (C) 2010-2021 Harald Welte <laforge(a)gnumonks.org> +# Copyright (C) 2010-2023 Harald Welte <laforge(a)gnumonks.org> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,20 +21,27 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # +from typing import List, Optional, Tuple +import typing # construct also has a Union, so we do typing.Union below + from construct import * from pySim.construct import LV from pySim.utils import rpad, b2h, h2b, sw_match, bertlv_encode_len, Hexstr, h2i, str_sanitize, expand_hex +from pySim.utils import Hexstr, SwHexstr, ResTuple from pySim.exceptions import SwMatchError +from pySim.transport import LinkBase +# A path can be either just a FID or a list of FID +Path = typing.Union[Hexstr, List[Hexstr]] class SimCardCommands: - def __init__(self, transport): + def __init__(self, transport: LinkBase): self._tp = transport self.cla_byte = "a0" self.sel_ctrl = "0000" # Extract a single FCP item from TLV - def __parse_fcp(self, fcp): + def __parse_fcp(self, fcp: Hexstr): # see also: ETSI TS 102 221, chapter 11.1.1.3.1 Response for MF, # DF or ADF from pytlv.TLV import TLV @@ -88,11 +95,11 @@ else: return int(r[-1][4:8], 16) - def get_atr(self) -> str: + def get_atr(self) -> Hexstr: """Return the ATR of the currently inserted card.""" return self._tp.get_atr() - def try_select_path(self, dir_list): + def try_select_path(self, dir_list: List[Hexstr]) -> List[ResTuple]: """ Try to select a specified path Args: @@ -110,7 +117,7 @@ return rv return rv - def select_path(self, dir_list): + def select_path(self, dir_list: Path) -> List[Hexstr]: """Execute SELECT for an entire list/path of FIDs. Args: @@ -127,7 +134,7 @@ rv.append(data) return rv - def select_file(self, fid: str): + def select_file(self, fid: Hexstr) -> ResTuple: """Execute SELECT a given file by FID. Args: @@ -136,11 +143,11 @@ return self._tp.send_apdu_checksw(self.cla_byte + "a4" + self.sel_ctrl + "02" + fid) - def select_parent_df(self): + def select_parent_df(self) -> ResTuple: """Execute SELECT to switch to the parent DF """ return self._tp.send_apdu_checksw(self.cla_byte + "a4030400") - def select_adf(self, aid: str): + def select_adf(self, aid: Hexstr) -> ResTuple: """Execute SELECT a given Applicaiton ADF. Args: @@ -150,7 +157,7 @@ aidlen = ("0" + format(len(aid) // 2, 'x'))[-2:] return self._tp.send_apdu_checksw(self.cla_byte + "a4" + "0404" + aidlen + aid) - def read_binary(self, ef, length: int = None, offset: int = 0): + def read_binary(self, ef: Path, length: int = None, offset: int = 0) -> ResTuple: """Execute READD BINARY. Args: @@ -181,7 +188,8 @@ chunk_offset += chunk_len return total_data, sw - def update_binary(self, ef, data: str, offset: int = 0, verify: bool = False, conserve: bool = False): + def update_binary(self, ef: Path, data: Hexstr, offset: int = 0, verify: bool = False, + conserve: bool = False) -> ResTuple: """Execute UPDATE BINARY. Args: @@ -235,7 +243,7 @@ raise ValueError('Binary verification failed (expected %s, got %s)' % ( data.lower(), res[0].lower())) - def read_record(self, ef, rec_no: int): + def read_record(self, ef: Path, rec_no: int) -> ResTuple: """Execute READ RECORD. Args: @@ -247,8 +255,8 @@ pdu = self.cla_byte + 'b2%02x04%02x' % (rec_no, rec_length) return self._tp.send_apdu_checksw(pdu) - def update_record(self, ef, rec_no: int, data: str, force_len: bool = False, verify: bool = False, - conserve: bool = False): + def update_record(self, ef: Path, rec_no: int, data: Hexstr, force_len: bool = False, + verify: bool = False, conserve: bool = False) -> ResTuple: """Execute UPDATE RECORD. Args: @@ -289,7 +297,7 @@ self.verify_record(ef, rec_no, data) return res - def verify_record(self, ef, rec_no: int, data: str): + def verify_record(self, ef: Path, rec_no: int, data: str): """Verify record against given data Args: @@ -302,7 +310,7 @@ raise ValueError('Record verification failed (expected %s, got %s)' % ( data.lower(), res[0].lower())) - def record_size(self, ef): + def record_size(self, ef: Path) -> int: """Determine the record size of given file. Args: @@ -311,7 +319,7 @@ r = self.select_path(ef) return self.__record_len(r) - def record_count(self, ef): + def record_count(self, ef: Path) -> int: """Determine the number of records in given file. Args: @@ -320,7 +328,7 @@ r = self.select_path(ef) return self.__len(r) // self.__record_len(r) - def binary_size(self, ef): + def binary_size(self, ef: Path) -> int: """Determine the size of given transparent file. Args: @@ -330,14 +338,14 @@ return self.__len(r) # TS 102 221 Section 11.3.1 low-level helper - def _retrieve_data(self, tag: int, first: bool = True): + def _retrieve_data(self, tag: int, first: bool = True) -> ResTuple: if first: pdu = '80cb008001%02x' % (tag) else: pdu = '80cb000000' return self._tp.send_apdu_checksw(pdu) - def retrieve_data(self, ef, tag: int): + def retrieve_data(self, ef: Path, tag: int) -> ResTuple: """Execute RETRIEVE DATA, see also TS 102 221 Section 11.3.1. Args @@ -357,7 +365,7 @@ return total_data, sw # TS 102 221 Section 11.3.2 low-level helper - def _set_data(self, data: str, first: bool = True): + def _set_data(self, data: Hexstr, first: bool = True) -> ResTuple: if first: p1 = 0x80 else: @@ -367,7 +375,7 @@ pdu = '80db00%02x%02x%s' % (p1, len(data)//2, data) return self._tp.send_apdu_checksw(pdu) - def set_data(self, ef, tag: int, value: str, verify: bool = False, conserve: bool = False): + def set_data(self, ef, tag: int, value: str, verify: bool = False, conserve: bool = False) -> ResTuple: """Execute SET DATA. Args @@ -398,7 +406,7 @@ remaining = remaining[255:] return rdata, sw - def run_gsm(self, rand: str): + def run_gsm(self, rand: Hexstr) -> ResTuple: """Execute RUN GSM ALGORITHM. Args: @@ -409,7 +417,7 @@ self.select_path(['3f00', '7f20']) return self._tp.send_apdu_checksw('a0' + '88000010' + rand, sw='9000') - def authenticate(self, rand: str, autn: str, context='3g'): + def authenticate(self, rand: Hexstr, autn: Hexstr, context: str = '3g') -> ResTuple: """Execute AUTHENTICATE (USIM/ISIM). Args: @@ -437,15 +445,15 @@ ret = {'successful_3g_authentication': data} return (ret, sw) - def status(self): + def status(self) -> ResTuple: """Execute a STATUS command as per TS 102 221 Section 11.1.2.""" return self._tp.send_apdu_checksw('80F20000ff') - def deactivate_file(self): + def deactivate_file(self) -> ResTuple: """Execute DECATIVATE FILE command as per TS 102 221 Section 11.1.14.""" return self._tp.send_apdu_constr_checksw(self.cla_byte, '04', '00', '00', None, None, None) - def activate_file(self, fid): + def activate_file(self, fid: Hexstr) -> ResTuple: """Execute ACTIVATE FILE command as per TS 102 221 Section 11.1.15. Args: @@ -453,31 +461,31 @@ """ return self._tp.send_apdu_checksw(self.cla_byte + '44000002' + fid) - def create_file(self, payload: Hexstr): + def create_file(self, payload: Hexstr) -> ResTuple: """Execute CREEATE FILE command as per TS 102 222 Section 6.3""" return self._tp.send_apdu_checksw(self.cla_byte + 'e00000%02x%s' % (len(payload)//2, payload)) - def resize_file(self, payload: Hexstr): + def resize_file(self, payload: Hexstr) -> ResTuple: """Execute RESIZE FILE command as per TS 102 222 Section 6.10""" return self._tp.send_apdu_checksw('80d40000%02x%s' % (len(payload)//2, payload)) - def delete_file(self, fid): + def delete_file(self, fid: Hexstr) -> ResTuple: """Execute DELETE FILE command as per TS 102 222 Section 6.4""" return self._tp.send_apdu_checksw(self.cla_byte + 'e4000002' + fid) - def terminate_df(self, fid): + def terminate_df(self, fid: Hexstr) -> ResTuple: """Execute TERMINATE DF command as per TS 102 222 Section 6.7""" return self._tp.send_apdu_checksw(self.cla_byte + 'e6000002' + fid) - def terminate_ef(self, fid): + def terminate_ef(self, fid: Hexstr) -> ResTuple: """Execute TERMINATE EF command as per TS 102 222 Section 6.8""" return self._tp.send_apdu_checksw(self.cla_byte + 'e8000002' + fid) - def terminate_card_usage(self): + def terminate_card_usage(self) -> ResTuple: """Execute TERMINATE CARD USAGE command as per TS 102 222 Section 6.9""" return self._tp.send_apdu_checksw(self.cla_byte + 'fe000000') - def manage_channel(self, mode='open', lchan_nr=0): + def manage_channel(self, mode: str = 'open', lchan_nr: int =0) -> ResTuple: """Execute MANAGE CHANNEL command as per TS 102 221 Section 11.1.17. Args: @@ -491,18 +499,18 @@ pdu = self.cla_byte + '70%02x%02x00' % (p1, lchan_nr) return self._tp.send_apdu_checksw(pdu) - def reset_card(self): + def reset_card(self) -> Hexstr: """Physically reset the card""" return self._tp.reset_card() - def _chv_process_sw(self, op_name, chv_no, pin_code, sw): + def _chv_process_sw(self, op_name: str, chv_no: int, pin_code: Hexstr, sw: SwHexstr): if sw_match(sw, '63cx'): raise RuntimeError('Failed to %s chv_no 0x%02X with code 0x%s, %i tries left.' % (op_name, chv_no, b2h(pin_code).upper(), int(sw[3]))) elif (sw != '9000'): raise SwMatchError(sw, '9000') - def verify_chv(self, chv_no: int, code: str): + def verify_chv(self, chv_no: int, code: Hexstr) -> ResTuple: """Verify a given CHV (Card Holder Verification == PIN) Args: @@ -529,7 +537,7 @@ self._chv_process_sw('unblock', chv_no, pin_code, sw) return (data, sw) - def change_chv(self, chv_no: int, pin_code: str, new_pin_code: str): + def change_chv(self, chv_no: int, pin_code: Hexstr, new_pin_code: Hexstr) -> ResTuple: """Change a given CHV (Card Holder Verification == PIN) Args: @@ -543,7 +551,7 @@ self._chv_process_sw('change', chv_no, pin_code, sw) return (data, sw) - def disable_chv(self, chv_no: int, pin_code: str): + def disable_chv(self, chv_no: int, pin_code: Hexstr) -> ResTuple: """Disable a given CHV (Card Holder Verification == PIN) Args: @@ -557,7 +565,7 @@ self._chv_process_sw('disable', chv_no, pin_code, sw) return (data, sw) - def enable_chv(self, chv_no: int, pin_code: str): + def enable_chv(self, chv_no: int, pin_code: Hexstr) -> ResTuple: """Enable a given CHV (Card Holder Verification == PIN) Args: @@ -570,7 +578,7 @@ self._chv_process_sw('enable', chv_no, pin_code, sw) return (data, sw) - def envelope(self, payload: str): + def envelope(self, payload: Hexstr) -> ResTuple: """Send one ENVELOPE command to the SIM Args: @@ -578,7 +586,7 @@ """ return self._tp.send_apdu_checksw('80c20000%02x%s' % (len(payload)//2, payload)) - def terminal_profile(self, payload: str): + def terminal_profile(self, payload: Hexstr) -> ResTuple: """Send TERMINAL PROFILE to card Args: @@ -589,7 +597,7 @@ return (data, sw) # ETSI TS 102 221 11.1.22 - def suspend_uicc(self, min_len_secs: int = 60, max_len_secs: int = 43200): + def suspend_uicc(self, min_len_secs: int = 60, max_len_secs: int = 43200) -> Tuple[int, Hexstr, SwHexstr]: """Send SUSPEND UICC to the card. Args: @@ -632,17 +640,18 @@ return (negotiated_duration_secs, resume_token, sw) # ETSI TS 102 221 11.1.22 - def resume_uicc(self, token: str): + def resume_uicc(self, token: Hexstr) -> ResTuple: """Send SUSPEND UICC (resume) to the card.""" if len(h2b(token)) != 8: raise ValueError("Token must be 8 bytes long") data, sw = self._tp.send_apdu_checksw('8076010008' + token) + return (data, sw) def get_data(self, tag: int, cla: int = 0x00): data, sw = self._tp.send_apdu('%02xca%04x00' % (cla, tag)) return (data, sw) # TS 31.102 Section 7.5.2 - def get_identity(self, context: int): + def get_identity(self, context: int) -> Tuple[Hexstr, SwHexstr]: data, sw = self._tp.send_apdu_checksw('807800%02x00' % (context)) return (data, sw) diff --git a/pySim/utils.py b/pySim/utils.py index 22dcda3..9defaf3 100644 --- a/pySim/utils.py +++ b/pySim/utils.py @@ -30,6 +30,7 @@ Hexstr = NewType('Hexstr', str) SwHexstr = NewType('SwHexstr', str) SwMatchstr = NewType('SwMatchstr', str) +ResTuple = Tuple[Hexstr, SwHexstr] def h2b(s: Hexstr) -> bytearray: """convert from a string of hex nibbles to a sequence of bytes""" -- To view, visit https://gerrit.osmocom.org/c/pysim/+/33658 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: pysim Gerrit-Branch: master Gerrit-Change-Id: I68081b5472188f80a964ca48d5ec1f03adc70c4a Gerrit-Change-Number: 33658 Gerrit-PatchSet: 4 Gerrit-Owner: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: dexter <pmaier(a)sysmocom.de> Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-MessageType: merged
1 0
0 0
[S] Change in pysim[master]: GlobalPlatform ADF.SD: Add command line reference + error message
by laforge 12 Jul '23

12 Jul '23
Attention is currently required from: dexter. laforge has posted comments on this change. ( https://gerrit.osmocom.org/c/pysim/+/33670 ) Change subject: GlobalPlatform ADF.SD: Add command line reference + error message ...................................................................... Patch Set 1: Code-Review+2 -- To view, visit https://gerrit.osmocom.org/c/pysim/+/33670 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: pysim Gerrit-Branch: master Gerrit-Change-Id: I09faaf5d45118635cf832c8c513033aede1427e5 Gerrit-Change-Number: 33670 Gerrit-PatchSet: 1 Gerrit-Owner: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: dexter <pmaier(a)sysmocom.de> Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Attention: dexter <pmaier(a)sysmocom.de> Gerrit-Comment-Date: Wed, 12 Jul 2023 16:13:14 +0000 Gerrit-HasComments: No Gerrit-Has-Labels: Yes Gerrit-MessageType: comment
1 0
0 0
[S] Change in pysim[master]: pySim/tlv.py: Fix TLV_IE_Collection from_dict with nested collections
by laforge 12 Jul '23

12 Jul '23
Attention is currently required from: lynxis lazus, dexter. laforge has posted comments on this change. ( https://gerrit.osmocom.org/c/pysim/+/33662 ) Change subject: pySim/tlv.py: Fix TLV_IE_Collection from_dict with nested collections ...................................................................... Patch Set 2: Code-Review+2 -- To view, visit https://gerrit.osmocom.org/c/pysim/+/33662 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: pysim Gerrit-Branch: master Gerrit-Change-Id: Ibdd30cf1652c864f167b1b655b49a87941e15fd5 Gerrit-Change-Number: 33662 Gerrit-PatchSet: 2 Gerrit-Owner: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: dexter <pmaier(a)sysmocom.de> Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: lynxis lazus <lynxis(a)fe80.eu> Gerrit-Attention: lynxis lazus <lynxis(a)fe80.eu> Gerrit-Attention: dexter <pmaier(a)sysmocom.de> Gerrit-Comment-Date: Wed, 12 Jul 2023 16:12:46 +0000 Gerrit-HasComments: No Gerrit-Has-Labels: Yes Gerrit-MessageType: comment
1 0
0 0
[S] Change in pysim[master]: pySim/transport: Use newly-defined ResTuple type
by laforge 12 Jul '23

12 Jul '23
Attention is currently required from: dexter. laforge has posted comments on this change. ( https://gerrit.osmocom.org/c/pysim/+/33659 ) Change subject: pySim/transport: Use newly-defined ResTuple type ...................................................................... Patch Set 4: Code-Review+2 -- To view, visit https://gerrit.osmocom.org/c/pysim/+/33659 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: pysim Gerrit-Branch: master Gerrit-Change-Id: I122589e8aec4bf66dc2e86d7602ebecb771dcb93 Gerrit-Change-Number: 33659 Gerrit-PatchSet: 4 Gerrit-Owner: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: dexter <pmaier(a)sysmocom.de> Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Attention: dexter <pmaier(a)sysmocom.de> Gerrit-Comment-Date: Wed, 12 Jul 2023 16:12:34 +0000 Gerrit-HasComments: No Gerrit-Has-Labels: Yes Gerrit-MessageType: comment
1 0
0 0
  • ← Newer
  • 1
  • ...
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • ...
  • 306
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.