blob: b55a089a1bc2a14166c7b55c7d0b87a3ad8ce0ce [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
28class L1CTLMessage(object):
29
30 # Every (encoded) L1CTL message has the following structure:
31 # - msg_length (2 bytes, net order)
32 # - l1ctl_hdr (packed structure)
33 # - msg_type
34 # - flags
35 # - padding (2 spare bytes)
36 # - ... payload ...
37
38 def __init__(self, msg_type, flags = 0x00):
39 # Init L1CTL message header
40 self.data = struct.pack("BBxx", msg_type, flags)
41
42 def gen_msg(self):
43 return struct.pack("!H", len(self.data)) + self.data
44
45class L1CTLMessageReset(L1CTLMessage):
46
47 # L1CTL message types
48 L1CTL_RESET_REQ = 0x0d
49 L1CTL_RESET_IND = 0x07
50 L1CTL_RESET_CONF = 0x0e
51
52 # Reset types
53 L1CTL_RES_T_BOOT = 0x00
54 L1CTL_RES_T_FULL = 0x01
55 L1CTL_RES_T_SCHED = 0x02
56
57 def __init__(self, type = L1CTL_RES_T_FULL):
58 super(L1CTLMessageReset, self).__init__(self.L1CTL_RESET_REQ)
59 self.data += struct.pack("Bxxx", type)
60
61class L1CTLMessageSIM(L1CTLMessage):
62
63 # SIM related message types
64 L1CTL_SIM_REQ = 0x16
65 L1CTL_SIM_CONF = 0x17
66
67 def __init__(self, pdu):
68 super(L1CTLMessageSIM, self).__init__(self.L1CTL_SIM_REQ)
69 self.data += pdu
70
71class CalypsoSimLink(LinkBase):
Harald Welteee3501f2021-04-02 13:00:18 +020072 """Transport Link for Calypso based phones."""
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070073
Harald Welteeb05b2f2021-04-10 11:01:56 +020074 def __init__(self, sock_path:str = "/tmp/osmocom_l2", **kwargs):
75 super().__init__(**kwargs)
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070076 # Make sure that a given socket path exists
77 if not os.path.exists(sock_path):
78 raise ReaderError("There is no such ('%s') UNIX socket" % sock_path)
79
80 print("Connecting to osmocon at '%s'..." % sock_path)
81
82 # Establish a client connection
83 self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
84 self.sock.connect(sock_path)
85
86 def __del__(self):
87 self.sock.close()
88
89 def wait_for_rsp(self, exp_len = 128):
90 # Wait for incoming data (timeout is 3 seconds)
91 s, _, _ = select.select([self.sock], [], [], 3.0)
92 if not s:
93 raise ReaderError("Timeout waiting for card response")
94
95 # Receive expected amount of bytes from osmocon
96 rsp = self.sock.recv(exp_len)
97 return rsp
98
99 def reset_card(self):
100 # Request FULL reset
101 req_msg = L1CTLMessageReset()
102 self.sock.send(req_msg.gen_msg())
103
104 # Wait for confirmation
105 rsp = self.wait_for_rsp()
106 rsp_msg = struct.unpack_from("!HB", rsp)
107 if rsp_msg[1] != L1CTLMessageReset.L1CTL_RESET_CONF:
108 raise ReaderError("Failed to reset Calypso PHY")
109
110 def connect(self):
111 self.reset_card()
112
113 def disconnect(self):
114 pass # Nothing to do really ...
115
116 def wait_for_card(self, timeout = None, newcardonly = False):
117 pass # Nothing to do really ...
118
Harald Weltec34f9402021-04-10 10:55:24 +0200119 def _send_apdu_raw(self, pdu):
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700120
121 # Request FULL reset
122 req_msg = L1CTLMessageSIM(h2b(pdu))
123 self.sock.send(req_msg.gen_msg())
124
125 # Read message length first
126 rsp = self.wait_for_rsp(struct.calcsize("!H"))
127 msg_len = struct.unpack_from("!H", rsp)[0]
128 if msg_len < struct.calcsize("BBxx"):
129 raise ReaderError("Missing L1CTL header for L1CTL_SIM_CONF")
130
131 # Read the whole message then
132 rsp = self.sock.recv(msg_len)
133
134 # Verify L1CTL header
135 hdr = struct.unpack_from("BBxx", rsp)
136 if hdr[0] != L1CTLMessageSIM.L1CTL_SIM_CONF:
137 raise ReaderError("Unexpected L1CTL message received")
138
139 # Verify the payload length
140 offset = struct.calcsize("BBxx")
141 if len(rsp) <= offset:
142 raise ProtocolError("Empty response from SIM?!?")
143
144 # Omit L1CTL header
145 rsp = rsp[offset:]
146
147 # Unpack data and SW
148 data = rsp[:-2]
149 sw = rsp[-2:]
150
151 return b2h(data), b2h(sw)