common methods
client.connect reprogrammed max message size can be negociated
This commit is contained in:
parent
e4e1009554
commit
86675204cb
|
@ -2,10 +2,11 @@
|
|||
# it implements the state machine for the client
|
||||
require 'common'
|
||||
|
||||
# this is an bastract class
|
||||
# to implement : connect,disconnect,reset,atr,apdu
|
||||
# this is an abstract class
|
||||
# TODO : verify state before sending, respect max size
|
||||
class Client < SAP
|
||||
|
||||
# make the class abstract
|
||||
private :initialize
|
||||
|
||||
def initialize(io)
|
||||
|
@ -21,8 +22,11 @@ class Client < SAP
|
|||
end
|
||||
|
||||
def start
|
||||
connect()
|
||||
super
|
||||
# start the client in another thread
|
||||
thread = Thread.new do
|
||||
super
|
||||
end
|
||||
thread.abort_on_exception = true
|
||||
end
|
||||
|
||||
def state_machine(message)
|
||||
|
@ -31,47 +35,27 @@ class Client < SAP
|
|||
case message[:name]
|
||||
when "CONNECT_RESP"
|
||||
connection_status = message[:payload][0][:value][0]
|
||||
max_msg_size = (message[:payload][1][:value][0]<<8)+message[:payload][1][:value][1]
|
||||
# print response
|
||||
if message[:payload].size == 1 then
|
||||
log("client","connection : #{SAP::CONNECTION_STATUS[connection_status]}",3)
|
||||
else
|
||||
@max_msg_size = (message[:payload][1][:value][0]<<8)+message[:payload][1][:value][1]
|
||||
log("client","connection : #{SAP::CONNECTION_STATUS[connection_status]} (max message size = #{@max_msg_size})",3)
|
||||
end
|
||||
# verify response
|
||||
if connection_status==0 and message[:payload].size==2 then # OK
|
||||
if connection_status==0x00 and message[:payload].size==2 then
|
||||
# OK, Server can fulfill requirements
|
||||
log("client","connection to server succeded",3)
|
||||
set_state :idle
|
||||
elsif connection_status==0x02 and message[:payload].size==2 then
|
||||
# Error, Server does not support maximum message size
|
||||
log("client","server can not handle size. adapting",3)
|
||||
@max_msg_size = max_msg_size
|
||||
set_state :not_connected
|
||||
else
|
||||
set_state :not_connected
|
||||
raise "connection error"
|
||||
end
|
||||
when "STATUS_IND"
|
||||
|
||||
# save status change
|
||||
@status = message[:parameters][0][:value][0]
|
||||
@verbose.puts "new status : #{SAP::STATUS_CHANGE[@status]}"
|
||||
@state = :idle
|
||||
|
||||
# if SIM reseted, ask for ATR
|
||||
if @status == SAP::STATUS_CHANGE.index("Card reset") then
|
||||
get_atr
|
||||
end
|
||||
|
||||
when "ERROR_RESP"
|
||||
case @state
|
||||
when :connection_under_negociation
|
||||
@case = :not_connected
|
||||
else
|
||||
@state = :idle
|
||||
end
|
||||
when "TRANSFER_ATR_RESP"
|
||||
result = message[:parameters][0][:value][0]
|
||||
@verbose.puts SAP::RESULT_CODE[result]
|
||||
if result == SAP::RESULT_CODE.index("OK, request processed correctly") then
|
||||
atr = message[:parameters][1][:value]
|
||||
@verbose.puts atr.collect{|x| x.to_s(16).rjust(2,'0')}*' '
|
||||
end
|
||||
@state = :idle
|
||||
else
|
||||
raise "not implemented or unknown message type : #{message[:name]}"
|
||||
end
|
||||
|
@ -80,14 +64,21 @@ class Client < SAP
|
|||
# send CONNECT_REQ
|
||||
def connect
|
||||
log("client","connecting",3)
|
||||
if @state == :not_connected then
|
||||
connect = create_message("CONNECT_REQ",[["MaxMsgSize",[(@max_msg_size>>8)&0xff,@max_msg_size&0xff]]])
|
||||
send(connect)
|
||||
@state = :connection_under_negociation
|
||||
else
|
||||
@io.close
|
||||
raise "can not connect. required state : not_connected, current state : #{@state}"
|
||||
until @state==:idle do
|
||||
if @state == :not_connected then
|
||||
payload = []
|
||||
# ["MaxMsgSize",[size]]
|
||||
payload << [0x00,[(@max_msg_size>>8)&0xff,@max_msg_size&0xff]]
|
||||
connect = create_message("CONNECT_REQ",payload)
|
||||
send(connect)
|
||||
set_state :connection_under_negociation
|
||||
elsif @state!=:connection_under_negociation
|
||||
raise "can not connect. required state : not_connected, current state : #{@state}"
|
||||
return false
|
||||
end
|
||||
sleep 0.1
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
# send TRANSFER_ATR_REQ
|
||||
|
|
|
@ -8,11 +8,19 @@ require 'stringio'
|
|||
# - message creation
|
||||
# - message parsing
|
||||
# - sending and recieving messages
|
||||
# to implement :
|
||||
# - state machine (client/server)
|
||||
# - connect,disconnect,reset,atr,apdu
|
||||
|
||||
# should be an abstract class
|
||||
# SAP common architecture
|
||||
class SAP
|
||||
|
||||
# make the class abstract
|
||||
private :initialize
|
||||
|
||||
#===============
|
||||
#== constants ==
|
||||
#===============
|
||||
|
||||
# SAP table 5.16
|
||||
CONNECTION_STATUS = { 0x00 => "OK, Server can fulfill requirements",
|
||||
|
@ -123,8 +131,9 @@ class SAP
|
|||
add_message_type("SET_TRANSPORT_PROTOCOL_REQ",true,0x13,[[0x09,true]])
|
||||
add_message_type("SET_TRANSPORT_PROTOCOL_RESP",false,0x14,[[0x02,true]])
|
||||
|
||||
# message format (hash)
|
||||
# message type + :payload = (array of paramerter type + :value)
|
||||
#=================
|
||||
#== SAP methods ==
|
||||
#=================
|
||||
|
||||
# create a new SAP client/server
|
||||
# - io : the Input/Output to monitor
|
||||
|
@ -170,12 +179,61 @@ class SAP
|
|||
end
|
||||
end
|
||||
|
||||
def set_state (new_state)
|
||||
if @state then
|
||||
log("state","state changed from #{@state} to #{new_state}",2)
|
||||
else
|
||||
log("state","state set to #{new_state}",2)
|
||||
end
|
||||
@state = new_state
|
||||
end
|
||||
|
||||
#==================
|
||||
#== to implement ==
|
||||
#==================
|
||||
|
||||
# the state machine that has to be implemented
|
||||
# SAP figure 4.13
|
||||
def state_machine(message)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
# client : connect to SAP server
|
||||
# server : connect to SIM
|
||||
# return : successfull connection
|
||||
def connect
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
# client : disconnect from SAP server
|
||||
# server : disconnect from SIM card
|
||||
# return : successfull disconnection
|
||||
def disconnect
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
# disconnect synonime
|
||||
def close
|
||||
return disconnect
|
||||
end
|
||||
|
||||
def atr
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def apdu(apdu)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
# methods not so important yet : reset, power_on, power_off, transfer_protocol
|
||||
|
||||
#==============
|
||||
#== messages ==
|
||||
#==============
|
||||
|
||||
# message format (hash)
|
||||
# message type + :payload = (array of paramerter type + :value)
|
||||
|
||||
# create a message
|
||||
# - type : message id or name
|
||||
# - payload : array [parameter id or name, content]
|
||||
|
@ -321,9 +379,9 @@ class SAP
|
|||
|
||||
end
|
||||
|
||||
#=========
|
||||
#== LOG ==
|
||||
#=========
|
||||
#=========
|
||||
#== log ==
|
||||
#=========
|
||||
|
||||
# verbosity
|
||||
# - 0 : nothing
|
||||
|
@ -342,14 +400,9 @@ class SAP
|
|||
end
|
||||
end
|
||||
|
||||
def set_state (new_state)
|
||||
if @state then
|
||||
log("state","state changed from #{@state} to #{new_state}",2)
|
||||
else
|
||||
log("state","state set to #{new_state}",2)
|
||||
end
|
||||
@state = new_state
|
||||
end
|
||||
#===========
|
||||
#== utils ==
|
||||
#===========
|
||||
|
||||
# return hex representation
|
||||
def hex(data)
|
||||
|
|
|
@ -11,4 +11,5 @@ end
|
|||
# demo application, using TCP socket
|
||||
io = TCPSocket.open("localhost",1234)
|
||||
client = DemoClient.new(io)
|
||||
client.start
|
||||
client.start
|
||||
client.connect
|
|
@ -1,12 +1,13 @@
|
|||
# this is the server part of the SAP
|
||||
# it implements the state machine for the server
|
||||
# this is an abstract class
|
||||
require 'common'
|
||||
|
||||
# this is an bastract class
|
||||
# to implement : connect,disconnect,reset,atr,apdu
|
||||
# TODO : respect max message size (SAP chapter 4.1.1)
|
||||
# TODO : respect max message size
|
||||
class Server < SAP
|
||||
|
||||
# make the class abstract
|
||||
private :initialize
|
||||
|
||||
# create the SAP server
|
||||
|
@ -38,15 +39,36 @@ class Server < SAP
|
|||
# get client max message size
|
||||
max_msg_size = (message[:payload][0][:value][0]<<8)+message[:payload][0][:value][1]
|
||||
log("server","incoming connection request (max message size = #{max_msg_size})",3)
|
||||
# commection response message
|
||||
payload = []
|
||||
payload << ["ConnectionStatus",[0x00]]
|
||||
payload << ["MaxMsgSize",message[:payload][0][:value]]
|
||||
response = create_message("CONNECT_RESP",payload)
|
||||
# send response
|
||||
send(response)
|
||||
set_state :idle
|
||||
log("server","connection established",3)
|
||||
# negociate MaxMsgSize
|
||||
if max_msg_size>@max_msg_size then
|
||||
# send my max size
|
||||
# connection response message
|
||||
payload = []
|
||||
# ["ConnectionStatus",["Error, Server does not support maximum message size"]]
|
||||
payload << [0x01,[0x02]]
|
||||
# ["MaxMsgSize",[size]]
|
||||
payload << [0x00,[@max_msg_size>>8,@max_msg_size&0xff]]
|
||||
# send response
|
||||
response = create_message("CONNECT_RESP",payload)
|
||||
send(response)
|
||||
set_state :not_connected
|
||||
log("server","connection refused",3)
|
||||
else
|
||||
# accept the value
|
||||
@max_msg_size = max_msg_size
|
||||
# connection response message
|
||||
payload = []
|
||||
# ["ConnectionStatus",["OK, Server can fulfill requirements"]]
|
||||
payload << [0x01,[0x00]]
|
||||
# ["MaxMsgSize",[size]]
|
||||
payload << [0x00,[@max_msg_size>>8,@max_msg_size&0xff]]
|
||||
# send response
|
||||
response = create_message("CONNECT_RESP",payload)
|
||||
send(response)
|
||||
set_state :idle
|
||||
log("server","connection established",3)
|
||||
end
|
||||
|
||||
when "STATUS_IND"
|
||||
else
|
||||
raise "not implemented or unknown message type : #{message[:name]}"
|
||||
|
|
Loading…
Reference in New Issue