smartcard 0.4.11 → 0.5.0
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 +2 -0
- data/Manifest +11 -16
- data/Rakefile +9 -16
- data/lib/smartcard.rb +11 -2
- data/lib/smartcard/iso/pcsc_transport.rb +22 -37
- data/lib/smartcard/pcsc/card.rb +281 -0
- data/lib/smartcard/pcsc/context.rb +160 -0
- data/lib/smartcard/pcsc/ffi_autogen.rb +327 -0
- data/lib/smartcard/pcsc/ffi_functions.rb +50 -0
- data/lib/smartcard/pcsc/ffi_lib.rb +38 -0
- data/lib/smartcard/pcsc/ffi_structs.rb +57 -0
- data/lib/smartcard/pcsc/pcsc_exception.rb +8 -3
- data/lib/smartcard/pcsc/reader_state_queries.rb +167 -0
- data/smartcard.gemspec +9 -7
- data/tasks/ffi_codegen.rb +145 -0
- data/test/pcsc/card_test.rb +68 -0
- data/test/pcsc/context_test.rb +64 -0
- data/test/pcsc/reader_state_queries_test.rb +72 -0
- metadata +42 -42
- data/ext/smartcard_pcsc/extconf.rb +0 -79
- data/ext/smartcard_pcsc/pcsc.h +0 -52
- data/ext/smartcard_pcsc/pcsc_card.c +0 -492
- data/ext/smartcard_pcsc/pcsc_constants.c +0 -195
- data/ext/smartcard_pcsc/pcsc_context.c +0 -290
- data/ext/smartcard_pcsc/pcsc_exception.c +0 -39
- data/ext/smartcard_pcsc/pcsc_io_request.c +0 -129
- data/ext/smartcard_pcsc/pcsc_main.c +0 -11
- data/ext/smartcard_pcsc/pcsc_multi_strings.c +0 -70
- data/ext/smartcard_pcsc/pcsc_namespace.c +0 -18
- data/ext/smartcard_pcsc/pcsc_reader_states.c +0 -331
- data/ext/smartcard_pcsc/pcsc_surrogate_reader.h +0 -207
- data/ext/smartcard_pcsc/pcsc_surrogate_wintypes.h +0 -63
- data/test/pcsc/containers_test.rb +0 -59
- data/test/pcsc/smoke_test.rb +0 -28
- data/tests/ts_pcsc_ext.rb +0 -76
data/CHANGELOG
CHANGED
data/Manifest
CHANGED
@@ -4,19 +4,6 @@ LICENSE
|
|
4
4
|
Manifest
|
5
5
|
README
|
6
6
|
Rakefile
|
7
|
-
ext/smartcard_pcsc/extconf.rb
|
8
|
-
ext/smartcard_pcsc/pcsc.h
|
9
|
-
ext/smartcard_pcsc/pcsc_card.c
|
10
|
-
ext/smartcard_pcsc/pcsc_constants.c
|
11
|
-
ext/smartcard_pcsc/pcsc_context.c
|
12
|
-
ext/smartcard_pcsc/pcsc_exception.c
|
13
|
-
ext/smartcard_pcsc/pcsc_io_request.c
|
14
|
-
ext/smartcard_pcsc/pcsc_main.c
|
15
|
-
ext/smartcard_pcsc/pcsc_multi_strings.c
|
16
|
-
ext/smartcard_pcsc/pcsc_namespace.c
|
17
|
-
ext/smartcard_pcsc/pcsc_reader_states.c
|
18
|
-
ext/smartcard_pcsc/pcsc_surrogate_reader.h
|
19
|
-
ext/smartcard_pcsc/pcsc_surrogate_wintypes.h
|
20
7
|
lib/smartcard.rb
|
21
8
|
lib/smartcard/gp/asn1_ber.rb
|
22
9
|
lib/smartcard/gp/cap_loader.rb
|
@@ -30,7 +17,15 @@ lib/smartcard/iso/jcop_remote_server.rb
|
|
30
17
|
lib/smartcard/iso/jcop_remote_transport.rb
|
31
18
|
lib/smartcard/iso/pcsc_transport.rb
|
32
19
|
lib/smartcard/iso/transport.rb
|
20
|
+
lib/smartcard/pcsc/card.rb
|
21
|
+
lib/smartcard/pcsc/context.rb
|
22
|
+
lib/smartcard/pcsc/ffi_autogen.rb
|
23
|
+
lib/smartcard/pcsc/ffi_functions.rb
|
24
|
+
lib/smartcard/pcsc/ffi_lib.rb
|
25
|
+
lib/smartcard/pcsc/ffi_structs.rb
|
33
26
|
lib/smartcard/pcsc/pcsc_exception.rb
|
27
|
+
lib/smartcard/pcsc/reader_state_queries.rb
|
28
|
+
tasks/ffi_codegen.rb
|
34
29
|
test/gp/asn1_ber_test.rb
|
35
30
|
test/gp/cap_loader_test.rb
|
36
31
|
test/gp/des_test.rb
|
@@ -42,6 +37,6 @@ test/iso/auto_configurator_test.rb
|
|
42
37
|
test/iso/iso_card_mixin_test.rb
|
43
38
|
test/iso/iso_exception_test.rb
|
44
39
|
test/iso/jcop_remote_test.rb
|
45
|
-
test/pcsc/
|
46
|
-
test/pcsc/
|
47
|
-
|
40
|
+
test/pcsc/card_test.rb
|
41
|
+
test/pcsc/context_test.rb
|
42
|
+
test/pcsc/reader_state_queries_test.rb
|
data/Rakefile
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
gem 'echoe'
|
3
2
|
require 'echoe'
|
4
3
|
|
4
|
+
require 'tasks/ffi_codegen.rb'
|
5
|
+
|
5
6
|
Echoe.new('smartcard') do |p|
|
6
7
|
p.project = 'smartcard' # rubyforge project
|
7
8
|
|
@@ -9,7 +10,8 @@ Echoe.new('smartcard') do |p|
|
|
9
10
|
p.email = 'victor@costan.us'
|
10
11
|
p.summary = 'Interface with ISO 7816 smart cards.'
|
11
12
|
p.url = 'http://www.costan.us/smartcard'
|
12
|
-
p.dependencies = ['
|
13
|
+
p.dependencies = ['ffi >=0.5.3',
|
14
|
+
'rubyzip >=0.9.1',
|
13
15
|
'zerg_support >=0.1.5']
|
14
16
|
p.development_dependencies = ['echoe >=3.2',
|
15
17
|
'flexmock >=0.8.6']
|
@@ -18,21 +20,12 @@ Echoe.new('smartcard') do |p|
|
|
18
20
|
p.need_zip = !Gem.win_platform?
|
19
21
|
p.clean_pattern += ['ext/**/*.manifest', 'ext/**/*_autogen.h']
|
20
22
|
p.rdoc_pattern =
|
21
|
-
/^(lib|bin|tasks|ext)|^BUILD|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
|
22
|
-
|
23
|
-
p.eval = proc do |p|
|
24
|
-
if Gem.win_platform?
|
25
|
-
p.files += ['lib/smartcard/pcsc.so']
|
26
|
-
p.platform = Gem::Platform::CURRENT
|
27
|
-
|
28
|
-
# take out the extension info from the gemspec
|
29
|
-
task :postcompile_hacks => [:compile] do
|
30
|
-
p.extensions.clear
|
31
|
-
end
|
23
|
+
/^(lib|bin|tasks|ext)|^BUILD|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
|
24
|
+
end
|
32
25
|
|
33
|
-
|
34
|
-
|
35
|
-
|
26
|
+
unless FFI::Platform.windows?
|
27
|
+
task :package => :ffi_header
|
28
|
+
task :test => :ffi_header
|
36
29
|
end
|
37
30
|
|
38
31
|
if $0 == __FILE__
|
data/lib/smartcard.rb
CHANGED
@@ -1,6 +1,15 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# :nodoc: namespace
|
2
|
+
module Smartcard
|
3
|
+
end
|
3
4
|
|
5
|
+
require 'smartcard/pcsc/card.rb'
|
6
|
+
require 'smartcard/pcsc/context.rb'
|
7
|
+
require 'smartcard/pcsc/ffi_lib.rb'
|
8
|
+
require 'smartcard/pcsc/ffi_autogen.rb'
|
9
|
+
require 'smartcard/pcsc/ffi_structs.rb'
|
10
|
+
require 'smartcard/pcsc/ffi_functions.rb'
|
11
|
+
require 'smartcard/pcsc/pcsc_exception.rb'
|
12
|
+
require 'smartcard/pcsc/reader_state_queries.rb'
|
4
13
|
|
5
14
|
require 'smartcard/iso/apdu_error.rb'
|
6
15
|
require 'smartcard/iso/iso_card_mixin.rb'
|
@@ -24,7 +24,7 @@ class PcscTransport
|
|
24
24
|
|
25
25
|
def exchange_apdu(apdu)
|
26
26
|
xmit_apdu_string = apdu.pack('C*')
|
27
|
-
result_string = @card.transmit xmit_apdu_string
|
27
|
+
result_string = @card.transmit xmit_apdu_string
|
28
28
|
return result_string.unpack('C*')
|
29
29
|
end
|
30
30
|
|
@@ -33,51 +33,41 @@ class PcscTransport
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def connect
|
36
|
-
@context = PCSC::Context.new
|
36
|
+
@context = PCSC::Context.new if @context.nil?
|
37
37
|
|
38
38
|
if @options[:reader_name]
|
39
39
|
@reader_name = @options[:reader_name]
|
40
40
|
else
|
41
|
-
#
|
42
|
-
readers = @context.
|
41
|
+
# Get the first reader.
|
42
|
+
readers = @context.readers
|
43
43
|
@reader_name = readers[@options[:reader_index] || 0]
|
44
44
|
end
|
45
45
|
|
46
|
-
#
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
@context.
|
51
|
-
|
46
|
+
# Query the reader's status.
|
47
|
+
queries = PCSC::ReaderStateQueries.new 1
|
48
|
+
queries[0].reader_name = @reader_name
|
49
|
+
queries[0].current_state = :unknown
|
50
|
+
@context.wait_for_status_change queries, 100
|
51
|
+
queries.ack_changes
|
52
52
|
|
53
|
-
#
|
54
|
-
|
55
|
-
puts "Please insert
|
56
|
-
|
57
|
-
@context.
|
58
|
-
|
53
|
+
# Prompt for card insertion unless that already happened.
|
54
|
+
unless queries[0].current_state.include? :present
|
55
|
+
puts "Please insert smart-card card in reader #{@reader_name}\n"
|
56
|
+
until queries[0].current_state.include? :presentt do
|
57
|
+
@context.wait_for_status_change queries
|
58
|
+
queries.ack_changes
|
59
59
|
end
|
60
60
|
puts "Card detected\n"
|
61
61
|
end
|
62
62
|
|
63
|
-
#
|
64
|
-
@card =
|
65
|
-
|
66
|
-
|
67
|
-
# build the transmit / receive IoRequests
|
68
|
-
status = @card.status
|
69
|
-
@xmit_ioreq = @@xmit_iorequest[status[:protocol]]
|
70
|
-
@atr = status[:atr]
|
71
|
-
if RUBY_PLATFORM =~ /win/ and (not RUBY_PLATFORM =~ /darwin/)
|
72
|
-
@recv_ioreq = nil
|
73
|
-
else
|
74
|
-
@recv_ioreq = PCSC::IoRequest.new
|
75
|
-
end
|
63
|
+
# Connect to the card.
|
64
|
+
@card = @context.card @reader_name, :shared
|
65
|
+
@atr = @card.info[:atr]
|
76
66
|
end
|
77
67
|
|
78
68
|
def disconnect
|
79
69
|
unless @card.nil?
|
80
|
-
@card.disconnect
|
70
|
+
@card.disconnect
|
81
71
|
@card = nil
|
82
72
|
@atr = nil
|
83
73
|
end
|
@@ -90,12 +80,7 @@ class PcscTransport
|
|
90
80
|
def to_s
|
91
81
|
"#<PC/SC Terminal: disconnected>" if @card.nil?
|
92
82
|
"#<PC/SC Terminal: #{@reader_name}>"
|
93
|
-
end
|
94
|
-
|
95
|
-
@@xmit_iorequest = {
|
96
|
-
Smartcard::PCSC::PROTOCOL_T0 => Smartcard::PCSC::IOREQUEST_T0,
|
97
|
-
Smartcard::PCSC::PROTOCOL_T1 => Smartcard::PCSC::IOREQUEST_T1,
|
98
|
-
}
|
83
|
+
end
|
99
84
|
end # class PcscTransport
|
100
85
|
|
101
|
-
end # module Smartcard::Iso
|
86
|
+
end # module Smartcard::Iso
|
@@ -0,0 +1,281 @@
|
|
1
|
+
# Connects Ruby to a smart-card in a PC/SC reader (wraps SCARDHANDLE).
|
2
|
+
#
|
3
|
+
# Author:: Victor Costan
|
4
|
+
# Copyright:: Copyright (C) 2009 Massachusetts Institute of Technology
|
5
|
+
# License:: MIT
|
6
|
+
|
7
|
+
require 'set'
|
8
|
+
|
9
|
+
# :nodoc: namespace
|
10
|
+
module Smartcard::PCSC
|
11
|
+
|
12
|
+
|
13
|
+
# Connects a smart-card in a PC/SC reader to the Ruby world.
|
14
|
+
class Card
|
15
|
+
# Establishes a connection to the card in a PC/SC reader.
|
16
|
+
#
|
17
|
+
# The first connection will power up the card and perform a reset on it.
|
18
|
+
#
|
19
|
+
# Args:
|
20
|
+
# context:: the Smartcard::PCSC::Context for the PC/SC resource manager
|
21
|
+
# reader_name:: friendly name of the reader to connect to; reader names can
|
22
|
+
# be obtained from Smartcard::PCSC::Context#readers
|
23
|
+
# sharing_mode:: whether a shared or exclusive lock will be requested on the
|
24
|
+
# reader; the possible values are +:shared+, +:exclusive+ and
|
25
|
+
# +:direct+ (see the SCARD_SHARE_ constants in the PC/SC API)
|
26
|
+
# preferred_protocols:: the desired communication protocol; the possible
|
27
|
+
# values are +:t0+, +:t1+, +:t15+, +:raw+, and +:any+
|
28
|
+
# (see the SCARD_PROTOCOL_ constants in the PC/SC API)
|
29
|
+
def initialize(context, reader_name, sharing_mode = :exclusive,
|
30
|
+
preferred_protocols = :any)
|
31
|
+
handle_ptr = FFILib::WordPtr.new
|
32
|
+
protocol_ptr = FFILib::WordPtr.new
|
33
|
+
status = FFILib.card_connect context._handle, reader_name, sharing_mode,
|
34
|
+
preferred_protocols, handle_ptr, protocol_ptr
|
35
|
+
raise Smartcard::PCSC::Exception, status unless status == :success
|
36
|
+
|
37
|
+
@context = context
|
38
|
+
set_protocol FFILib::Protocol[protocol_ptr[:value]]
|
39
|
+
@_handle = handle_ptr[:value]
|
40
|
+
end
|
41
|
+
|
42
|
+
# Updates internal buffers to reflect a change in the communication protocol.
|
43
|
+
def set_protocol(protocol)
|
44
|
+
@protocol = protocol
|
45
|
+
|
46
|
+
case protocol
|
47
|
+
when :t0
|
48
|
+
@send_pci = @recv_pci = FFILib::PCI_T0
|
49
|
+
when :t1
|
50
|
+
@send_pci = @recv_pci = FFILib::PCI_T1
|
51
|
+
when :raw
|
52
|
+
@send_pci = @recv_pci = FFILib::PCI_RAW
|
53
|
+
end
|
54
|
+
|
55
|
+
# Windows really doesn't like a receiving IoRequest.
|
56
|
+
if FFI::Platform.windows? || FFI::Platform.mac?
|
57
|
+
@recv_pci = nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
private :set_protocol
|
61
|
+
|
62
|
+
# Reconnects to the smart-card, potentially using a different protocol.
|
63
|
+
#
|
64
|
+
# Args:
|
65
|
+
# sharing_mode:: whether a shared or exclusive lock will be requested on the
|
66
|
+
# reader; the possible values are +:shared+, +:exclusive+ and
|
67
|
+
# +:direct+ (see the SCARD_SHARE_ constants in the PC/SC API)
|
68
|
+
# preferred_protocols:: the desired communication protocol; the possible
|
69
|
+
# values are +:t0+, +:t1+, +:t15+, +:raw+, and +:any+
|
70
|
+
# (see the SCARD_PROTOCOL_ constants in the PC/SC API)
|
71
|
+
# disposition:: what to do with the smart-card right before disconnecting;
|
72
|
+
# the possible values are +:leave+, +:reset+, +:unpower+, and
|
73
|
+
# +:eject+ (see the SCARD_*_CARD constants in the PC/SC API)
|
74
|
+
def reconnect(sharing_mode = :exclusive, preferred_protocols = :any,
|
75
|
+
disposition = :leave)
|
76
|
+
protocol_ptr = FFILib::WordPtr.new
|
77
|
+
status = FFILib.card_reconnect @_handle, sharing_mode,
|
78
|
+
preferred_protocols, disposition, protocol_ptr
|
79
|
+
raise Smartcard::PCSC::Exception, status unless status == :success
|
80
|
+
|
81
|
+
set_protocol FFILib::Protocol[protocol_ptr[:value]]
|
82
|
+
end
|
83
|
+
|
84
|
+
# Disconnects from the smart-card.
|
85
|
+
#
|
86
|
+
# Future method calls on this object will raise PC/SC errors.
|
87
|
+
#
|
88
|
+
# Args:
|
89
|
+
# disposition:: what to do with the smart-card right before disconnecting;
|
90
|
+
# the possible values are +:leave+, +:reset+, +:unpower+, and
|
91
|
+
# +:eject+ (see the SCARD_*_CARD constants in the PC/SC API)
|
92
|
+
def disconnect(disposition = :leave)
|
93
|
+
status = FFILib.card_disconnect @_handle, disposition
|
94
|
+
raise Smartcard::PCSC::Exception, status unless status == :success
|
95
|
+
|
96
|
+
@_handle = nil
|
97
|
+
@protocol = nil
|
98
|
+
end
|
99
|
+
|
100
|
+
# Starts a transaction, obtaining an exclusive lock on the smart-card.
|
101
|
+
def begin_transaction
|
102
|
+
status = FFILib.begin_transaction @_handle
|
103
|
+
raise Smartcard::PCSC::Exception, status unless status == :success
|
104
|
+
end
|
105
|
+
|
106
|
+
# Ends a transaction started with begin_transaction.
|
107
|
+
#
|
108
|
+
# The calling application must be the owner of the previously started
|
109
|
+
# transaction or an error will occur.
|
110
|
+
#
|
111
|
+
# Args:
|
112
|
+
# disposition:: what to do with the smart-card after the transaction; the
|
113
|
+
# possible values are +:leave+, +:reset+, +:unpower+, and
|
114
|
+
# +:eject+ (see the SCARD_*_CARD constants in the PC/SC API)
|
115
|
+
def end_transaction(disposition = :leave)
|
116
|
+
status = FFILib.end_transaction @_handle, disposition
|
117
|
+
raise Smartcard::PCSC::Exception, status unless status == :success
|
118
|
+
end
|
119
|
+
|
120
|
+
# Performs a block inside a transaction, with an exclusive smart-card lock.
|
121
|
+
#
|
122
|
+
# Args:
|
123
|
+
# disposition:: what to do with the smart-card after the transaction; the
|
124
|
+
# possible values are +:leave+, +:reset+, +:unpower+, and
|
125
|
+
# +:eject+ (see the SCARD_*_CARD constants in the PC/SC API)
|
126
|
+
def transaction(disposition = :leave)
|
127
|
+
begin_transaction
|
128
|
+
yield
|
129
|
+
end_transaction disposition
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
def [](attribute_name)
|
134
|
+
length_ptr = FFILib::WordPtr.new
|
135
|
+
status = FFILib.get_attrib @_handle, attribute_name, nil, length_ptr
|
136
|
+
raise Smartcard::PCSC::Exception, status unless status == :success
|
137
|
+
|
138
|
+
value_ptr = FFI::MemoryPointer.new :char, length_ptr[:value]
|
139
|
+
begin
|
140
|
+
status = FFILib.get_attrib @_handle, attribute_name, value_ptr,
|
141
|
+
length_ptr
|
142
|
+
raise Smartcard::PCSC::Exception, status unless status == :success
|
143
|
+
|
144
|
+
value_ptr.get_bytes 0, length_ptr[:value]
|
145
|
+
ensure
|
146
|
+
value_ptr.free
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Sets the value of an attribute in the interface driver.
|
151
|
+
#
|
152
|
+
# The interface driver may not implement all possible attributes.
|
153
|
+
#
|
154
|
+
# Args:
|
155
|
+
# attribute_name:: the attribute to be set; possible values are the members
|
156
|
+
# of Smartcard::PCSC::FFILib::Attribute, for example
|
157
|
+
# +:vendor_name+)
|
158
|
+
# value:: string containing the value bytes to be assigned to the attribute
|
159
|
+
def []=(attribute_name, value)
|
160
|
+
value_ptr = FFI::MemoryPointer.from_string value
|
161
|
+
begin
|
162
|
+
status = FFILib.set_attrib @_handle, attribute_name, value_ptr,
|
163
|
+
value.length
|
164
|
+
raise Smartcard::PCSC::Exception, status unless status == :success
|
165
|
+
value
|
166
|
+
ensure
|
167
|
+
value_ptr.free
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Sends an APDU to the smart card, and returns the card's response.
|
172
|
+
#
|
173
|
+
# Args:
|
174
|
+
# send_data:: string containing the APDU bytes to be sent to the card
|
175
|
+
# receive_buffer_size: the maximum number of bytes that can be received
|
176
|
+
def transmit(data, receive_buffer_size = 65546)
|
177
|
+
send_ptr = FFI::MemoryPointer.from_string data
|
178
|
+
recv_ptr = FFI::MemoryPointer.new receive_buffer_size
|
179
|
+
recv_size_ptr = FFILib::WordPtr.new
|
180
|
+
recv_size_ptr[:value] = receive_buffer_size
|
181
|
+
begin
|
182
|
+
status = FFILib.transmit @_handle, @send_pci, send_ptr, data.length,
|
183
|
+
@recv_pci, recv_ptr, recv_size_ptr
|
184
|
+
raise Smartcard::PCSC::Exception, status unless status == :success
|
185
|
+
recv_ptr.get_bytes 0, recv_size_ptr[:value]
|
186
|
+
ensure
|
187
|
+
send_ptr.free
|
188
|
+
recv_ptr.free
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# Sends a interface driver command for the smart-card reader.
|
193
|
+
#
|
194
|
+
# This method is useful for creating client side reader drivers for functions
|
195
|
+
# like PIN pads, biometrics, or other smart card reader extensions that are
|
196
|
+
# not normally handled by the PC/SC API.
|
197
|
+
#
|
198
|
+
# Args:
|
199
|
+
# code:: control code for the operation; a driver-specific integer
|
200
|
+
# data:: string containing the data bytes to be sent to the driver
|
201
|
+
# receive_buffer_size:: the maximum number of bytes that can be received
|
202
|
+
#
|
203
|
+
# Returns a string containg the response bytes.
|
204
|
+
def control(code, data, receive_buffer_size = 4096)
|
205
|
+
# NOTE: In general, I tried to avoid specifying receive buffer sizes. This
|
206
|
+
# is the only case where that is impossible to achieve, because there
|
207
|
+
# is no well-known maximum buffer size, and the SCardControl call is
|
208
|
+
# not guaranteed to be idempotent, so it's not OK to re-issue it after
|
209
|
+
# guessing a buffer size works out.
|
210
|
+
send_ptr = FFI::MemoryPointer.from_string data
|
211
|
+
recv_ptr = FFI::MemoryPointer.new receive_buffer_size
|
212
|
+
recv_size_ptr = FFILib::WordPtr.new
|
213
|
+
recv_size_ptr[:value] = receive_buffer_size
|
214
|
+
begin
|
215
|
+
status = FFILib.card_control @_handle, code, send_ptr, data.length,
|
216
|
+
recv_ptr, receive_buffer_size, recv_size_ptr
|
217
|
+
raise Smartcard::PCSC::Exception, status unless status == :success
|
218
|
+
recv_ptr.get_bytes 0, recv_size_ptr[:value]
|
219
|
+
ensure
|
220
|
+
send_ptr.free
|
221
|
+
recv_ptr.free
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Assorted information about this smart-card.
|
226
|
+
#
|
227
|
+
# Returns a hash with the following keys:
|
228
|
+
# :state:: reader/card status, as a Set of symbols; the possible values are
|
229
|
+
# +:present+, +:swallowed+, +:absent+, +:specific+, and +:powered+
|
230
|
+
# (see the SCARD_* constants in the PC/SC API)
|
231
|
+
# :protocol:: the protocol established with the card
|
232
|
+
# :atr:: the card's ATR bytes, wrapped in a string
|
233
|
+
# :reader_names:: array of strings containing all the names of the reader
|
234
|
+
# connected to this smart-card
|
235
|
+
def info
|
236
|
+
readers_length_ptr = FFILib::WordPtr.new
|
237
|
+
state_ptr = FFILib::WordPtr.new
|
238
|
+
protocol_ptr = FFILib::WordPtr.new
|
239
|
+
atr_ptr = FFI::MemoryPointer.new FFILib::Consts::MAX_ATR_SIZE
|
240
|
+
atr_length_ptr = FFILib::WordPtr.new
|
241
|
+
atr_length_ptr[:value] = FFILib::Consts::MAX_ATR_SIZE
|
242
|
+
|
243
|
+
begin
|
244
|
+
status = FFILib.card_status @_handle, nil, readers_length_ptr, state_ptr,
|
245
|
+
protocol_ptr, atr_ptr, atr_length_ptr
|
246
|
+
raise Smartcard::PCSC::Exception, status unless status == :success
|
247
|
+
|
248
|
+
readers_ptr = FFI::MemoryPointer.new :char, readers_length_ptr[:value]
|
249
|
+
begin
|
250
|
+
status = FFILib.card_status @_handle, readers_ptr, readers_length_ptr,
|
251
|
+
state_ptr, protocol_ptr, atr_ptr, atr_length_ptr
|
252
|
+
raise Smartcard::PCSC::Exception, status unless status == :success
|
253
|
+
|
254
|
+
state_word = state_ptr[:value]
|
255
|
+
state = Set.new
|
256
|
+
FFILib::CardState.to_h.each do |key, mask|
|
257
|
+
state << key if (state_word & mask) == mask && mask != 0
|
258
|
+
end
|
259
|
+
|
260
|
+
{ :readers => Context.decode_multi_string(readers_ptr),
|
261
|
+
:protocol => FFILib::Protocol[protocol_ptr[:value]],
|
262
|
+
:atr => atr_ptr.get_bytes(0, atr_length_ptr[:value]),
|
263
|
+
:state => state }
|
264
|
+
ensure
|
265
|
+
readers_ptr.free
|
266
|
+
end
|
267
|
+
ensure
|
268
|
+
atr_ptr.free
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
# The low-level _SCARDHANDLE_ data.
|
273
|
+
#
|
274
|
+
# This should not be used by client code.
|
275
|
+
attr_reader :_handle
|
276
|
+
|
277
|
+
# The communication protocol in use with this smart-card.
|
278
|
+
attr_reader :protocol
|
279
|
+
end # class Smartcard::PCSC::Card
|
280
|
+
|
281
|
+
end # namespace Smartcard::PCSC
|