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

Sign up to get free protection for your applications and to get access to all the features.
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