iso7816 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,173 @@
1
+
2
+ require 'hexy'
3
+
4
+ module ISO7816
5
+ class ATR
6
+ # require 'atr_generated'
7
+ attr_reader :atr_bytes
8
+ # provide a string of bytes to init this object.
9
+ def initialize atr
10
+ @atr_bytes = atr
11
+ @string_rep = ATR.stringify atr
12
+ @desc = ATR.get_description atr
13
+ @ta=Hash.new
14
+ @tb=Hash.new
15
+ @tc=Hash.new
16
+ @td=Hash.new
17
+ decode
18
+ end
19
+
20
+ # ATR spec:
21
+ # TS : initial
22
+ # 3F = inverse logic
23
+ # 3B = direct logic
24
+ # T0 : format
25
+ # |7|6|5|4||3|2|1|0|
26
+ # bits 4 through 7 indicate presence of TA1, TB1, TC1, TD1 respectively
27
+ # bits 0-3 are the length of the "historical data"
28
+ #
29
+ # TA1...TD1, if present follow T0
30
+ #
31
+ # TAi, TBi, and TCi have codified sematics, a possible TDi field signals the
32
+ # existance of further TAi+1... fields.
33
+ #
34
+ # |7|6|5|4||3|2|1|0|
35
+ #
36
+ # where 4, 5 and 6 signal he presesence of TAx, TBx, TCx, TDx.
37
+ #
38
+ # finally the last bytes are "historical data" followed by a single byte checksum.
39
+ def decode
40
+ pos = 0
41
+ @ts = @atr_bytes[pos]
42
+ case @ts
43
+ when 0x3F: @ts_d="inverse"
44
+ when 0x3B: @ts_d="direct"
45
+ else @ts_d = "invalid"
46
+ end
47
+
48
+ @t0 = @atr_bytes[pos+=1]
49
+
50
+ ta, tb, tc,td, @hist_data_len = decode_TD 0, pos
51
+
52
+ i=1
53
+ @max_i = 0
54
+ while ta || tb || tc || td
55
+ @max_i = i
56
+ decodeTA i, pos+=1 if ta
57
+ decodeTB i, pos+=1 if tb
58
+ decodeTC i, pos+=1 if tc
59
+ if td
60
+ ta, tb, tc, td = decode_TD i, pos+=1
61
+ else
62
+ break
63
+ end
64
+ i+=1
65
+ end
66
+
67
+ @historical_data = @atr_bytes[pos+=1, @hist_data_len]
68
+ #puts @historical_data.size
69
+
70
+ #puts ATR.stringify(@historical_data)
71
+ end
72
+
73
+ def decodeTA i, pos
74
+ @ta ||= Hash.new
75
+ @ta[i]=@atr_bytes[pos]
76
+ end
77
+ def decodeTB i, pos
78
+ @tb ||= Hash.new
79
+ @tb[i]=@atr_bytes[pos]
80
+ end
81
+
82
+ def decodeTC i, pos
83
+ @tc ||= Hash.new
84
+ @tc[i]=@atr_bytes[pos]
85
+ end
86
+
87
+ # decodes a byte in the form:
88
+ # |7|6|5|4||3|2|1|0|
89
+ # and returns
90
+ # [TA?, B?, TC?, TD?, num]
91
+ def decode_TD i, pos
92
+ byte = @atr_bytes[pos]
93
+ @td ||= Hash.new
94
+ @td[i]=byte
95
+
96
+ [
97
+ (byte & 0x10)!=0,
98
+ (byte & 0x20)!=0,
99
+ (byte & 0x40)!=0,
100
+ (byte & 0x80)!=0,
101
+ byte & 0x0F
102
+ ]
103
+ end
104
+
105
+ def to_s
106
+ str = <<HELLO
107
+ ATR #{@string_rep}
108
+ TS (#{"%X"%@ts}) : #{@ts_d}
109
+ T0 (#{"%X"%@t0}) : #{"%08b"%@t0}
110
+
111
+ histdatalen: #{@hist_data_len}
112
+
113
+ HELLO
114
+
115
+ 1.upto(@max_i) {|i|
116
+ str << "i=#{i}\n"
117
+ str << label_i_hex_bin("TA", i, @ta[i])
118
+ str << label_i_hex_bin("TB", i, @tb[i])
119
+ str << label_i_hex_bin("TC", i, @tc[i])
120
+ str << label_i_hex_bin("TD", i, @td[i])
121
+
122
+ }
123
+
124
+ if @historical_data
125
+ str << "Historical Data:\n"
126
+ str << Hexy.new(@historical_data).to_s
127
+ end
128
+
129
+ str
130
+ end
131
+
132
+
133
+ def label_i_hex_bin label, i, byte
134
+ byte == nil ? "" : " %s%d(%02x) = %08b\n" % [label, i, byte, byte]
135
+ end
136
+
137
+ def self.stringify atr
138
+ atr.unpack("H*")[0].upcase
139
+ end
140
+ def self.get_description atr, bytes=false
141
+ atr = stringify atr if bytes
142
+ #desc = ATR_HASH[atr]
143
+ #desc ? desc : "unknown"
144
+ "unknown"
145
+ end
146
+ end
147
+ end
148
+
149
+ if $0 == __FILE__
150
+ puts ISO7816::ATR.get_description("3B660000314B01010080")
151
+ puts ISO7816::ATR.get_description("3B66000;ldskjfalsdkj")
152
+ bytes = ["3B660000314B01010080"].pack("H*")
153
+ puts ISO7816::ATR.get_description(bytes , true)
154
+ atr = ISO7816::ATR.new bytes
155
+ puts atr
156
+
157
+
158
+ bytes = ["3BFF9500FFC00A1F438031E073F62113574A334861324147D6"].pack("H*")
159
+ atr = ISO7816::ATR.new bytes
160
+ puts atr
161
+
162
+
163
+ ISO7816::ATR::ATR_HASH.keys.each{|key|
164
+ puts key
165
+ bytes= [key].pack("H*")
166
+ puts ISO7816::ATR.new(bytes).to_s
167
+ puts "--------------------------------------"
168
+
169
+ }
170
+
171
+
172
+ end
173
+
@@ -0,0 +1,278 @@
1
+ require 'smartcard'
2
+ module ISO7816
3
+ module Card
4
+
5
+ # Dummy implementation of `Card` to describe the provided interface
6
+ class Card
7
+ attr_reader :atr
8
+ # Set up a connection to this card and return the ATR String.
9
+ # May be called with a block which will be passed `self`. Card
10
+ # will call `disconnect` at the end of the block
11
+ def connect
12
+ raise "not implemented, use a subclass of `Card`!"
13
+ end
14
+
15
+ # disconnect from the card and free all resources.
16
+ def disconnect
17
+ raise "not implemented, use a subclass of `Card`!"
18
+ end
19
+
20
+ # Send bytes to the card.
21
+ def send bytes=""
22
+ raise "not implemented, use a subclass of `Card`!"
23
+ end
24
+
25
+ def reconnect
26
+ disconnect
27
+ connect
28
+ end
29
+
30
+ # receive a response from the card, optionally pass in the
31
+ # number of expected bytes in the response, this value will be 1024
32
+ # by default, which should be enough to hold most responses sent by
33
+ # a card.
34
+ def receive le=1024
35
+ raise "not implemented, use a subclass of `Card`!"
36
+ end
37
+
38
+ def t0?
39
+ raise "not implemented, use a subclass of `Card`!"
40
+ end
41
+ end #class Card
42
+
43
+ class PCSCCard < Card
44
+ def initialize()
45
+ end
46
+
47
+ def connect
48
+ begin
49
+ if @connected
50
+ #puts "!!!!!!!!!!!!!! Already Connected!"
51
+ else
52
+ @card = ISO7816::PCSC::Card.new()
53
+ end
54
+ rescue
55
+ @card.disconnect if @card
56
+ raise $!
57
+ end
58
+
59
+ stat = @card.status
60
+ @connected = true
61
+ @atr = @card.atr
62
+ @t0 = stat[:protocol] == Smartcard::PCSC::PROTOCOL_T0
63
+ if block_given?
64
+ yield self
65
+ disconnect
66
+ end
67
+ @atr
68
+ end
69
+
70
+ def reconnect
71
+ #puts "calling reconn! #{ (@card && @connected)}"
72
+ @card.reconnect if (@card && @connected)
73
+ end
74
+
75
+ def disconnect
76
+ @card.disconnect() if @card
77
+ @card = nil
78
+ @connected = false
79
+ end
80
+
81
+ def send bytes = ""
82
+ @received = @card.transmit(bytes)
83
+ end
84
+
85
+ def receive len=0
86
+ ret = @received
87
+ @received = nil
88
+ return ret
89
+ end
90
+
91
+ def t0?
92
+ return @t0
93
+ end
94
+
95
+ end
96
+
97
+ class LoggingCard < Card
98
+ attr_accessor :card
99
+ def initialize card
100
+ @card = card
101
+ @log = []
102
+ end
103
+ def atr
104
+ @card.atr
105
+ end
106
+ def connect &block
107
+ if block_given?
108
+ @card.connect
109
+ @log.push [:atr, @card.atr]
110
+ yield self
111
+ disconnect
112
+ else
113
+ @card.connect
114
+ @log.push [:atr, @card.atr]
115
+ end
116
+ end
117
+
118
+ def disconnect
119
+ @card.disconnect
120
+ @log.push [:disco, ""]
121
+ end
122
+
123
+ def send bytes
124
+ @card.send bytes
125
+ @log.push [:send, bytes]
126
+ end
127
+
128
+ def receive le=1024
129
+ recv = @card.receive(le)
130
+ @log.push [:recv, recv]
131
+ recv
132
+ end
133
+
134
+ def t0?
135
+ @card.t0?
136
+ end
137
+
138
+ def comment txt
139
+ @log.push [:comment, txt]
140
+ end
141
+
142
+ DEFAULT_DUMP = lambda {|dir, b|
143
+ prefix = case dir
144
+ when :atr then "ATR"
145
+ when :send then " >"
146
+ when :recv then " <"
147
+ when :disco then "\nCLIENT DISCONNECT"
148
+ else :comment
149
+ end
150
+ if prefix == :comment
151
+ txt = "\n"
152
+ b.each_line{|line| txt << " #{line}"}
153
+ txt << "\n\n"
154
+ else
155
+ "%s %s" % [prefix, ISO7816.b2s(b)]
156
+ end
157
+ }
158
+ def dump io=STDOUT, formater=DEFAULT_DUMP
159
+ @log.each{ |line|
160
+ format = DEFAULT_DUMP.call(line[0], line[1])
161
+ io.puts(format)
162
+ }
163
+
164
+ end
165
+
166
+
167
+ end
168
+
169
+ # This implementation of card relies on
170
+ # socket communication, it expects an atr to be
171
+ # send on TCP connect
172
+ class TCPCard < Card
173
+ SLEEP_TIME = 0.1
174
+ attr_accessor :addr, :port
175
+ def initialize addr="127.0.0.1", port=1024
176
+ @addr = addr
177
+ @port = port
178
+ end
179
+
180
+ def connect
181
+ require 'socket'
182
+ @socket = TCPSocket.new(@addr, @port)
183
+ @connected = true
184
+
185
+ sleep SLEEP_TIME
186
+ @atr = @socket.recv 1024,0
187
+ if block_given?
188
+ yield self
189
+ disconnect
190
+ end
191
+ @atr
192
+ end
193
+
194
+ def disconnect
195
+ @socket.close if @socket && @connected
196
+ @connected = false
197
+ @expect_sw=nil
198
+ true
199
+ end
200
+
201
+ def connected?
202
+ return false unless @connected
203
+ # we're still connected, check if peer is still available
204
+ true
205
+ end
206
+
207
+ def send bytes, flags=0
208
+ raise "not connected" unless @connected
209
+ bytes1 = bytes[0,5]
210
+ bytes2 = bytes[5,bytes.length]
211
+ @socket.send bytes1, flags
212
+ # TCP2 echos back the ins byte, flags
213
+ # according to ISO 7816-3 10.3.3
214
+ proc_byte = @socket.recv 1, Socket::MSG_PEEK
215
+ # recv 90 60, INS, ...
216
+ if proc_byte == "\x60" # null byte
217
+ #
218
+ elsif in_range(proc_byte, "\x61", "\x6f") || in_range(proc_byte, "\x90", "\x9f")
219
+ @expect_sw=true
220
+ else # INS being echoed back
221
+ @socket.recv 1, 0
222
+ @socket.send bytes2,flags
223
+ end
224
+ end
225
+
226
+ def in_range byte, from, to
227
+ byte >= from && byte <= to
228
+ end
229
+
230
+ def receive le=nil
231
+ raise "not connected" unless @connected
232
+
233
+ if @expect_sw
234
+ le=2
235
+ @expect_sw=nil
236
+ end
237
+
238
+ if le && Socket.const_defined?("MSG_WAITALL")
239
+ # if we know the num bytes to receive, set MSG_WAIT_ALL
240
+ flags = Socket::MSG_WAITALL
241
+ elsif le
242
+ data = ""
243
+ data << @socket.recv(le, flags) while data.length < le
244
+ return data
245
+ else
246
+ # set le to 1024 (default) and wait a little bit
247
+ le = 1024
248
+ sleep SLEEP_TIME
249
+ flags = 0
250
+ end
251
+
252
+ #puts "Waiting to receive: #{le}"
253
+ @socket.recv(le, flags)
254
+ end
255
+
256
+ def t0?
257
+ true
258
+ end
259
+
260
+ end # class TCPCard
261
+ end # module Card
262
+ end # module ISO7816
263
+
264
+
265
+
266
+
267
+
268
+ # VERY VERY basic tests... Actual testing is in ../test/card_test.rb
269
+ if $0 == __FILE__
270
+ card = ISO7816::Card::TCPCard.new "172.18.4.27", 1024
271
+ puts "connecting"
272
+ atr = card.connect
273
+ puts "connected"
274
+ puts atr.unpack("H*")[0]
275
+ puts "disconnecting"
276
+ card.disconnect
277
+ puts "disconnected"
278
+ end