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