Skip to content

Commit

Permalink
Fixed crash in MRI 1.9.3.
Browse files Browse the repository at this point in the history
  • Loading branch information
pwnall committed Dec 18, 2012
1 parent 8da9140 commit 4ff2b0a
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 257 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Original file line Diff line number Diff line change
@@ -1,4 +1,6 @@
.loadpath .loadpath
*.sw*

Makefile Makefile
doc doc
pkg pkg
10 changes: 10 additions & 0 deletions BUILD
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -75,3 +75,13 @@ Ubuntu (tested on 8.04, 8.10, 9.04 and 9.10):
* libpcsclite-dev (depends on libpcsclite or libpcsclite1) * libpcsclite-dev (depends on libpcsclite or libpcsclite1)
* pcscd * pcscd
* pcsc-tools * pcsc-tools

=== Fedora

The following packages should do the trick on Fedora (tested on 18):
* ruby-devel
* pcsc-lite
* pcsc-lite-ccid
* pcsc-lite-devel
* pcsc-lite-openct
* pcsc-tools
6 changes: 4 additions & 2 deletions CHANGELOG
Original file line number Original file line Diff line number Diff line change
@@ -1,3 +1,5 @@
v0.5.5. Fixed crash in MRI 1.9.3.

v0.5.4. Fixed ReaderStateQuery to work when the status has bits not defined in the PCSC header. v0.5.4. Fixed ReaderStateQuery to work when the status has bits not defined in the PCSC header.


v0.5.3. Fixed buggy protocol number decoding logic. v0.5.3. Fixed buggy protocol number decoding logic.
Expand Down Expand Up @@ -38,7 +40,7 @@ v0.3.1. Fixed documentation for the new PcscException class.


v0.3.0. Added PcscException class used to wrap PC/SC exceptions. v0.3.0. Added PcscException class used to wrap PC/SC exceptions.


v0.2.3. Fixed minor bug in error string for PCSC::Card.transmit v0.2.3. Fixed minor bug in error string for PCSC::Card.transmit


v0.2.2. Fixed APDU exchange bugs on Windows v0.2.2. Fixed APDU exchange bugs on Windows
Restructured PCSC::IoRequest to allow PCI_ consts Restructured PCSC::IoRequest to allow PCI_ consts
Expand All @@ -51,7 +53,7 @@ v0.2.1. Added OSX Tiger support


v0.2.0. Added automatic builds v0.2.0. Added automatic builds
Rakefile for auto builds using echoe Rakefile for auto builds using echoe
extconf.rb: hack to fix Windows makefiles extconf.rb: hack to fix Windows makefiles


v0.1.2. Added Windows compatibility v0.1.2. Added Windows compatibility
*.c: restructured code so VC2005 likes it *.c: restructured code so VC2005 likes it
Expand Down
18 changes: 9 additions & 9 deletions Rakefile
Original file line number Original file line Diff line number Diff line change
@@ -1,26 +1,26 @@
require 'rubygems' require 'rubygems'
require 'echoe' require 'echoe'


require './tasks/ffi_codegen.rb' require './tasks/ffi_codegen.rb'


Echoe.new('smartcard') do |p| Echoe.new('smartcard') do |p|
p.project = 'smartcard' # rubyforge project p.project = 'smartcard' # rubyforge project

p.author = 'Victor Costan' p.author = 'Victor Costan'
p.email = 'victor@costan.us' p.email = 'victor@costan.us'
p.summary = 'Interface with ISO 7816 smart cards.' p.summary = 'Interface with ISO 7816 smart cards.'
p.url = 'http://www.costan.us/smartcard' p.url = 'http://www.costan.us/smartcard'
p.dependencies = ['ffi >=0.5.3', p.dependencies = ['ffi >=1.2.0',
'rubyzip >=0.9.1', 'rubyzip >=0.9.9',
'zerg_support >=0.1.5'] 'zerg_support >=0.1.6']
p.development_dependencies = ['echoe >=3.2', p.development_dependencies = ['echoe >=4.6.3',
'flexmock >=0.8.6'] 'flexmock >=1.2.0']

p.need_tar_gz = !Gem.win_platform? p.need_tar_gz = !Gem.win_platform?
p.need_zip = !Gem.win_platform? p.need_zip = !Gem.win_platform?
p.clean_pattern += ['ext/**/*.manifest', 'ext/**/*_autogen.h'] p.clean_pattern += ['ext/**/*.manifest', 'ext/**/*_autogen.h']
p.rdoc_pattern = p.rdoc_pattern =
/^(lib|bin|tasks|ext)|^BUILD|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/ /^(lib|bin|tasks|ext)|^BUILD|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
end end


file 'lib/smartcard/pcsc/ffi_autogen.rb' => 'tasks/ffi_codegen.rb' do file 'lib/smartcard/pcsc/ffi_autogen.rb' => 'tasks/ffi_codegen.rb' do
Expand Down
106 changes: 51 additions & 55 deletions lib/smartcard/pcsc/card.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@


# :nodoc: namespace # :nodoc: namespace
module Smartcard::PCSC module Smartcard::PCSC


# Connects a smart-card in a PC/SC reader to the Ruby world. # Connects a smart-card in a PC/SC reader to the Ruby world.
class Card class Card
# Establishes a connection to the card in a PC/SC reader. # Establishes a connection to the card in a PC/SC reader.
Expand All @@ -19,10 +19,10 @@ class Card
# Args: # Args:
# context:: the Smartcard::PCSC::Context for the PC/SC resource manager # context:: the Smartcard::PCSC::Context for the PC/SC resource manager
# reader_name:: friendly name of the reader to connect to; reader names can # reader_name:: friendly name of the reader to connect to; reader names can
# be obtained from Smartcard::PCSC::Context#readers # be obtained from Smartcard::PCSC::Context#readers
# sharing_mode:: whether a shared or exclusive lock will be requested on the # sharing_mode:: whether a shared or exclusive lock will be requested on the
# reader; the possible values are +:shared+, +:exclusive+ and # reader; the possible values are +:shared+, +:exclusive+ and
# +:direct+ (see the SCARD_SHARE_ constants in the PC/SC API) # +:direct+ (see the SCARD_SHARE_ constants in the PC/SC API)
# preferred_protocols:: the desired communication protocol; the possible # preferred_protocols:: the desired communication protocol; the possible
# values are +:t0+, +:t1+, +:t15+, +:raw+, and +:any+ # values are +:t0+, +:t1+, +:t15+, +:raw+, and +:any+
# (see the SCARD_PROTOCOL_ constants in the PC/SC API) # (see the SCARD_PROTOCOL_ constants in the PC/SC API)
Expand All @@ -33,50 +33,46 @@ def initialize(context, reader_name, sharing_mode = :exclusive,
status = FFILib.card_connect context._handle, reader_name, sharing_mode, status = FFILib.card_connect context._handle, reader_name, sharing_mode,
preferred_protocols, handle_ptr, protocol_ptr preferred_protocols, handle_ptr, protocol_ptr
raise Smartcard::PCSC::Exception, status unless status == :success raise Smartcard::PCSC::Exception, status unless status == :success

@context = context @context = context
@sharing_mode = sharing_mode @sharing_mode = sharing_mode
@_handle = handle_ptr[:value] @_handle = handle_ptr[:value]
set_protocol FFILib::PROTOCOLS[protocol_ptr[:value]] set_protocol FFILib::PROTOCOLS[protocol_ptr[:value]]
end end

# Updates internal buffers to reflect a change in the communication protocol. # Updates internal buffers to reflect a change in the communication protocol.
# #
# Args: # Args:
# protocol:: the protocol to change to; if invalid, the card's ATR will be # protocol:: the protocol to change to; if invalid, the card's ATR will be
# used to guess a valid protocol # used to guess a valid protocol
def set_protocol(protocol) def set_protocol(protocol)
@protocol = protocol @protocol = protocol

case protocol case protocol
when :t0 when :t0
@send_pci = @recv_pci = FFILib::PCI_T0 @send_pci = FFILib::PCI_T0
when :t1 when :t1
@send_pci = @recv_pci = FFILib::PCI_T1 @send_pci = FFILib::PCI_T1
when :raw when :raw
@send_pci = @recv_pci = FFILib::PCI_RAW @send_pci = FFILib::PCI_RAW
else else
reconnect sharing_mode, guess_protocol_from_atr, :leave reconnect sharing_mode, guess_protocol_from_atr, :leave
return self return self
end

# Windows really doesn't like a receiving IoRequest.
if FFI::Platform.windows? || FFI::Platform.mac?
@recv_pci = nil
end end

self self
end end
private :set_protocol private :set_protocol

# Reconnects to the smart-card, potentially using a different protocol. # Reconnects to the smart-card, potentially using a different protocol.
# #
# Args: # Args:
# sharing_mode:: whether a shared or exclusive lock will be requested on the # sharing_mode:: whether a shared or exclusive lock will be requested on the
# reader; the possible values are +:shared+, +:exclusive+ and # reader; the possible values are +:shared+, +:exclusive+ and
# +:direct+ (see the SCARD_SHARE_ constants in the PC/SC API) # +:direct+ (see the SCARD_SHARE_ constants in the PC/SC API)
# preferred_protocols:: the desired communication protocol; the possible # preferred_protocols:: the desired communication protocol; the possible
# values are +:t0+, +:t1+, +:t15+, +:raw+, and +:any+ # values are +:t0+, +:t1+, +:t15+, +:raw+, and +:any+
# (see the SCARD_PROTOCOL_ constants in the PC/SC API) # (see the SCARD_PROTOCOL_ constants in the PC/SC API)
# disposition:: what to do with the smart-card right before disconnecting; # disposition:: what to do with the smart-card right before disconnecting;
# the possible values are +:leave+, +:reset+, +:unpower+, and # the possible values are +:leave+, +:reset+, +:unpower+, and
# +:eject+ (see the SCARD_*_CARD constants in the PC/SC API) # +:eject+ (see the SCARD_*_CARD constants in the PC/SC API)
Expand All @@ -86,11 +82,11 @@ def reconnect(sharing_mode = :exclusive, preferred_protocols = :any,
status = FFILib.card_reconnect @_handle, sharing_mode, status = FFILib.card_reconnect @_handle, sharing_mode,
preferred_protocols, disposition, protocol_ptr preferred_protocols, disposition, protocol_ptr
raise Smartcard::PCSC::Exception, status unless status == :success raise Smartcard::PCSC::Exception, status unless status == :success

@sharing_mode = sharing_mode @sharing_mode = sharing_mode
set_protocol FFILib::Protocol[protocol_ptr[:value]] set_protocol FFILib::Protocol[protocol_ptr[:value]]
end end

# Disconnects from the smart-card. # Disconnects from the smart-card.
# #
# Future method calls on this object will raise PC/SC errors. # Future method calls on this object will raise PC/SC errors.
Expand All @@ -110,9 +106,9 @@ def disconnect(disposition = :leave)
# Starts a transaction, obtaining an exclusive lock on the smart-card. # Starts a transaction, obtaining an exclusive lock on the smart-card.
def begin_transaction def begin_transaction
status = FFILib.begin_transaction @_handle status = FFILib.begin_transaction @_handle
raise Smartcard::PCSC::Exception, status unless status == :success raise Smartcard::PCSC::Exception, status unless status == :success
end end

# Ends a transaction started with begin_transaction. # Ends a transaction started with begin_transaction.
# #
# The calling application must be the owner of the previously started # The calling application must be the owner of the previously started
Expand All @@ -126,7 +122,7 @@ def end_transaction(disposition = :leave)
status = FFILib.end_transaction @_handle, disposition status = FFILib.end_transaction @_handle, disposition
raise Smartcard::PCSC::Exception, status unless status == :success raise Smartcard::PCSC::Exception, status unless status == :success
end end

# Performs a block inside a transaction, with an exclusive smart-card lock. # Performs a block inside a transaction, with an exclusive smart-card lock.
# #
# Args: # Args:
Expand All @@ -138,8 +134,8 @@ def transaction(disposition = :leave)
yield yield
end_transaction disposition end_transaction disposition
end end


def [](attribute_name) def [](attribute_name)
length_ptr = FFILib::WordPtr.new length_ptr = FFILib::WordPtr.new
status = FFILib.get_attrib @_handle, attribute_name, nil, length_ptr status = FFILib.get_attrib @_handle, attribute_name, nil, length_ptr
Expand All @@ -150,13 +146,13 @@ def [](attribute_name)
status = FFILib.get_attrib @_handle, attribute_name, value_ptr, status = FFILib.get_attrib @_handle, attribute_name, value_ptr,
length_ptr length_ptr
raise Smartcard::PCSC::Exception, status unless status == :success raise Smartcard::PCSC::Exception, status unless status == :success

value_ptr.get_bytes 0, length_ptr[:value] value_ptr.get_bytes 0, length_ptr[:value]
ensure ensure
value_ptr.free value_ptr.free
end end
end end

# Sets the value of an attribute in the interface driver. # Sets the value of an attribute in the interface driver.
# #
# The interface driver may not implement all possible attributes. # The interface driver may not implement all possible attributes.
Expand All @@ -171,34 +167,34 @@ def []=(attribute_name, value)
begin begin
status = FFILib.set_attrib @_handle, attribute_name, value_ptr, status = FFILib.set_attrib @_handle, attribute_name, value_ptr,
value.length value.length
raise Smartcard::PCSC::Exception, status unless status == :success raise Smartcard::PCSC::Exception, status unless status == :success
value value
ensure ensure
value_ptr.free value_ptr.free
end end
end end

# Sends an APDU to the smart card, and returns the card's response. # Sends an APDU to the smart card, and returns the card's response.
# #
# Args: # Args:
# send_data:: string containing the APDU bytes to be sent to the card # send_data:: string containing the APDU bytes to be sent to the card
# receive_buffer_size: the maximum number of bytes that can be received # receive_buffer_size: the maximum number of bytes that can be received
def transmit(data, receive_buffer_size = 65546) def transmit(data, receive_buffer_size = 65546)
send_ptr = FFI::MemoryPointer.from_string data send_ptr = FFI::MemoryPointer.from_string data
recv_ptr = FFI::MemoryPointer.new receive_buffer_size recv_ptr = FFI::MemoryPointer.new receive_buffer_size
recv_size_ptr = FFILib::WordPtr.new recv_size_ptr = FFILib::WordPtr.new
recv_size_ptr[:value] = receive_buffer_size recv_size_ptr[:value] = receive_buffer_size
begin begin
status = FFILib.transmit @_handle, @send_pci, send_ptr, data.length, status = FFILib.transmit @_handle, @send_pci, send_ptr, data.length,
@recv_pci, recv_ptr, recv_size_ptr nil, recv_ptr, recv_size_ptr
raise Smartcard::PCSC::Exception, status unless status == :success raise Smartcard::PCSC::Exception, status unless status == :success
recv_ptr.get_bytes 0, recv_size_ptr[:value] recv_ptr.get_bytes 0, recv_size_ptr[:value]
ensure ensure
send_ptr.free send_ptr.free
recv_ptr.free recv_ptr.free
end end
end end

# Sends a interface driver command for the smart-card reader. # Sends a interface driver command for the smart-card reader.
# #
# This method is useful for creating client side reader drivers for functions # This method is useful for creating client side reader drivers for functions
Expand All @@ -220,20 +216,20 @@ def control(code, data, receive_buffer_size = 4096)
send_ptr = FFI::MemoryPointer.from_string data send_ptr = FFI::MemoryPointer.from_string data
recv_ptr = FFI::MemoryPointer.new receive_buffer_size recv_ptr = FFI::MemoryPointer.new receive_buffer_size
recv_size_ptr = FFILib::WordPtr.new recv_size_ptr = FFILib::WordPtr.new
recv_size_ptr[:value] = receive_buffer_size recv_size_ptr[:value] = receive_buffer_size
begin begin
status = FFILib.card_control @_handle, code, send_ptr, data.length, status = FFILib.card_control @_handle, code, send_ptr, data.length,
recv_ptr, receive_buffer_size, recv_size_ptr recv_ptr, receive_buffer_size, recv_size_ptr
raise Smartcard::PCSC::Exception, status unless status == :success raise Smartcard::PCSC::Exception, status unless status == :success
recv_ptr.get_bytes 0, recv_size_ptr[:value] recv_ptr.get_bytes 0, recv_size_ptr[:value]
ensure ensure
send_ptr.free send_ptr.free
recv_ptr.free recv_ptr.free
end end
end end

# Assorted information about this smart-card. # Assorted information about this smart-card.
# #
# Returns a hash with the following keys: # Returns a hash with the following keys:
# :state:: reader/card status, as a Set of symbols; the possible values are # :state:: reader/card status, as a Set of symbols; the possible values are
# +:present+, +:swallowed+, +:absent+, +:specific+, and +:powered+ # +:present+, +:swallowed+, +:absent+, +:specific+, and +:powered+
Expand All @@ -248,25 +244,25 @@ def info
protocol_ptr = FFILib::WordPtr.new protocol_ptr = FFILib::WordPtr.new
atr_ptr = FFI::MemoryPointer.new FFILib::Consts::MAX_ATR_SIZE atr_ptr = FFI::MemoryPointer.new FFILib::Consts::MAX_ATR_SIZE
atr_length_ptr = FFILib::WordPtr.new atr_length_ptr = FFILib::WordPtr.new
atr_length_ptr[:value] = FFILib::Consts::MAX_ATR_SIZE atr_length_ptr[:value] = FFILib::Consts::MAX_ATR_SIZE


begin begin
status = FFILib.card_status @_handle, nil, readers_length_ptr, state_ptr, status = FFILib.card_status @_handle, nil, readers_length_ptr, state_ptr,
protocol_ptr, atr_ptr, atr_length_ptr protocol_ptr, atr_ptr, atr_length_ptr
raise Smartcard::PCSC::Exception, status unless status == :success raise Smartcard::PCSC::Exception, status unless status == :success


readers_ptr = FFI::MemoryPointer.new :char, readers_length_ptr[:value] readers_ptr = FFI::MemoryPointer.new :char, readers_length_ptr[:value]
begin begin
status = FFILib.card_status @_handle, readers_ptr, readers_length_ptr, status = FFILib.card_status @_handle, readers_ptr, readers_length_ptr,
state_ptr, protocol_ptr, atr_ptr, atr_length_ptr state_ptr, protocol_ptr, atr_ptr, atr_length_ptr
raise Smartcard::PCSC::Exception, status unless status == :success raise Smartcard::PCSC::Exception, status unless status == :success

state_word = state_ptr[:value] state_word = state_ptr[:value]
state = Set.new state = Set.new
FFILib::CardState.to_h.each do |key, mask| FFILib::CardState.to_h.each do |key, mask|
state << key if (state_word & mask) == mask && mask != 0 state << key if (state_word & mask) == mask && mask != 0
end end

{ :readers => Context.decode_multi_string(readers_ptr), { :readers => Context.decode_multi_string(readers_ptr),
:protocol => FFILib::PROTOCOLS[protocol_ptr[:value]], :protocol => FFILib::PROTOCOLS[protocol_ptr[:value]],
:atr => atr_ptr.get_bytes(0, atr_length_ptr[:value]), :atr => atr_ptr.get_bytes(0, atr_length_ptr[:value]),
Expand All @@ -278,11 +274,11 @@ def info
atr_ptr.free atr_ptr.free
end end
end end

# Returns the first valid protocol listed in the card's ATR. # Returns the first valid protocol listed in the card's ATR.
def guess_protocol_from_atr def guess_protocol_from_atr
atr = info[:atr] atr = info[:atr]

# NOTE: inspired from the following sources: # NOTE: inspired from the following sources:
# http://en.wikipedia.org/wiki/Answer_to_reset # http://en.wikipedia.org/wiki/Answer_to_reset
# http://www.atmel.com/dyn/resources/prod_documents/doc5025.pdf # http://www.atmel.com/dyn/resources/prod_documents/doc5025.pdf
Expand All @@ -295,13 +291,13 @@ def guess_protocol_from_atr
next_bits = (0...4).map { |j| (next_nibble >> j) & 1 } next_bits = (0...4).map { |j| (next_nibble >> j) & 1 }
next_bits.each { |bit| i += bit } next_bits.each { |bit| i += bit }
if next_bits[3] == 1 # TD byte was last, i is right on it if next_bits[3] == 1 # TD byte was last, i is right on it
protocols << (atr_bytes[i] & 0x0f) protocols << (atr_bytes[i] & 0x0f)
else else
break # No TD, no more bytes break # No TD, no more bytes
end end
end end


protocols.sort! protocols.sort!
case protocols[0] case protocols[0]
when 0 when 0
:t0 :t0
Expand All @@ -312,15 +308,15 @@ def guess_protocol_from_atr
end end
end end
private :guess_protocol_from_atr private :guess_protocol_from_atr

# The low-level _SCARDHANDLE_ data. # The low-level _SCARDHANDLE_ data.
# #
# This should not be used by client code. # This should not be used by client code.
attr_reader :_handle attr_reader :_handle

# The communication protocol in use with this smart-card. # The communication protocol in use with this smart-card.
attr_reader :protocol attr_reader :protocol

# The sharing mode for this smart-card session. (:shared or :exclusive) # The sharing mode for this smart-card session. (:shared or :exclusive)
attr_reader :sharing_mode attr_reader :sharing_mode
end # class Smartcard::PCSC::Card end # class Smartcard::PCSC::Card
Expand Down
Loading

0 comments on commit 4ff2b0a

Please sign in to comment.