-
Notifications
You must be signed in to change notification settings - Fork 13
/
fingerpass.py
executable file
·204 lines (164 loc) · 6.95 KB
/
fingerpass.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
#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-
import utils, cards, TLV_utils, sys, binascii, time, traceback, re, readers
def fingerprint_rfid(card):
# Need RFID
if not isinstance(card, cards.rfid_card.RFID_Card):
return []
uid = card.get_uid()
return ["%02X" % ord(uid[0])]
# FIXME: Determine ISO type and then return a value depending on A-fixed UID vs. A-random UID vs. B
def fingerprint_7816(card):
# Need ISO 7816-4
if not isinstance(card, cards.iso_7816_4_card.ISO_7816_4_Card):
return []
# Try a select MF, just in case ...
try:
card.change_dir()
except (SystemExit, KeyboardInterrupt):
raise
except:
traceback.print_exc()
SHORT_SW_MAP = {
"\x90\x00": 0,
"\x69\x82": 1, # Security status not satisfied
"\x6a\x82": 2, # File not found
None: 3,
}
SHORT_SW_WIDTH = 2
def detect_bac(card):
"Check whether BAC is active and if yes what type of card-os (select not allowed, select allowed but read not allowed)"
result = card.open_file("\x01\x01", 0x0c) # EF.DG1
if result.sw == "\x90\x00":
prefix = str(SHORT_SW_MAP[result.sw])
result = card.send_apdu(utils.C_APDU(card.APDU_READ_BINARY, p1=0, p2=0, le=1))
else:
prefix = ""
if SHORT_SW_MAP.has_key(result.sw):
return prefix + str(SHORT_SW_MAP[result.sw])
else:
return prefix + "%s:%s" % (SHORT_SW_MAP[None], binascii.b2a_hex(result.sw) )
def map_dg(card):
"Get a map of which DGs exist and are readable/unreadable and with which SW they are unreadable"
# Try to read 1 byte from each DG through READ BINARY with short file identifier
responses = [card.send_apdu(utils.C_APDU(card.APDU_READ_BINARY, p1=i|0x80, p2=0, le=1)) for i in range(1,17)]
result = []
exceptional = []
for response in responses:
if SHORT_SW_MAP.has_key( response.sw ):
result.append( SHORT_SW_MAP[response.sw] )
else:
result.append( SHORT_SW_MAP[None] )
exceptional.append(response.sw)
UNIT_FORMAT = "%X"
UNIT_LEN = 4 # For hex in "%X" format. Would be 8 for hex in "%02X" format.
compressed = []
current = 0
count = 0
for r in result:
if count >= UNIT_LEN:
compressed.append( current )
current = count = 0
current = (current << SHORT_SW_WIDTH) | r
count = count + SHORT_SW_WIDTH
if count > 0:
if not count >= UNIT_LEN:
while count < UNIT_LEN:
current = current << SHORT_SW_WIDTH
count += SHORT_SW_WIDTH
compressed.append( current )
current = count = 0
return "".join( [UNIT_FORMAT % r for r in compressed] ) + ":".join( (len(exceptional) > 0 and [""] or []) + [binascii.b2a_hex(e) for e in exceptional] )
result = []
postfix = ""
test_icao = card.select_application(card.resolve_symbolic_aid("mrtd"), le=None)
if test_icao.sw == "\x67\x00":
postfix = ":6700" # SELECT APPLICATION with P2=0 and without Le returns 6700 Wrong Length
test_icao = card.select_application(card.resolve_symbolic_aid("mrtd"), le=None, P2=0x0c)
if not card.check_sw(test_icao.sw, card.PURPOSE_SUCCESS):
result.append("N"+postfix) # Not an ICAO MRTD
else:
result.append("P"+postfix) # An ICAO MRTD
bac = detect_bac(card)
result.append(bac) # BAC status
dgmap = map_dg(card)
result.append(dgmap) # Data Group map
return result
def fingerprint(card):
def compress_atr(atr):
numhist = ord(atr[1]) & 0x0f
if binascii.a2b_hex( "3B8%X8001" % numhist ) == atr[:4]:
# Contactless, conforming to PC/SC part 3 section 3.1.3.2.3
if atr[4:6] == "\x80\x4f": # Status indicator in compact-tlv object
si_len = ord(atr[6])
aid = atr[7:7+si_len]
if aid[:5] == "\xa0\x00\x00\x03\x06": # RID of PC/SC Workgroup
standard_and_name = aid[5:]
if standard_and_name[3:] == "\x00" * (len(standard_and_name)-3):
return "1:%s" % binascii.b2a_hex(standard_and_name[:3]) # RFU bytes unset
else:
return "2:%s" % binascii.b2a_hex(standard_and_name) # RFU bytes set
return "0:%s" % binascii.b2a_hex(atr[4:])
else:
# Not contactless (or not conforming)
return "3:%s" % binascii.b2a_hex(atr)
return ""
result = []
atr = card.get_atr()
try:
catr = compress_atr(atr)
except (KeyboardInterrupt, SystemExit):
raise
except: # Any error in the ATR processing
catr = "F:%s" % binascii.b2a_hex(atr)
result.append( catr )
result.extend( fingerprint_7816(card) )
result.extend( fingerprint_rfid(card) )
return ",".join(result)
def match_fingerprint(fingerprint, database="fingerprints.txt"):
fp = file(database, "r")
results = []
current_result = []
first_line = True
matched = False
def do_match(line, fingerprint):
return re.match(line.strip(), fingerprint.strip()) is not None
for line in fp.readlines():
if line[0] == "#":
continue
if line.strip() == "":
matched = False
if len(current_result) > 0:
results.append(current_result)
current_result = []
elif not line[0].isspace():
if do_match(line, fingerprint):
matched = True
else:
matched = False
elif matched:
current_result.append(line.strip())
if len(current_result) > 0:
results.append(current_result)
current_result = []
fp.close()
return ["\n".join(e) for e in results]
if __name__ == "__main__":
c = readers.CommandLineArgumentHelper()
(options, arguments) = c.getopt(sys.argv[1:])
card_object = c.connect()
card = cards.new_card_object(card_object)
cards.generic_card.DEBUG = False
print >>sys.stderr, "Using %s" % card.DRIVER_NAME
if isinstance(card, cards.rfid_card.RFID_Card):
print "UID: %s" % utils.hexdump(card.get_uid(), short=True)
fp = fingerprint(card)
print "Fingerprint: %s" % fp
matches = match_fingerprint(fp)
if len(matches) > 1:
print "Matched as: \n\t+ %s" % "\nor\t+ ".join( ["\n\t ".join(e.split("\n")) for e in matches] )
elif len(matches) == 1:
if len(matches[0].split("\n")) == 1:
print "Matched as: %s" % matches[0]
else:
print "Matched as: \n\t%s" % "\n\t".join( matches[0].split("\n") )