blob: 15c9e5f648f0f541b77b885b4b4cee4311edb1cd [file] [log] [blame]
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +07001# -*- coding: utf-8 -*-
2
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +07003# Copyright (C) 2018 Vadim Yanitskiy <axilirator@gmail.com>
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070019import select
20import struct
21import socket
22import os
23
24from pySim.transport import LinkBase
25from pySim.exceptions import *
26from pySim.utils import h2b, b2h
27
Harald Weltec91085e2022-02-10 18:05:45 +010028
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070029class L1CTLMessage(object):
30
Harald Weltec91085e2022-02-10 18:05:45 +010031 # Every (encoded) L1CTL message has the following structure:
32 # - msg_length (2 bytes, net order)
33 # - l1ctl_hdr (packed structure)
34 # - msg_type
35 # - flags
36 # - padding (2 spare bytes)
37 # - ... payload ...
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070038
Harald Weltec91085e2022-02-10 18:05:45 +010039 def __init__(self, msg_type, flags=0x00):
40 # Init L1CTL message header
41 self.data = struct.pack("BBxx", msg_type, flags)
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070042
Harald Weltec91085e2022-02-10 18:05:45 +010043 def gen_msg(self):
44 return struct.pack("!H", len(self.data)) + self.data
45
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070046
47class L1CTLMessageReset(L1CTLMessage):
48
Harald Weltec91085e2022-02-10 18:05:45 +010049 # L1CTL message types
50 L1CTL_RESET_REQ = 0x0d
51 L1CTL_RESET_IND = 0x07
52 L1CTL_RESET_CONF = 0x0e
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070053
Harald Weltec91085e2022-02-10 18:05:45 +010054 # Reset types
55 L1CTL_RES_T_BOOT = 0x00
56 L1CTL_RES_T_FULL = 0x01
57 L1CTL_RES_T_SCHED = 0x02
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070058
Harald Weltec91085e2022-02-10 18:05:45 +010059 def __init__(self, type=L1CTL_RES_T_FULL):
60 super(L1CTLMessageReset, self).__init__(self.L1CTL_RESET_REQ)
61 self.data += struct.pack("Bxxx", type)
62
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070063
64class L1CTLMessageSIM(L1CTLMessage):
65
Harald Weltec91085e2022-02-10 18:05:45 +010066 # SIM related message types
67 L1CTL_SIM_REQ = 0x16
68 L1CTL_SIM_CONF = 0x17
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070069
Harald Weltec91085e2022-02-10 18:05:45 +010070 def __init__(self, pdu):
71 super(L1CTLMessageSIM, self).__init__(self.L1CTL_SIM_REQ)
72 self.data += pdu
73
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070074
75class CalypsoSimLink(LinkBase):
Harald Weltec91085e2022-02-10 18:05:45 +010076 """Transport Link for Calypso based phones."""
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070077
Harald Weltec91085e2022-02-10 18:05:45 +010078 def __init__(self, sock_path: str = "/tmp/osmocom_l2", **kwargs):
79 super().__init__(**kwargs)
80 # Make sure that a given socket path exists
81 if not os.path.exists(sock_path):
82 raise ReaderError(
83 "There is no such ('%s') UNIX socket" % sock_path)
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070084
Harald Weltec91085e2022-02-10 18:05:45 +010085 print("Connecting to osmocon at '%s'..." % sock_path)
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070086
Harald Weltec91085e2022-02-10 18:05:45 +010087 # Establish a client connection
88 self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
89 self.sock.connect(sock_path)
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070090
Harald Weltec91085e2022-02-10 18:05:45 +010091 def __del__(self):
92 self.sock.close()
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070093
Harald Weltec91085e2022-02-10 18:05:45 +010094 def wait_for_rsp(self, exp_len=128):
95 # Wait for incoming data (timeout is 3 seconds)
96 s, _, _ = select.select([self.sock], [], [], 3.0)
97 if not s:
98 raise ReaderError("Timeout waiting for card response")
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070099
Harald Weltec91085e2022-02-10 18:05:45 +0100100 # Receive expected amount of bytes from osmocon
101 rsp = self.sock.recv(exp_len)
102 return rsp
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700103
Harald Weltec91085e2022-02-10 18:05:45 +0100104 def reset_card(self):
105 # Request FULL reset
106 req_msg = L1CTLMessageReset()
107 self.sock.send(req_msg.gen_msg())
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700108
Harald Weltec91085e2022-02-10 18:05:45 +0100109 # Wait for confirmation
110 rsp = self.wait_for_rsp()
111 rsp_msg = struct.unpack_from("!HB", rsp)
112 if rsp_msg[1] != L1CTLMessageReset.L1CTL_RESET_CONF:
113 raise ReaderError("Failed to reset Calypso PHY")
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700114
Harald Weltec91085e2022-02-10 18:05:45 +0100115 def connect(self):
116 self.reset_card()
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700117
Harald Weltec91085e2022-02-10 18:05:45 +0100118 def disconnect(self):
119 pass # Nothing to do really ...
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700120
Harald Weltec91085e2022-02-10 18:05:45 +0100121 def wait_for_card(self, timeout=None, newcardonly=False):
122 pass # Nothing to do really ...
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700123
Harald Weltec91085e2022-02-10 18:05:45 +0100124 def _send_apdu_raw(self, pdu):
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700125
Harald Weltec91085e2022-02-10 18:05:45 +0100126 # Request FULL reset
127 req_msg = L1CTLMessageSIM(h2b(pdu))
128 self.sock.send(req_msg.gen_msg())
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700129
Harald Weltec91085e2022-02-10 18:05:45 +0100130 # Read message length first
131 rsp = self.wait_for_rsp(struct.calcsize("!H"))
132 msg_len = struct.unpack_from("!H", rsp)[0]
133 if msg_len < struct.calcsize("BBxx"):
134 raise ReaderError("Missing L1CTL header for L1CTL_SIM_CONF")
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700135
Harald Weltec91085e2022-02-10 18:05:45 +0100136 # Read the whole message then
137 rsp = self.sock.recv(msg_len)
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700138
Harald Weltec91085e2022-02-10 18:05:45 +0100139 # Verify L1CTL header
140 hdr = struct.unpack_from("BBxx", rsp)
141 if hdr[0] != L1CTLMessageSIM.L1CTL_SIM_CONF:
142 raise ReaderError("Unexpected L1CTL message received")
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700143
Harald Weltec91085e2022-02-10 18:05:45 +0100144 # Verify the payload length
145 offset = struct.calcsize("BBxx")
146 if len(rsp) <= offset:
147 raise ProtocolError("Empty response from SIM?!?")
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700148
Harald Weltec91085e2022-02-10 18:05:45 +0100149 # Omit L1CTL header
150 rsp = rsp[offset:]
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700151
Harald Weltec91085e2022-02-10 18:05:45 +0100152 # Unpack data and SW
153 data = rsp[:-2]
154 sw = rsp[-2:]
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700155
Harald Weltec91085e2022-02-10 18:05:45 +0100156 return b2h(data), b2h(sw)