Class: CTAPI::Cardterminal

Inherits:
Object
  • Object
show all
Includes:
CTAPI
Defined in:
lib/ctapi.rb

Overview

Represents a cardterminal. Mixes in CTAPI methods and constants.

Defined Under Namespace

Classes: Manufacturer

Constant Summary collapse

LOCK =

Global lock for managment of cardterminals.

Sync.new

Constants included from CTAPI

VERSION, VERSION_ARRAY, VERSION_BUILD, VERSION_MAJOR, VERSION_MINOR

Constants included from CTAPICore

CTAPICore::CT, CTAPICore::CTBCS_CLA, CTAPICore::CTBCS_DATA_STATUS_CARD, CTAPICore::CTBCS_DATA_STATUS_CARD_CONNECT, CTAPICore::CTBCS_DATA_STATUS_NOCARD, CTAPICore::CTBCS_INS_EJECT, CTAPICore::CTBCS_INS_REQUEST, CTAPICore::CTBCS_INS_RESET, CTAPICore::CTBCS_INS_STATUS, CTAPICore::CTBCS_MIN_COMMAND_SIZE, CTAPICore::CTBCS_MIN_RESPONSE_SIZE, CTAPICore::CTBCS_P1_CT_KERNEL, CTAPICore::CTBCS_P1_DISPLAY, CTAPICore::CTBCS_P1_INTERFACE1, CTAPICore::CTBCS_P1_INTERFACE10, CTAPICore::CTBCS_P1_INTERFACE11, CTAPICore::CTBCS_P1_INTERFACE12, CTAPICore::CTBCS_P1_INTERFACE13, CTAPICore::CTBCS_P1_INTERFACE14, CTAPICore::CTBCS_P1_INTERFACE2, CTAPICore::CTBCS_P1_INTERFACE3, CTAPICore::CTBCS_P1_INTERFACE4, CTAPICore::CTBCS_P1_INTERFACE5, CTAPICore::CTBCS_P1_INTERFACE6, CTAPICore::CTBCS_P1_INTERFACE7, CTAPICore::CTBCS_P1_INTERFACE8, CTAPICore::CTBCS_P1_INTERFACE9, CTAPICore::CTBCS_P1_KEYPAD, CTAPICore::CTBCS_P2_REQUEST_GET_ATR, CTAPICore::CTBCS_P2_REQUEST_GET_HIST, CTAPICore::CTBCS_P2_REQUEST_NO_RESP, CTAPICore::CTBCS_P2_RESET_GET_ATR, CTAPICore::CTBCS_P2_RESET_GET_HIST, CTAPICore::CTBCS_P2_RESET_NO_RESP, CTAPICore::CTBCS_P2_STATUS_ICC, CTAPICore::CTBCS_P2_STATUS_MANUFACTURER, CTAPICore::CTBCS_SW1_COMMAND_NOT_ALLOWED, CTAPICore::CTBCS_SW1_EJECT_NOT_REMOVED, CTAPICore::CTBCS_SW1_EJECT_OK, CTAPICore::CTBCS_SW1_EJECT_REMOVED, CTAPICore::CTBCS_SW1_ICC_ERROR, CTAPICore::CTBCS_SW1_OK, CTAPICore::CTBCS_SW1_REQUEST_ASYNC_OK, CTAPICore::CTBCS_SW1_REQUEST_CARD_PRESENT, CTAPICore::CTBCS_SW1_REQUEST_ERROR, CTAPICore::CTBCS_SW1_REQUEST_NO_CARD, CTAPICore::CTBCS_SW1_REQUEST_SYNC_OK, CTAPICore::CTBCS_SW1_REQUEST_TIMER_ERROR, CTAPICore::CTBCS_SW1_RESET_ASYNC_OK, CTAPICore::CTBCS_SW1_RESET_CT_OK, CTAPICore::CTBCS_SW1_RESET_ERROR, CTAPICore::CTBCS_SW1_RESET_SYNC_OK, CTAPICore::CTBCS_SW1_WRONG_CLA, CTAPICore::CTBCS_SW1_WRONG_INS, CTAPICore::CTBCS_SW1_WRONG_LENGTH, CTAPICore::CTBCS_SW1_WRONG_PARAM, CTAPICore::CTBCS_SW2_COMMAND_NOT_ALLOWED, CTAPICore::CTBCS_SW2_EJECT_NOT_REMOVED, CTAPICore::CTBCS_SW2_EJECT_OK, CTAPICore::CTBCS_SW2_EJECT_REMOVED, CTAPICore::CTBCS_SW2_ICC_ERROR, CTAPICore::CTBCS_SW2_OK, CTAPICore::CTBCS_SW2_REQUEST_ASYNC_OK, CTAPICore::CTBCS_SW2_REQUEST_CARD_PRESENT, CTAPICore::CTBCS_SW2_REQUEST_ERROR, CTAPICore::CTBCS_SW2_REQUEST_NO_CARD, CTAPICore::CTBCS_SW2_REQUEST_SYNC_OK, CTAPICore::CTBCS_SW2_REQUEST_TIMER_ERROR, CTAPICore::CTBCS_SW2_RESET_ASYNC_OK, CTAPICore::CTBCS_SW2_RESET_CT_OK, CTAPICore::CTBCS_SW2_RESET_ERROR, CTAPICore::CTBCS_SW2_RESET_SYNC_OK, CTAPICore::CTBCS_SW2_WRONG_CLA, CTAPICore::CTBCS_SW2_WRONG_INS, CTAPICore::CTBCS_SW2_WRONG_LENGTH, CTAPICore::CTBCS_SW2_WRONG_PARAM, CTAPICore::ERR_CT, CTAPICore::ERR_HTSI, CTAPICore::ERR_INVALID, CTAPICore::ERR_MEMORY, CTAPICore::ERR_TRANS, CTAPICore::HOST, CTAPICore::ICC1, CTAPICore::ICC10, CTAPICore::ICC11, CTAPICore::ICC12, CTAPICore::ICC13, CTAPICore::ICC14, CTAPICore::ICC2, CTAPICore::ICC3, CTAPICore::ICC4, CTAPICore::ICC5, CTAPICore::ICC6, CTAPICore::ICC7, CTAPICore::ICC8, CTAPICore::ICC9, CTAPICore::MAX_APDULEN, CTAPICore::OK, CTAPICore::PORT_COM1, CTAPICore::PORT_COM2, CTAPICore::PORT_COM3, CTAPICore::PORT_COM4, CTAPICore::PORT_LPT1, CTAPICore::PORT_LPT2, CTAPICore::PORT_Modem, CTAPICore::PORT_Printer

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from CTAPICore

ct_close, ct_data, ct_init

Constructor Details

#initialize(interface, number = nil) ⇒ Cardterminal

Creates a new Cardterminal object for this interface and initializes the card terminal that is connected. If no cardterminal number is given, a number (that is not currently in use) is assigned to this object. Otherwise this object is forced to use the given card terminal number. BTW: number has to be >= 0.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/ctapi.rb', line 31

def initialize(interface, number = nil)
  LOCK.synchronize do
    @interface = interface
    @slot = ICC1
    @chunk_size = 255
    if number
      @number = number
    else
      @number = 0
      while cardterminals.key?(@number)
        @number += 1
      end
    end
    ct_init(@number, interface)
    cardterminals[@number] = true
    @manufacturer = get_manufacturer
    reset
    select_file # master file
  end
end

Instance Attribute Details

#cardObject (readonly)

Card object of the inserted chipcard.



97
98
99
# File 'lib/ctapi.rb', line 97

def card
  @card
end

#chunk_sizeObject

The maximum size of the chunks of data that are read from/written to the card. Can be set by calling the chunk_size= method.



83
84
85
# File 'lib/ctapi.rb', line 83

def chunk_size
  @chunk_size
end

#interfaceObject (readonly)

The interface this cardterminal is connected to. This is a value of PORT_COM1, PORT_COM2, PORT_COM3, PORT_COM4, PORT_LPT1, PORT_LPT2, PORT_Modem or PORT_Printer.



74
75
76
# File 'lib/ctapi.rb', line 74

def interface
  @interface
end

#manufacturerObject (readonly)

The Manufacturer object for this cardterminal.



94
95
96
# File 'lib/ctapi.rb', line 94

def manufacturer
  @manufacturer
end

#numberObject (readonly)

The cardterminal number assigned to this object.



69
70
71
# File 'lib/ctapi.rb', line 69

def number
  @number
end

#slotObject

The slot of the card terminal where the card is inserted. This is a value in the range of ICC1, ICC2,…, ICC14. Default is ICC1.



79
80
81
# File 'lib/ctapi.rb', line 79

def slot
  @slot
end

Class Method Details

.cardterminalsObject

Returns the mapping of cardterminal numbers to Cardterminal instances as a hash.



17
18
19
# File 'lib/ctapi.rb', line 17

def self.cardterminals
  @cardterminals ||= {}
end

.open(interface, number = nil) ⇒ Object

Takes a block and yields to an initialized Cardterminal object. After the block has been called, the cardterminal is closed again.



102
103
104
105
106
107
# File 'lib/ctapi.rb', line 102

def self.open(interface, number = nil)
  cardterminal = self.new(interface, cardterminal)
  yield cardterminal
ensure
  cardterminal.close if cardterminal
end

Instance Method Details

#card_inserted?Boolean

Returns true if a card is inserted in the cardterminal at the moment, false if the cardterminal is empty.

Returns:

  • (Boolean)


339
340
341
342
# File 'lib/ctapi.rb', line 339

def card_inserted?
  cs = card_status
  cs == :card || cs == :card_connect ? true : false
end

#card_statusObject

Returns the card status as symbol: :no_card, :card, :card_connect.



325
326
327
328
329
330
331
332
333
334
335
# File 'lib/ctapi.rb', line 325

def card_status
  response = request_card_status
  case response[0]
  when CTBCS_DATA_STATUS_NOCARD
    :no_card
  when CTBCS_DATA_STATUS_CARD
    :card
  when CTBCS_DATA_STATUS_CARD_CONNECT
    :card_connect
  end
end

#cardterminalsObject

Convenience method to access Cardterminal.cardterminals.



22
23
24
# File 'lib/ctapi.rb', line 22

def cardterminals
  self.class.cardterminals
end

#change_pin(old_pin, new_pin) ⇒ Object

The pin of this card is changed from old_pin to new_pin. They can be given as strings or arrays of characters. A true result is returned if the sending was successful.



256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/ctapi.rb', line 256

def change_pin(old_pin, new_pin)
  old_pin = old_pin.unpack("C*") if old_pin.is_a? String
  new_pin = new_pin.unpack("C*") if new_pin.is_a? String
  data = old_pin + new_pin
  unless data.size <= 255
    raise ArgumentError.new("size of old and new pin must be <= 255")
  end
  change_pin = [ 0, 0x24, 0, 0, data.size ] + data
  response = data(@slot, HOST, change_pin)
  response.successful? or return
  true
end

#closeObject

Terminates the communication with the cardterminal associated with this object and releases the assigned cardterminal number.



134
135
136
137
138
139
# File 'lib/ctapi.rb', line 134

def close
  LOCK.synchronize do
    ct_close(@number)
    cardterminals.delete @number
  end
end

#data(dad, sad, *commands) ⇒ Object

Sends a sequence of commands to the card or the cardterminal (depending on destination/dad and source address/sad) and returns the response (or responses) to the calling program. A command can be given as an array of bytes [ 0x12, 0x23 ] or as a string of the form ‘12:23:a4’ or ‘12 23 a4’ or as a Command object.



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/ctapi.rb', line 114

def data(dad, sad, *commands)
  responses = []
  commands.each do |command|
    command =
      case command
      when String then Command.from_string(command)
      when Array then Command.from_array(command)
      else command
      end
    $DEBUG and debug(2, command)
    data = ct_data(@number, dad, sad, command.data)
    response = Response.new(data)
    $DEBUG and debug(1, response)
    responses << response
  end
  return *responses
end

#eject_iccObject

Sends the eject card byte sequence to the card terminal and returns the Response object. This method is called by the Cardterminal#reset method.



308
309
310
311
# File 'lib/ctapi.rb', line 308

def eject_icc
  eject = [ 0x20, 0x15, 0x01, 0x00, 0x00 ]
  data(CT, HOST, eject)
end

#enter_pin(pin) ⇒ Object

The pin is sent to the card. It can be given as a string or an array of characters. A true result is returned if the sending was successful.



242
243
244
245
246
247
248
249
250
251
# File 'lib/ctapi.rb', line 242

def enter_pin(pin)
  unless pin.size <= 255
    raise ArgumentError.new("size of pin must be <= 255")
  end
  pin = pin.unpack("C*") if pin.is_a? String
  enter_pin = [ 0, 0x20, 0, 0, pin.size ] + pin
  response = data(@slot, HOST, enter_pin)
  response.successful? or return
  true
end

#read(address = 0, size = nil) ⇒ Object

Attempts to read data of length size starting at address. If size is nil, an attempt is made to read the whole card memory.



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/ctapi.rb', line 168

def read(address = 0, size = nil)
  if size == nil
    if @card
      size = @card.memory_size - address
    else
      size = chunk_size
    end
  elsif @card and address + size > @card.memory_size
    size = @card.memory_size - address
  end
  return if size <= 0
  data = ''
  caught = catch(:break) do
    while size >= chunk_size
      d = read_chunk(address, chunk_size)
      if d
        data << d
        address += chunk_size
        size -= chunk_size
      else
        break :break  
      end
    end
  end
  if caught != :break and size > 0
    d = read_chunk(address, size) and data << d
  end
  data
end

#request_card_statusObject

Requests the card status from the cardterminal. Returns the Response object or nil if the response wasn’t successful. This method is called by the card_inserted? method to find out if a card is inserted into the terminal.



273
274
275
276
277
278
# File 'lib/ctapi.rb', line 273

def request_card_status
  get_card_status = [ CTBCS_CLA, CTBCS_INS_STATUS,
    CTBCS_P1_CT_KERNEL, CTBCS_P2_STATUS_ICC, 0 ]
  response = data(CT, HOST, get_card_status)
  response.successful? ? response : nil
end

#request_iccObject

Sends a byte sequence to the card to get the answer to reset Response object. This method is called by the Cardterminal#reset method.



283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/ctapi.rb', line 283

def request_icc
    get_atr = [ CTBCS_CLA, CTBCS_INS_REQUEST, CTBCS_P1_INTERFACE1,
           CTBCS_P2_REQUEST_GET_ATR, 0 ]
  @card_old = @card if @card
  @card, atr = nil, nil
  begin
    if card_inserted?
      atr = data(CT, HOST, get_atr)
      if atr
        if atr.not_changed?
          @card = @card_old
          return @card.atr
        end
        @card = Card.new(atr)
      end
    end
  rescue CTAPIError => e
    STDERR.puts "Caught: #{e}."
  end
  @card.atr
end

#resetObject

The cardterminal is reset by first calling eject_icc and then request_icc. If the reset was successful the Response object of request_icc is returned, otherwise a nontrue value is returned.



317
318
319
320
321
322
# File 'lib/ctapi.rb', line 317

def reset
  eject_icc
  response = request_icc or return
  response.successful? or return
  response
end

#select_file(fid = [ 0x3f, 0 ]) ⇒ Object

Sends the select file byte sequence to the card with a default value for the master file. Returns true if the response was successful, false otherwise.



144
145
146
147
148
# File 'lib/ctapi.rb', line 144

def select_file(fid = [ 0x3f, 0 ])
  select_file = [ 0, 0xa4, 0, 0, 0x02, *fid ]
  response = data(@slot, HOST, select_file)
  response.successful?
end

#write(data, address = 0) ⇒ Object

Attempts to write the string data to the card starting at address. On success returns a true value.



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/ctapi.rb', line 216

def write(data, address = 0)
  size = data.size
  if @card and address + size > @card.memory_size
    size = @card.memory_size - address
  end
  return if size <= 0
  offset = 0
  caught = catch(:break) do
    while size >= chunk_size
      write_chunk(address, data[offset, chunk_size]) or break :break
      address += chunk_size
      offset += chunk_size
      size -= chunk_size
    end
  end
  if caught == :break
    return false
  elsif size > 0
    write_chunk(address, data[offset, size])
  end
  true
end