blob: 7f99d21dcfde6e7084c8b47c809a91b154f9650e [file] [log] [blame]
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +07001# -*- coding: utf-8 -*-
2
3""" pySim: Transport Link for Calypso bases phones
4"""
5
6#
7# Copyright (C) 2018 Vadim Yanitskiy <axilirator@gmail.com>
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070023import select
24import struct
25import socket
26import os
27
28from pySim.transport import LinkBase
29from pySim.exceptions import *
30from pySim.utils import h2b, b2h
31
32class L1CTLMessage(object):
33
34 # Every (encoded) L1CTL message has the following structure:
35 # - msg_length (2 bytes, net order)
36 # - l1ctl_hdr (packed structure)
37 # - msg_type
38 # - flags
39 # - padding (2 spare bytes)
40 # - ... payload ...
41
42 def __init__(self, msg_type, flags = 0x00):
43 # Init L1CTL message header
44 self.data = struct.pack("BBxx", msg_type, flags)
45
46 def gen_msg(self):
47 return struct.pack("!H", len(self.data)) + self.data
48
49class L1CTLMessageReset(L1CTLMessage):
50
51 # L1CTL message types
52 L1CTL_RESET_REQ = 0x0d
53 L1CTL_RESET_IND = 0x07
54 L1CTL_RESET_CONF = 0x0e
55
56 # Reset types
57 L1CTL_RES_T_BOOT = 0x00
58 L1CTL_RES_T_FULL = 0x01
59 L1CTL_RES_T_SCHED = 0x02
60
61 def __init__(self, type = L1CTL_RES_T_FULL):
62 super(L1CTLMessageReset, self).__init__(self.L1CTL_RESET_REQ)
63 self.data += struct.pack("Bxxx", type)
64
65class L1CTLMessageSIM(L1CTLMessage):
66
67 # SIM related message types
68 L1CTL_SIM_REQ = 0x16
69 L1CTL_SIM_CONF = 0x17
70
71 def __init__(self, pdu):
72 super(L1CTLMessageSIM, self).__init__(self.L1CTL_SIM_REQ)
73 self.data += pdu
74
75class CalypsoSimLink(LinkBase):
76
77 def __init__(self, sock_path = "/tmp/osmocom_l2"):
78 # Make sure that a given socket path exists
79 if not os.path.exists(sock_path):
80 raise ReaderError("There is no such ('%s') UNIX socket" % sock_path)
81
82 print("Connecting to osmocon at '%s'..." % sock_path)
83
84 # Establish a client connection
85 self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
86 self.sock.connect(sock_path)
87
88 def __del__(self):
89 self.sock.close()
90
91 def wait_for_rsp(self, exp_len = 128):
92 # Wait for incoming data (timeout is 3 seconds)
93 s, _, _ = select.select([self.sock], [], [], 3.0)
94 if not s:
95 raise ReaderError("Timeout waiting for card response")
96
97 # Receive expected amount of bytes from osmocon
98 rsp = self.sock.recv(exp_len)
99 return rsp
100
101 def reset_card(self):
102 # Request FULL reset
103 req_msg = L1CTLMessageReset()
104 self.sock.send(req_msg.gen_msg())
105
106 # Wait for confirmation
107 rsp = self.wait_for_rsp()
108 rsp_msg = struct.unpack_from("!HB", rsp)
109 if rsp_msg[1] != L1CTLMessageReset.L1CTL_RESET_CONF:
110 raise ReaderError("Failed to reset Calypso PHY")
111
112 def connect(self):
113 self.reset_card()
114
115 def disconnect(self):
116 pass # Nothing to do really ...
117
118 def wait_for_card(self, timeout = None, newcardonly = False):
119 pass # Nothing to do really ...
120
121 def send_apdu_raw(self, pdu):
122 """see LinkBase.send_apdu_raw"""
123
124 # Request FULL reset
125 req_msg = L1CTLMessageSIM(h2b(pdu))
126 self.sock.send(req_msg.gen_msg())
127
128 # Read message length first
129 rsp = self.wait_for_rsp(struct.calcsize("!H"))
130 msg_len = struct.unpack_from("!H", rsp)[0]
131 if msg_len < struct.calcsize("BBxx"):
132 raise ReaderError("Missing L1CTL header for L1CTL_SIM_CONF")
133
134 # Read the whole message then
135 rsp = self.sock.recv(msg_len)
136
137 # Verify L1CTL header
138 hdr = struct.unpack_from("BBxx", rsp)
139 if hdr[0] != L1CTLMessageSIM.L1CTL_SIM_CONF:
140 raise ReaderError("Unexpected L1CTL message received")
141
142 # Verify the payload length
143 offset = struct.calcsize("BBxx")
144 if len(rsp) <= offset:
145 raise ProtocolError("Empty response from SIM?!?")
146
147 # Omit L1CTL header
148 rsp = rsp[offset:]
149
150 # Unpack data and SW
151 data = rsp[:-2]
152 sw = rsp[-2:]
153
154 return b2h(data), b2h(sw)