smartcard 0.4.1-x86-mswin32-60 → 0.4.3-x86-mswin32-60

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/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ v0.4.3. Fixed gem manifest to state dependency on rubyzip.
2
+
3
+ v0.4.2. GlobalPlatform applet instalation.
4
+
1
5
  v0.4.1. Workaround ATR resend bug in JCOP simulator 3.2.7.
2
6
 
3
7
  v0.4.0. High-level functionality for ISO7816 cards.
data/Manifest CHANGED
@@ -1,5 +1,9 @@
1
1
  BUILD
2
2
  CHANGELOG
3
+ LICENSE
4
+ Manifest
5
+ README
6
+ Rakefile
3
7
  ext/smartcard_pcsc/extconf.rb
4
8
  ext/smartcard_pcsc/pcsc.h
5
9
  ext/smartcard_pcsc/pcsc_card.c
@@ -13,6 +17,10 @@ ext/smartcard_pcsc/pcsc_namespace.c
13
17
  ext/smartcard_pcsc/pcsc_reader_states.c
14
18
  ext/smartcard_pcsc/pcsc_surrogate_reader.h
15
19
  ext/smartcard_pcsc/pcsc_surrogate_wintypes.h
20
+ lib/smartcard.rb
21
+ lib/smartcard/gp/asn1_ber.rb
22
+ lib/smartcard/gp/cap_loader.rb
23
+ lib/smartcard/gp/des.rb
16
24
  lib/smartcard/gp/gp_card_mixin.rb
17
25
  lib/smartcard/iso/auto_configurator.rb
18
26
  lib/smartcard/iso/iso_card_mixin.rb
@@ -22,13 +30,12 @@ lib/smartcard/iso/jcop_remote_transport.rb
22
30
  lib/smartcard/iso/pcsc_transport.rb
23
31
  lib/smartcard/iso/transport.rb
24
32
  lib/smartcard/pcsc/pcsc_exception.rb
25
- lib/smartcard.rb
26
- LICENSE
27
- Manifest
28
- Rakefile
29
- README
30
- smartcard.gemspec
33
+ test/gp/asn1_ber_test.rb
34
+ test/gp/cap_loader_test.rb
35
+ test/gp/des_test.rb
31
36
  test/gp/gp_card_mixin_test.rb
37
+ test/gp/hello.apdu
38
+ test/gp/hello.cap
32
39
  test/iso/auto_configurator_test.rb
33
40
  test/iso/iso_card_mixin_test.rb
34
41
  test/iso/jcop_remote_test.rb
data/Rakefile CHANGED
@@ -9,14 +9,16 @@ Echoe.new('smartcard') do |p|
9
9
  p.email = 'victor@costan.us'
10
10
  p.summary = 'Interface with ISO 7816 smart cards.'
11
11
  p.url = 'http://www.costan.us/smartcard'
12
+ p.dependencies = ['rubyzip >=0.9.1']
12
13
 
13
- p.need_tar_gz = !Platform.windows?
14
- p.need_zip = !Platform.windows?
14
+ p.need_tar_gz = !Gem.win_platform?
15
+ p.need_zip = !Gem.win_platform?
15
16
  p.clean_pattern += ['ext/**/*.manifest', 'ext/**/*_autogen.h']
16
- p.rdoc_pattern = /^(lib|bin|tasks|ext)|^BUILD|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
17
+ p.rdoc_pattern =
18
+ /^(lib|bin|tasks|ext)|^BUILD|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
17
19
 
18
20
  p.eval = proc do |p|
19
- if Platform.windows?
21
+ if Gem.win_platform?
20
22
  p.files += ['lib/smartcard/pcsc.so']
21
23
  p.platform = Gem::Platform::CURRENT
22
24
 
@@ -0,0 +1,199 @@
1
+ # Encoding and decoding of ASN.1-BER data.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Massachusetts Institute of Technology
5
+ # License:: MIT
6
+
7
+ # :nodoc: namespace
8
+ module Smartcard::Gp
9
+
10
+
11
+ # Logic for encoding and decoding ASN.1-BER data as specified in X.690-0207.
12
+ module Asn1Ber
13
+ # Decodes a TLV tag (the data type).
14
+ #
15
+ # Args:
16
+ # data:: the array to decode from
17
+ # offset:: the position of the first byte containing the tag
18
+ #
19
+ # Returns the offset of the first byte after the tag, and the tag information.
20
+ # Tag information is a hash with the following keys.
21
+ # :class:: the tag's class (symbol, named after X690-0207)
22
+ # :primitive:: if +false+, the tag's value is a sequence of TLVs
23
+ # :number:: the tag's number
24
+ def self.decode_tag(data, offset)
25
+ class_bits = data[offset] >> 6
26
+ tag_class = [:universal, :application, :context, :private][class_bits]
27
+ tag_primitive = (data[offset] & 0x20) == 0
28
+ tag_number = (data[offset] & 0x1F)
29
+ if tag_number == 0x1F
30
+ tag_number = 0
31
+ loop do
32
+ offset += 1
33
+ tag_number <<= 7
34
+ tag_number |= (data[offset] & 0x7F)
35
+ break if (data[offset] & 0x80) == 0
36
+ end
37
+ end
38
+ return (offset + 1), { :class => tag_class, :primitive => tag_primitive,
39
+ :number => tag_number }
40
+ end
41
+
42
+ # Decodes a TLV length.
43
+ #
44
+ # Args:
45
+ # data:: the array to decode from
46
+ # offset:: the position of the first byte containing the length
47
+ #
48
+ # Returns the offset of the first byte after the length, and the length. The
49
+ # returned value might be +:indefinite+ if the encoding uses the indefinite
50
+ # length.
51
+ def self.decode_length(data, offset)
52
+ return (offset + 1), data[offset] if (data[offset] & 0x80) == 0
53
+ len_bytes = (data[offset] & 0x7F)
54
+ return (offset + 1), :indefinite if len_bytes == 0
55
+ length = 0
56
+ len_bytes.times do
57
+ offset += 1
58
+ length = (length << 8) | data[offset]
59
+ end
60
+ return (offset + 1), length
61
+ end
62
+
63
+
64
+ # Decodes a TLV value.
65
+ #
66
+ # Args:
67
+ # data:: the array to decode from
68
+ # offset:: the position of the first byte containing the length
69
+ #
70
+ # Returns the offset of the first byte after the value, and the value.
71
+ def self.decode_value(data, offset, length)
72
+ return offset + length, data[offset, length] unless length == :indefinite
73
+
74
+ length = 0
75
+ loop do
76
+ raise 'Unterminated data' if offset + length + 2 > data.length
77
+ break if data[offset + length, 2] == [0, 0]
78
+ length += 1
79
+ end
80
+ return (offset + length + 2), data[offset, length]
81
+ end
82
+
83
+ # Maps a TLV value with a known tag to a Ruby data type.
84
+ def self.map_value(value, tag)
85
+ # TODO(costan): map primitive types if necessary
86
+ value
87
+ end
88
+
89
+ # Decodes a TLV (tag-length-value).
90
+ #
91
+ # Returns a hash that contains tag and value information. See decode_tag for
92
+ # the keys containing the tag information. Value information is contained in
93
+ # the :value: tag.
94
+ def self.decode_tlv(data, offset)
95
+ offset, tag = decode_tag data, offset
96
+ offset, length = decode_length data, offset
97
+ offset, value = decode_value data, offset, length
98
+
99
+ tag[:value] = tag[:primitive] ? map_value(value, tag) : decode(value)
100
+ return offset, tag
101
+ end
102
+
103
+ # Decodes a sequence of TLVs (tag-length-value).
104
+ #
105
+ # Returns an array with one element for each TLV in the sequence. See
106
+ # decode_tlv for the format of each array element.
107
+ def self.decode(data, offset = 0, length = data.length - offset)
108
+ sequence = []
109
+ loop do
110
+ break if offset >= length
111
+ offset, tlv = decode_tlv data, offset
112
+ sequence << tlv
113
+ end
114
+ sequence
115
+ end
116
+
117
+ # Encodes a TLV tag (the data type).
118
+ #
119
+ # Args:
120
+ # tag:: a hash with the keys produced by decode_tag.
121
+ #
122
+ # Returns an array of byte values.
123
+ def self.encode_tag(tag)
124
+ tag_classes = { :universal => 0, :application => 1, :context => 2,
125
+ :private => 3 }
126
+ tag_lead = (tag_classes[tag[:class]] << 6) | (tag[:primitive] ? 0x00 : 0x20)
127
+ return [tag_lead | tag[:number]] if tag[:number] < 0x1F
128
+
129
+ number_bytes, number = [], tag[:number]
130
+ first = true
131
+ while number != 0
132
+ byte = (number & 0x7F)
133
+ number >>= 7
134
+ byte |= 0x80 unless first
135
+ first = false
136
+ number_bytes << byte
137
+ end
138
+ [tag_lead | 0x1F] + number_bytes.reverse
139
+ end
140
+
141
+ # Encodes a TLV length (the length of the data).
142
+ #
143
+ # Args::
144
+ # length:: the length to be encoded (number of :indefinite)
145
+ #
146
+ # Returns an array of byte values.
147
+ def self.encode_length(length)
148
+ return [0x80] if length == :indefinite
149
+ return [length] if length < 0x80
150
+ length_bytes = []
151
+ while length > 0
152
+ length_bytes << (length & 0xFF)
153
+ length >>= 8
154
+ end
155
+ [0x80 | length_bytes.length] + length_bytes.reverse
156
+ end
157
+
158
+ # Encodes a TLV (tag-length-value).
159
+ #
160
+ # Args::
161
+ # tlv:: hash with tag and value information, to be encoeded as TLV; see
162
+ # decode_tlv for the hash keys encoding the tag and value
163
+ #
164
+ # Returns an array of byte values.
165
+ def self.encode_tlv(tlv)
166
+ value = tlv[:primitive] ? tlv[:value] : encode(tlv[:value])
167
+ [encode_tag(tlv), encode_length(value.length), value].flatten
168
+ end
169
+
170
+ # Encodes a sequence of TLVs (tag-length-value).
171
+ #
172
+ # Args::
173
+ # tlvs:: an array of hashes to be encoded as TLV
174
+ #
175
+ # Returns an array of byte values.
176
+ def self.encode(tlvs)
177
+ tlvs.map { |tlv| encode_tlv tlv }.flatten
178
+ end
179
+
180
+ # Visitor pattern for decoded TLVs.
181
+ #
182
+ # Args:
183
+ # tlvs:: the TLVs to visit
184
+ # tag_path:: internal, do not use
185
+ #
186
+ # Yields: |tag_path, value| tag_path lists the numeric tags for the current
187
+ # value's tag, and all the parents' tags.
188
+ def self.visit(tlvs, tag_path = [], &block)
189
+ tlvs.each do |tlv|
190
+ tag_number = encode_tag(tlv).inject { |acc, v| (acc << 8) | v }
191
+ new_tag_path = tag_path + [tag_number]
192
+ yield new_tag_path, tlv[:value]
193
+ next if tlv[:primitive]
194
+ visit tlv[:value], new_tag_path, &block
195
+ end
196
+ end
197
+ end # module Smartcard::Gp::Asn1Ber
198
+
199
+ end # namespace
@@ -0,0 +1,88 @@
1
+ # Loads JavaCard CAP files.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Massachusetts Institute of Technology
5
+ # License:: MIT
6
+
7
+ require 'zip/zip'
8
+
9
+ # :nodoc: namespace
10
+ module Smartcard::Gp
11
+
12
+
13
+ # Logic for loading JavaCard CAP files.
14
+ module CapLoader
15
+ # Loads a CAP file.
16
+ #
17
+ # Returns a hash mapping component names to component data.
18
+ def self.load_cap(cap_file)
19
+ components = {}
20
+ Zip::ZipFile.open(cap_file) do |file|
21
+ file.each do |entry|
22
+ data = entry.get_input_stream { |io| io.read }
23
+ offset = 0
24
+ while offset < data.length
25
+ tag = TAG_NAMES[data[offset, 1].unpack('C').first]
26
+ length = data[offset + 1, 2].unpack('n').first
27
+ value = data[offset + 3, length]
28
+ components[tag] = value
29
+ offset += 3 + length
30
+ end
31
+ end
32
+ end
33
+ components
34
+ end
35
+
36
+ # Serializes CAP components for on-card loading.
37
+ #
38
+ # Returns an array of bytes.
39
+ def self.serialize_components(components)
40
+ [:header, :directory, :import, :applet, :class, :method, :static_field,
41
+ :export, :constant_pool, :reference_location].map { |name|
42
+ tag = TAG_NAMES.keys.find { |k| TAG_NAMES[k] == name }
43
+ if components[name]
44
+ length = [components[name].length].pack('n').unpack('C*')
45
+ data = components[name].unpack('C*')
46
+ [tag, length, data]
47
+ else
48
+ []
49
+ end
50
+ }.flatten
51
+ end
52
+
53
+ # Parses the Applet section in a CAP file, obtaining applet AIDs.
54
+ #
55
+ # Returns an array of hashes, one hash per applet. The hash has a key +:aid+
56
+ # that contains the applet's AID.
57
+ def self.parse_applets(components)
58
+ applets = []
59
+ return applets unless section = components[:applet]
60
+ offset = 1
61
+ section[0].times do
62
+ aid_length = section[offset]
63
+ install_method = section[offset + 1 + aid_length, 2].unpack('n').first
64
+ applets << { :aid => section[offset + 1, aid_length].unpack('C*'),
65
+ :install_method => install_method }
66
+ offset += 3 + aid_length
67
+ end
68
+ applets
69
+ end
70
+
71
+ # Loads a CAP file and serializes its components for on-card loading.
72
+ #
73
+ # Returns an array of bytes.
74
+ def self.cap_load_data(cap_file)
75
+ components = load_cap cap_file
76
+ { :data => serialize_components(components),
77
+ :applets => parse_applets(components) }
78
+ end
79
+
80
+ # Maps numeric tags to tag names.
81
+ TAG_NAMES = {
82
+ 1 => :header, 2 => :directory, 3 => :applet, 4 => :import,
83
+ 5 => :constant_pool, 6 => :class, 7 => :method, 8 => :static_field,
84
+ 9 => :reference_location, 10 => :export, 11 => :descriptor, 12 => :debug
85
+ }
86
+ end # module Smartcard::Gp::CapLoader
87
+
88
+ end # namespace
@@ -0,0 +1,68 @@
1
+ # DES and 3DES encryption and MAC logic for GlobalPlatform secure channels.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Massachusetts Institute of Technology
5
+ # License:: MIT
6
+
7
+ require 'openssl'
8
+
9
+ # :nodoc: namespace
10
+ module Smartcard::Gp
11
+
12
+
13
+ # DES and 3DES encryption and MAC logic for GlobalPlatform secure channels.
14
+ module Des
15
+ # Generates random bytes for session nonces.
16
+ #
17
+ # Args:
18
+ # bytes:: how many bytes are desired
19
+ #
20
+ # Returns a string of random bytes.
21
+ def self.random_bytes(bytes)
22
+ OpenSSL::Random.random_bytes bytes
23
+ end
24
+
25
+ # Perform DES or 3DES encryption.
26
+ #
27
+ # Args:
28
+ # key:: the encryption key to be used (8-byte or 16-byte)
29
+ # data:: the data to be encrypted or decrypted
30
+ # iv:: initialization vector
31
+ # decrypt:: if +false+ performs encryption, otherwise performs decryption
32
+ #
33
+ # Returns the encrypted / decrypted data.
34
+ def self.crypt(key, data, iv = nil, decrypt = false)
35
+ cipher_name = key.length == 8 ? 'DES-CBC' : 'DES-EDE-CBC'
36
+ cipher = OpenSSL::Cipher::Cipher.new cipher_name
37
+ decrypt ? cipher.decrypt : cipher.encrypt
38
+ cipher.key = key
39
+ cipher.iv = iv || ("\x00" * 8)
40
+ cipher.padding = 0
41
+ crypted = cipher.update data
42
+ crypted += cipher.final
43
+ crypted
44
+ end
45
+
46
+ # Computes a MAC using DES mixed with 3DES.
47
+ def self.mac_retail(key, data, iv = nil)
48
+ # Output transformation: add 80, then 00 until it's block-sized.
49
+ data = data + "\x80"
50
+ data += "\x00" * (8 - data.length % 8) unless data.length % 8 == 0
51
+
52
+ # DES-encrypt everything except for the last block.
53
+ iv = crypt(key[0, 8], data[0, data.length - 8], iv)[-8, 8]
54
+ # Take the chained block and supply it to a 3DES-encryption.
55
+ crypt(key, data[-8, 8], iv)
56
+ end
57
+
58
+ def self.mac_3des(key, data)
59
+ # Output transformation: add 80, then 00 until it's block-sized.
60
+ data = data + "\x80"
61
+ data += "\x00" * (8 - data.length % 8) unless data.length % 8 == 0
62
+
63
+ # The MAC is the last block from 3DES-encrypting the data.
64
+ crypt(key, data)[-8, 8]
65
+ end
66
+ end # module Smartcard::Gp::Des
67
+
68
+ end # namespace