iso7816 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,58 @@
1
+ This package is copyrighted free software by Tim Becker <tim@kuriositaet.de>.
2
+ You can redistribute it and/or modify it under either the terms of the GPL
3
+ (see COPYING.txt file), or the conditions below:
4
+
5
+ 1. You may make and give away verbatim copies of the source form of the
6
+ software without restriction, provided that you duplicate all of the
7
+ original copyright notices and associated disclaimers.
8
+
9
+ 2. You may modify your copy of the software in any way, provided that
10
+ you do at least ONE of the following:
11
+
12
+ a) place your modifications in the Public Domain or otherwise
13
+ make them Freely Available, such as by posting said
14
+ modifications to Usenet or an equivalent medium, or by allowing
15
+ the author to include your modifications in the software.
16
+
17
+ b) use the modified software only within your corporation or
18
+ organization.
19
+
20
+ c) rename any non-standard executables so the names do not conflict
21
+ with standard executables, which must also be provided.
22
+
23
+ d) make other distribution arrangements with the author.
24
+
25
+ 3. You may distribute the software in object code or executable
26
+ form, provided that you do at least ONE of the following:
27
+
28
+ a) distribute the executables and library files of the software,
29
+ together with instructions (in the manual page or equivalent)
30
+ on where to get the original distribution.
31
+
32
+ b) accompany the distribution with the machine-readable source of
33
+ the software.
34
+
35
+ c) give non-standard executables non-standard names, with
36
+ instructions on where to get the original software distribution.
37
+
38
+ d) make other distribution arrangements with the author.
39
+
40
+ 4. You may modify and include the part of the software into any other
41
+ software (possibly commercial). But some files in the distribution
42
+ are not written by the author, so that they are not under this terms.
43
+
44
+ They are gc.c(partly), utils.c(partly), regex.[ch], st.[ch] and some
45
+ files under the ./missing directory. See each file for the copying
46
+ condition.
47
+
48
+ 5. The scripts and library files supplied as input to or produced as
49
+ output from the software do not automatically fall under the
50
+ copyright of the software, but belong to whomever generated them,
51
+ and may be sold commercially, and may be aggregated with this
52
+ software.
53
+
54
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
55
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
56
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
57
+ PURPOSE.
58
+
data/README ADDED
@@ -0,0 +1,9 @@
1
+
2
+ = pcsc - ruby interface to pcsc smartcard libs
3
+
4
+ == Dependencies
5
+
6
+ * smartcard : http://github.com/costan/smartcard
7
+ * hexy : http://github.com/a2800276/hexy
8
+ * tlv : http://github.com/a2800276/tlv
9
+
@@ -0,0 +1,141 @@
1
+ require "rdoc/task"
2
+ require "rubygems/package_task"
3
+ require "rake/testtask"
4
+ require "rake/clean"
5
+ require "rubygems"
6
+
7
+ # Some definitions that you'll need to edit in case you reuse this
8
+ # Rakefile for your own project.
9
+
10
+ SHORTNAME ='iso7816' # this should be the rubyforge project name
11
+ DESC ='ruby smartcard access'
12
+ PKG_VERSION ='0.0.3'
13
+ LONG_DESC = <<END_DESC
14
+ Utilities to provided ISO 7816 smartcard functionality
15
+ END_DESC
16
+ RUBYFORGE_USER ='a2800276'
17
+
18
+ # Specifies the default task to execute.
19
+ task :default => [:test]
20
+
21
+ # The directory to generate +rdoc+ in.
22
+ RDOC_DIR="doc/html"
23
+
24
+ # This global variable contains files that will be erased by the `clean` task.
25
+ # The `clean` task itself is automatically generated by requiring `rake/clean`.
26
+
27
+ CLEAN << RDOC_DIR << "pkg"
28
+
29
+
30
+ # This is the task that generates the +rdoc+ documentation from the
31
+ # source files. Instantiating Rake::RDocTask automatically generates a
32
+ # task called `rdoc`.
33
+
34
+ Rake::RDocTask.new do |rd|
35
+ # Options for documenation generation are specified inside of
36
+ # this block. For example the following line specifies that the
37
+ # content of the README file should be the main page of the
38
+ # documenation.
39
+ rd.main = "README"
40
+
41
+ # The following line specifies all the files to extract
42
+ # documenation from.
43
+ rd.rdoc_files.include( "README", "AUTHORS", "LICENSE", "TODO",
44
+ "CHANGELOG", "bin/**/*", "lib/**/*.rb",
45
+ "examples/**/*rb","test/**/*.rb", "doc/*.rdoc")
46
+ # This one specifies the output directory ...
47
+ rd.rdoc_dir = "doc/html"
48
+
49
+ # Or the HTML title of the generated documentation set.
50
+ rd.title = "#{SHORTNAME}: #{DESC}"
51
+
52
+ # These are options specifiying how source code inlined in the
53
+ # documentation should be formatted.
54
+
55
+ rd.options = ["--line-numbers"]
56
+
57
+ # Check:
58
+ # `rdoc --help` for more rdoc options
59
+ # the {rdoc documenation home}[http://www.ruby-doc.org/stdlib/libdoc/rdoc/rdoc/index.html]
60
+ # or the documentation for the +Rake::RDocTask+ task[http://rake.rubyforge.org/classes/Rake/RDocTask.html]
61
+ end
62
+
63
+ # The GemPackageTask facilitates getting all your files collected
64
+ # together into gem archives. You can also use it to generate tarball
65
+ # and zip archives.
66
+
67
+ # First you'll need to assemble a gemspec
68
+
69
+ PKG_FILES = FileList['lib/**/*.rb', 'bin/**/*', 'examples/**/*', '[A-Z]*', 'test/**/*'].to_a
70
+
71
+ spec = Gem::Specification.new do |s|
72
+ s.platform = Gem::Platform::RUBY
73
+ s.summary = "#{SHORTNAME}: #{DESC}"
74
+ s.name = SHORTNAME
75
+ s.version = PKG_VERSION
76
+ s.files = PKG_FILES
77
+ s.author = "Tim Becker"
78
+ s.email = "tim.becker@kuriositaet.de"
79
+ s.homepage = "https://github.com/a2800276/7816"
80
+ s.requirements << "none"
81
+ s.require_path = 'lib'
82
+ s.add_dependency("hexy")
83
+ s.add_dependency("tlv")
84
+ s.add_dependency("smartcard")
85
+ #s.has_rdoc=true
86
+ s.description = LONG_DESC
87
+ end
88
+
89
+ # Adding a new GemPackageTask adds a task named `package`, which generates
90
+ # packages as gems, tarball and zip archives.
91
+ Gem::PackageTask.new(spec) do |pkg|
92
+ pkg.need_zip = true
93
+ pkg.need_tar_gz = true
94
+ end
95
+
96
+
97
+ # This task is used to demonstrate how to upload files to Rubyforge.
98
+ # Calling `upload_page` creates a current version of the +rdoc+
99
+ # documentation and uploads it to the Rubyforge homepage of the project,
100
+ # assuming it's hosted there and naming conventions haven't changed.
101
+ #
102
+ # This task uses `sh` to call the `scp` binary, which is plattform
103
+ # dependant and may not be installed on your computer if you're using
104
+ # Windows. I'm currently not aware of any pure ruby way to do scp
105
+ # transfers.
106
+
107
+ RubyForgeProject=SHORTNAME
108
+
109
+ desc "Upload the web pages to the web."
110
+ task :upload_pages => ["rdoc"] do
111
+ if RubyForgeProject then
112
+ path = "/var/www/gforge-projects/#{RubyForgeProject}"
113
+ sh "scp -r doc/html/* #{RUBYFORGE_USER}@rubyforge.org:#{path}"
114
+ sh "scp doc/images/*.png #{RUBYFORGE_USER}@rubyforge.org:#{path}/images"
115
+ end
116
+ end
117
+
118
+ # This task will run the unit tests provided in files called
119
+ # `test/test*.rb`. The task itself can be run with a call to `rake test`
120
+
121
+ Rake::TestTask.new do |t|
122
+ t.libs << "test"
123
+ t.libs << "lib"
124
+ t.ruby_opts = ["-rubygems"]
125
+ t.test_files = FileList['test/*.rb']
126
+ t.verbose = true
127
+ end
128
+
129
+
130
+ desc "generate iso apdu command classes"
131
+ task :iso_apdu do
132
+ ruby "-rubygems build/create_apdu.rb -i lib/apdu.spec -o lib/apdu_generated.rb -s lib/apdu_generated_impl.rb"
133
+ end
134
+
135
+ desc "delete generated apdu classes"
136
+ task :iso_apdu_clean do
137
+ File.delete "lib/apdu_generated.rb" if File.exist? "lib/apdu_generated.rb"
138
+ end
139
+
140
+
141
+
data/THANKS ADDED
File without changes
data/TODO ADDED
@@ -0,0 +1,2 @@
1
+
2
+ the atr list contains regular expression type wildcards, handle those for better recognition.
@@ -0,0 +1,5 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'iso7816'
5
+
@@ -0,0 +1,554 @@
1
+ require 'hexy'
2
+ module ISO7816
3
+ def ISO7816.b2s bytestr
4
+ r = bytestr.unpack("H*")[0]
5
+ r.length > 1 ? r : " "
6
+ end
7
+ def ISO7816.make_binary value
8
+ value = [value].pack("C") if value.is_a? Numeric
9
+ end
10
+
11
+ def b2s bytestr
12
+ ISO7816.b2s bytestr
13
+ end
14
+
15
+ def s2b string
16
+ ISO7816.s2b string
17
+ end
18
+
19
+ def ISO7816.s2b string
20
+ string = string.gsub(/\s+/, "")
21
+ [string].pack("H*")
22
+ end
23
+
24
+
25
+ module APDU
26
+
27
+ #
28
+ # Models an ISO 7816 APDU (Application Protocol Data Unit) the basic
29
+ # "packet"/unit of communication sent from terminal/cardreader to the
30
+ # card attributes are: CLASS (.cla) INS (.ins), PARAM1 (p1), PARAM2
31
+ # (.p2) DATA (.data) and LE (.le), the length of the data expected in
32
+ # the response from the card.
33
+
34
+ class APDU
35
+ include ISO7816
36
+ # data is the transported data
37
+ attr_accessor :card, :name
38
+ attr_writer :data, :le
39
+ #attr_reader :cla, :ins, :p1, :p2
40
+
41
+ def initialize card=nil
42
+ @card=card
43
+ end
44
+
45
+ def cla
46
+ @cla || self.class._cla
47
+ end
48
+
49
+ def ins
50
+ @ins || self.class._ins
51
+ end
52
+
53
+ def p1
54
+ @p1 || self.class._p1
55
+ end
56
+
57
+ def p2
58
+ @p2 || self.class._p2
59
+ end
60
+
61
+ def le
62
+ @le || self.class._le
63
+ end
64
+
65
+ def data
66
+ @data || self.class._data
67
+ end
68
+
69
+ def cla= cla
70
+ @cla = "" << cla
71
+ end
72
+ def ins= ins
73
+ @ins = "" << ins
74
+ end
75
+ def p1= p1
76
+ @p1 = "" << p1
77
+ end
78
+ def p2= p2
79
+ @p2 = "" << p2
80
+ end
81
+ def lc
82
+ unless @lc
83
+ return "" if @data == "" || @data==nil
84
+ return [@data.length].pack("C")
85
+ end
86
+ @lc
87
+ end
88
+
89
+
90
+ # normally don't need to set lc, because it's calculated from the
91
+ # data's length, but for testing it may be necessary to set an
92
+ # correct value ...
93
+ def lc= val
94
+ if val.is_a? Numeric
95
+ @lc = [val].pack("C")
96
+ else
97
+ @lc=val
98
+ end
99
+ end
100
+
101
+ def le= val
102
+ if val.is_a? Numeric
103
+ @le = [val].pack("C")
104
+ else
105
+ @le=val
106
+ end
107
+ end
108
+
109
+ # add data field to the apdu, contrary to
110
+ # the +data+ accessor, this method takes a hex string
111
+ # and not a binary string. The following are identical:
112
+ #
113
+ # apdu.data = "\xaa\xaa\xaa"
114
+ #
115
+ # and
116
+ #
117
+ # apdu.hex_data = "aa aa aa"
118
+ def hex_data= val
119
+ self.data = s2b(val)
120
+ end
121
+
122
+ # Indicate whether this is a case_4 APDU.
123
+ # In case of T=0, le field is NOT sent along for case 4 APDUs
124
+ def case_4?
125
+ data!="" && le!=""
126
+ end
127
+
128
+ # INS may not have values of 0x60..0x6f and 0x90..0x9f
129
+ def ins_valid?
130
+ invalid = APDU.in_range(@ins, "\x60", "\x6f") || APDU.in_range(@ins, "\x90", "\x9f")
131
+ !invalid
132
+ end
133
+
134
+ def self.in_range byte, from, to
135
+ byte >= from && byte <= to
136
+ end
137
+
138
+ def to_b
139
+ bytes = ""
140
+ bytes << cla
141
+ bytes << ins
142
+ bytes << p1
143
+ bytes << p2
144
+
145
+ if data != "" || lc != nil
146
+ bytes << lc
147
+ bytes << data
148
+ end
149
+ if le != "" && le != nil
150
+ bytes << le
151
+ end
152
+ bytes
153
+ end
154
+
155
+ def send handle_more_data=true, card=nil
156
+ card = card || @card
157
+ raise "no card to send data to" unless card
158
+
159
+ data_to_send = self.to_b
160
+ data_to_send = data_to_send.chop if card.t0? && self.case_4?
161
+
162
+ if card.t0? && data_to_send.length == 4
163
+ #7816-3: ... case1 apdu mapped onto TPDU with P3=00
164
+ data_to_send << "\x00"
165
+ end
166
+ if card.respond_to? :comment
167
+ card.comment "#{self.class.to_s}\n#{self.to_s.strip}"
168
+ end
169
+
170
+ card.send data_to_send
171
+
172
+ to_receive = 2
173
+ to_receive += le.unpack("C")[0] if le && le != ""
174
+ if card.t0? && self.case_4?
175
+ to_receive = 2
176
+ end
177
+
178
+ r = card.receive(to_receive)
179
+ resp = Response.new(r, self)
180
+
181
+ if (handle_more_data && how_much_more = resp.more_data?)
182
+ gr = GET_RESPONSE.new
183
+ gr.le = how_much_more
184
+ resp = gr.send(false, card) # avoid infinite get_response loop... how to handle?
185
+ end
186
+
187
+ resp
188
+ end
189
+
190
+ def to_s
191
+
192
+ line1 = "#{@name}\n|CLA|INS| P1| P2|"
193
+ line2 = [cla, ins, p1, p2].map { |b|
194
+ "| "+ ( b.unpack("H*")[0] )
195
+ }.join+"|"
196
+
197
+ data_ = data
198
+ if data_.length > 16
199
+ data_ = ""
200
+ end
201
+ if data_ != "" || le != ""
202
+ field_size = data_.length*2>"Data".length ? data_.length*2 : "Data".length
203
+ if data_.length >=2
204
+ pad0 = " "*((data_.length*2 - 4)/2)
205
+ pad1 = ""
206
+ else
207
+ pad0 = ""
208
+ pad1 = " "
209
+ end
210
+
211
+ #line1 += "| LC|#{pad0}Data#{pad0}| LE|"
212
+ line1 += ("| LC|% #{field_size}s| LE|" % "Data")
213
+ #line2 += "| #{b2s(self.lc)}|#{pad1}#{b2s(@data)}#{pad1}| #{@le?b2s(@le):" "}|"
214
+ line2 += "| #{b2s(lc)}|%#{field_size}s| #{le ? b2s(le) : ' '}|" % b2s(data_)
215
+ end
216
+ txt = "#{line1}\n#{line2}"
217
+ if data.length > 16
218
+ h = Hexy.new @data
219
+ txt << "\n\n"
220
+ txt << h.to_s
221
+ end
222
+ txt
223
+ end
224
+
225
+ # parses a string of bytes into an APDU object.
226
+ def self.to_apdu bytes, sanity=true
227
+ apdu = APDU.new
228
+ apdu.cla = bytes[0,1]
229
+ apdu.ins = bytes[1,1]
230
+ apdu.p1 = bytes[2,1]
231
+ apdu.p2 = bytes[3,1]
232
+ if bytes.size > 4 # at least le
233
+ if bytes.size > 5 # weird, 0 size lc and no data
234
+ apdu.lc = bytes[4,1]
235
+ len = apdu.lc.unpack("C")[0]
236
+ apdu.data = bytes[5,len]
237
+ if bytes.size > 5+len
238
+ raise "Too many bytes!" if bytes.size > 6+len
239
+ apdu.le = bytes[5+len,1]
240
+ end
241
+ else # only le
242
+ apdu.le = bytes[4,1]
243
+ end #le
244
+ end # lc, data, le
245
+ apdu
246
+ end #to_apdu
247
+
248
+ class << self
249
+
250
+ def cla cla
251
+ @cla=""<<cla
252
+ end
253
+ def ins ins
254
+ @ins=""<<ins
255
+ end
256
+ def p1 p1
257
+ @p1=""<<p1
258
+ end
259
+ def p2 p2
260
+ @p2=""<<p2
261
+ end
262
+ def data data
263
+ @data=""<<data
264
+ end
265
+ def le le
266
+ @le=""<<le
267
+ end
268
+
269
+ def _cla
270
+ @cla ||= self == APDU ? "\x00" : superclass._cla
271
+ end
272
+
273
+ def _ins
274
+ @ins ||= self == APDU ? "\x00" : superclass._ins
275
+ end
276
+
277
+ def _p1
278
+ @p1 ||= self == APDU ? "\x00" : superclass._p1
279
+ end
280
+
281
+ def _p2
282
+ @p2 ||= self == APDU ? "\x00" : superclass._p2
283
+ end
284
+
285
+ def _data
286
+ @data ||= self == APDU ? "" : superclass._data
287
+ end
288
+ def _le
289
+ @le ||= self == APDU ? "" : superclass._le
290
+ end
291
+ end
292
+ end # class APDU
293
+
294
+
295
+ # Each new APDU generated provides a random CLA, INS
296
+ class RandomAPDU < APDU
297
+ def initialize card=nil, rand_p1p2 = true, rand_le = true, rand_data=0
298
+ super card
299
+ @cla = [rand(256)].pack("C")
300
+ @ins = [rand(256)].pack("C")
301
+ until ins_valid?
302
+ @ins = [rand(256)].pack("C")
303
+ end
304
+ if (rand_p1p2)
305
+ @p1 = [rand(256)].pack("C")
306
+ @p2 = [rand(256)].pack("C")
307
+ end
308
+
309
+ if (rand_le)
310
+ if rand() > 0.5
311
+ @le = [rand(256)].pack("C")
312
+ end
313
+ end
314
+
315
+ if (rand_data && rand_data>0)
316
+ data=[]
317
+ 1.upto(rand_data) {
318
+ data << [rand(256)].pack("C")
319
+ }
320
+ @data = data.join
321
+ end
322
+
323
+ end
324
+
325
+ def self.get_random card, num, seed=0, allow_invalid_ins = false
326
+ srand(seed)
327
+ arr = []
328
+ 1.upto(num) {
329
+ apdu = RandomAPDU.new card
330
+ while allow_invalid_ins || !apdu.ins_valid?
331
+ apdu = RandomAPDU.new card
332
+ end
333
+ arr << apdu
334
+ }
335
+ arr
336
+ end
337
+
338
+ end
339
+
340
+ class Response
341
+ include ISO7816
342
+ attr_accessor :data, :sw1, :sw2
343
+
344
+ def initialize data, le=0
345
+ if le.is_a? ISO7816::APDU::APDU
346
+ le = le.le.unpack("C")[0]
347
+ le = le ? le : 0
348
+ end
349
+ @data = data[0,le]
350
+ @sw1 = data[-2,1]
351
+ @sw2 = data[-1,1]
352
+
353
+ end
354
+
355
+ def status
356
+ if @sw1 && @sw2
357
+ status = @sw1.dup
358
+ return b2s(status<<@sw2)
359
+ end
360
+ "????"
361
+ end
362
+
363
+ def normal?
364
+ (@sw1 == "\x90" && @sw2 == "\x00") || @sw1 == "\x61"
365
+ end
366
+
367
+ def warning?
368
+ ["\x62","\x63"].include? @sw1
369
+ end
370
+
371
+ def execution_err?
372
+ ["\x64", "\x65", "\x66"].include? @sw1
373
+ end
374
+
375
+ def checking_err?
376
+ ["\x67", "\x68", "\x69", "\x6a", "\x6b", "\x6c", "\x6d", "\x6e", "\x6f"].include? @sw1
377
+ end
378
+
379
+ def more_data?
380
+ return @sw1 == "\x61" ? @sw2 : false
381
+ end
382
+
383
+ def to_s
384
+ str = ""
385
+ str << "APDU Response\n"
386
+ str << " Data(#{@data.length}): #{b2s(@data)}\n" unless @data == ""
387
+ str << " SW1: #{b2s(@sw1)} (#{sw1_explanation})\n"
388
+ str << " SW2: #{b2s(@sw2)} (#{sw2_explanation})\n"
389
+ str
390
+ end
391
+
392
+ def sw1_explanation
393
+ case @sw1
394
+ when "\x90"
395
+ return "OK: No further qualification"
396
+ when "\x61"
397
+ return "OK: SW2 indicates the number of response bytes still available"
398
+ # Warning processings
399
+ when "\x62"
400
+ return "WARN: State of non-volatile memory unchanged"
401
+ when "\x63" : return "WARN: State of non-volatile memory changed"
402
+ # Execution errors
403
+ when "\x64"
404
+ return "EXE ERR: State of non-volatile memory unchanged"
405
+ when "\x65"
406
+ return "EXE ERR: State of non-volatile memory changed"
407
+ when "\x66"
408
+ return "EXE ERR: Reserved for security-related issues"
409
+ # Checking errors
410
+ when "\x67"
411
+ return "CHK ERR: Wrong length"
412
+ when "\x68"
413
+ return "CHK ERR: Functions in CLA not supported"
414
+ when "\x69"
415
+ return "CHK ERR: Command not allowed"
416
+ when "\x6A"
417
+ return "CHK ERR: Wrong parameter(s) P1-P2"
418
+ when "\x6B"
419
+ return "CHK ERR: Wrong parameter(s) P1-P2"
420
+ when "\x6C"
421
+ return "CHK ERR: Wrong length Le: SW2 indicates the exact length"
422
+ when "\x6D"
423
+ return "CHK ERR: Instruction code not supported or invalid"
424
+ when "\x6E"
425
+ return "CHK ERR: Class not supported"
426
+ when "\x6F"
427
+ return "CHK ERR: No precise diagnosis"
428
+ else
429
+ return "UNKNOWN : #{b2s(@sw1)}"
430
+ end
431
+ end
432
+
433
+ def sw2_explanation
434
+ case @sw1
435
+ when "\x90", "\x64", "\x67", "\x6b", "\x6d", "\x6e", "\x6f"
436
+ sw2_check00
437
+ when "\x61"
438
+ "#{b2s(@sw2)} bytes remaining."
439
+ when "\x62"
440
+ sw2_62
441
+ when "\x63"
442
+ sw2_63
443
+ when "\x65"
444
+ sw2_65
445
+ when "\x66"
446
+ "security (undefined)"
447
+ when "\x68"
448
+ sw2_68
449
+ when "\x69"
450
+ sw2_69
451
+ when "\x6a"
452
+ sw2_6a
453
+ when "\x6c"
454
+ "wrong lenght. Exact len: #{b2s(@sw2)}"
455
+ else
456
+ "UNKNOWN : #{b2s(@sw2)}"
457
+ end
458
+ end
459
+
460
+ private
461
+
462
+ def sw2_check00
463
+ @sw2 == "\x00" ? "" : "sw2 should be 0x00, is #{b2s(@sw2)}"
464
+ end
465
+ def sw2_62
466
+ case @sw2
467
+ when "\x00": "No information given"
468
+ when "\x81": "Part of returned data may be corrupted"
469
+ when "\x82": "End of file/record reached before reading Le bytes"
470
+ when "\x83": "Selected file invalidated"
471
+ when "\x84": "FCI not formatted according to spec"
472
+ else "unknown sw2 for sw1=#{b2s(@sw1)}: #{b2s(@sw2)}"
473
+ end
474
+ end
475
+
476
+ def sw2_63
477
+ case @sw2
478
+ when "\x00": "No information given"
479
+ when "\x81": "File filled up by the last write"
480
+ when "\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6","\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE","\xCF"
481
+ "Counter provided by '#{@sw2[0] & 0x0f}'"
482
+ else "unknown sw2 for sw1=#{b2s(@sw1)}: #{b2s(@sw2)}"
483
+ end
484
+ end
485
+
486
+ def sw2_65
487
+ case @sw2
488
+ when "\x00": "No information given"
489
+ when "\x81": "Memory failure"
490
+ else "unknown sw2 for sw1=#{b2s(@sw1)}: #{b2s(@sw2)}"
491
+ end
492
+ end
493
+
494
+ def sw2_68
495
+ case @sw2
496
+ when "\x00": "No information given"
497
+ when "\x81": "Logical channel not supported"
498
+ when "\x82": "Secure messaging not supported"
499
+ else "unknown sw2 for sw1=#{b2s(@sw1)}: #{b2s(@sw2)}"
500
+ end
501
+ end
502
+
503
+ def sw2_69
504
+ case @sw2
505
+ when "\x00": "No information given"
506
+ when "\x81": "Command incompatible with file structure"
507
+ when "\x82": "Security status not satisfied"
508
+ when "\x83": "Authentication method blocked"
509
+ when "\x84": "Referenced data invalidated"
510
+ when "\x85": "Conditions of use not satisfied"
511
+ when "\x86": "Command not allowed (no current EF)"
512
+ when "\x87": "Expected SM data objects missing"
513
+ when "\x88": "SM data objects incorrect"
514
+ else "unknown sw2 for sw1=#{b2s(@sw1)}: #{b2s(@sw2)}"
515
+ end
516
+ end
517
+
518
+ def sw2_6a
519
+ case @sw2
520
+ when "\x00": "No information given"
521
+ when "\x80": "Incorrect parameters in the data field"
522
+ when "\x81": "Function not supported"
523
+ when "\x82": "File not found"
524
+ when "\x83": "Record not found"
525
+ when "\x84": "Not enough memory space in the file"
526
+ when "\x85": "Lc inconsistent with TLV structure"
527
+ when "\x86": "Incorrect parameters P1-P2"
528
+ when "\x87": "Lc inconsistent with P1-P2"
529
+ when "\x88": "Referenced data not found"
530
+ else "unknown sw2 for sw1=#{b2s(@sw1)}: #{b2s(@sw2)}"
531
+ end
532
+ end
533
+
534
+ end
535
+
536
+
537
+ end # module APDU
538
+
539
+ end # ISO7816
540
+
541
+ if __FILE__ == $0
542
+ puts 0x04.class
543
+ puts "here"
544
+ a = ISO7816::APDU::APDU.new
545
+ puts a
546
+ a.data = "\xde\xad\xbe\xef"
547
+ puts a
548
+ a = ISO7816::APDU::APDU.new
549
+ a.data = "\xde"
550
+ a.le = "\x01"
551
+ puts a
552
+ a = ISO7816::APDU::SELECT.new
553
+ puts a
554
+ end