-
Notifications
You must be signed in to change notification settings - Fork 11
/
iso7816.py
207 lines (180 loc) · 8.62 KB
/
iso7816.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# Copyright 2009 Jean-Francois Houzard, Olivier Roger
#
# This file is part of pypassport.
#
# pypassport is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# pypassport is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with pyPassport.
# If not, see <http://www.gnu.org/licenses/>.
from pypassport.reader import ReaderException
from pypassport.hexfunctions import *
from pypassport import apdu
from pypassport.logger import Logger
class Iso7816Exception(Exception):
def __init__(self, *params):
Exception.__init__(self, *params)
class Iso7816(Logger):
Errors = {
0x61:'SW2 indicates the number of response bytes still available',
0x62:{0x00:'No information given',\
0x81:'Part of returned data may be corrupted',\
0x82:'End of file/record reached before reading Le bytes',\
0x83:'Selected file invalidated',\
0x84:'FCI not formatted according to ISO7816-4 section 5.1.5'},
0x63:{0x00:'No information given',\
0x81:'File filled up by the last write',\
0x82:'Card Key not supported',\
0x83:'Reader Key not supported',\
0x84:'Plain transmission not supported',\
0x85:'Secured Transmission not supported',\
0x86:'Volatile memory not available',\
0x87:'Non Volatile memory not available',\
0x88:'Key number not valid',\
0x89:'Key length is not correct',\
0xC:'Counter provided by X (valued from 0 to 15) (exact meaning depending on the command)'},
0x64:'State of non-volatile memory unchanged (SW2=00, other values are RFU)',
0x65:{0x00:'No information given',\
0x81:'Memory failure'},
0x66:'Reserved for security-related issues (not defined in this part of ISO/IEC 7816)',
0x67:{0x00:'Wrong length'},
0x68:{0x00:'No information given',\
0x81:'Logical channel not supported',\
0x82:'Secure messaging not supported'},
0x69:{0x00:'No information given',\
0x81:'Command incompatible with file structure',\
0x82:'Security status not satisfied',\
0x83:'Authentication method blocked',\
0x84:'Referenced data invalidated',\
0x85:'Conditions of use not satisfied',\
0x86:'Command not allowed (no current EF)',\
0x87:'Expected SM data objects missing',\
0x88:'SM data objects incorrect'},
0x6A:{0x00:'No information given',\
0x80:'Incorrect parameters in the data field',\
0x81:'Function not supported',\
0x82:'File not found',\
0x83:'Record not found',\
0x84:'Not enough memory space in the file',\
0x85:'Lc inconsistent with TLV structure',\
0x86:'Incorrect parameters P1-P2',\
0x87:'Lc inconsistent with P1-P2',\
0x88:'Referenced data not found'},
0x6B:{0x00:'Wrong parameter(s) P1-P2'},
0x6C:'Wrong length Le: SW2 indicates the exact length',
0x6D:{0x00:'Instruction code not supported or invalid'},
0x6E:{0x00:'Class not supported'},
0x6F:{0x00:'No precise diagnosis'},
0x90:{0x00:'Success'} #No further qualification
}
def __init__(self, reader):
Logger.__init__(self, "ISO7816")
self._reader = reader
self._ciphering = False
# MODIFICATION 5/15/2012 par ANTONIN BEAUJEANT #
def rstConnection(self):
rn = self._reader.readerNum
try:
self._reader.disconnect()
self._reader.connect(rn)
self.setCiphering()
self.selectFile("04", "0C", "A0000002471001")
except Exception:
raise Iso7816Exception("An error occured while resetting the connection")
def getTypeReader(self):
return type(self._reader)
def transmitRaw(self, toSend):
try:
if self._ciphering: toSend = self._ciphering.protect(toSend)
res = self._reader.transmit(toSend)
if self._ciphering: res = self._ciphering.unprotect(res)
return res
except KeyError, k:
raise Iso7816Exception("Unknown error", res.sw1, res.sw2)
def rstConnectionRaw(self):
rn = self._reader.readerNum
try:
self._reader.disconnect()
self._reader.connect(rn)
self.setCiphering()
except Exception:
raise Iso7816Exception("An error occured while resetting the connection")
################################################
def transmit(self, toSend, logMsg):
"""
@param toSend: The command to transmit.
@type toSend: A commandAPDU object.
@param logMsg: A log message associated to the transmit.
@type logMsg: A string.
@return: The result field of the responseAPDU object
The P1 and P2 fields are checked after each transmit.
If they don't mean success, the appropriate error string is retrieved
from the Error dictionary and an APDUException is raised.
The Iso7816Exception is composed of three fields: ('error message', p1, p2)
To access these fields when the exception is raised,
access the APDUException object like a list::
try:
x.apduTransmit(commandAPDU(..))
except Iso7816Exception, exc:
print "error: " + exc[0]
print "(pw1, pw2) + str( (exc[1], exc[2]) )
"""
try:
self.log(logMsg)
self.log(str(toSend))
if self._ciphering:
toSend = self._ciphering.protect(toSend)
self.log("[SM] " + str(toSend))
res = self._reader.transmit(toSend)
if self._ciphering:
self.log("[SM] " + str(res))
res = self._ciphering.unprotect(res)
msg = Iso7816.Errors[res.sw1][res.sw2]
self.log(str(res)+" //" + msg)
if msg == "Success":
return res.res
else:
raise Iso7816Exception(msg, res.sw1, res.sw2)
except KeyError, k:
raise Iso7816Exception("Unknown error", res.sw1, res.sw2)
def setCiphering(self, c=False):
self._ciphering = c
def selectFile(self, p1, p2, file="", cla="00", ins="A4"):
lc = hexToHexRep(len(file)/2)
toSend = apdu.CommandAPDU(cla, ins, p1, p2, lc, file, "")
return self.transmit(toSend, "Select File")
def readBinary(self, offset, nbOfByte):
os = "%04x" % int(offset)
toSend = apdu.CommandAPDU("00", "B0", os[0:2], os[2:4], "", "", hexToHexRep(nbOfByte))
return self.transmit(toSend, "Read Binary")
def updateBinary(self, offset, data, cla="00", ins="D6"):
os = "%04x" % int(offset)
data = binToHexRep(data)
lc = hexToHexRep(len(data)/2)
toSend = apdu.CommandAPDU(cla, ins, os[0:2], os[2:4], lc, data, "")
return self.transmit(toSend, "Update Binary")
def getChallenge(self):
toSend = apdu.CommandAPDU("00", "84", "00", "00", "", "", "08")
return self.transmit(toSend, "Get Challenge")
def getUID(self):
toSend = apdu.CommandAPDU("FF", "CA", "00", "00", "", "", "00")
return self.transmit(toSend, "Get UID")
def internalAuthentication(self, rnd_ifd):
data = binToHexRep(rnd_ifd)
lc = hexToHexRep(len(data)/2)
toSend = apdu.CommandAPDU("00", "88", "00", "00", lc, data, "00")
res = self.transmit(toSend, "Internal Authentication")
return res
# def mutualAuthentication(self, data):
# data = binToHexRep(data)
# lc = hexToHexRep(len(data)/2)
# toSend = apdu.CommandAPDU("00", "82", "00", "00", lc, data, "28")
# return self.transmit(toSend, "Mutual Authentication")