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