10
0
Fork 0

common methods

client.connect reprogrammed
max message size can be negociated
This commit is contained in:
kevredon 2011-02-04 22:35:13 +01:00
parent e4e1009554
commit 86675204cb
4 changed files with 133 additions and 66 deletions

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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]}"