iso7816 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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