135 lines
4.8 KiB
Ruby
135 lines
4.8 KiB
Ruby
# this is the server part of the SAP
|
|
# it implements the state machine for the server
|
|
# this is an abstract class
|
|
require 'lib/common'
|
|
|
|
# this is an bastract class
|
|
# TODO (not implemented) :
|
|
# - respect max message size (and check minimum)
|
|
# - refuse connection if card is missing
|
|
# - server initiated disconnect (when programm want to exit or card is lost)
|
|
# - transport protocol change
|
|
# - power sim on/off or reset
|
|
# - ERROR_RESP sending (instead of exception)
|
|
class Server < SAP
|
|
|
|
# make the class abstract
|
|
private :initialize
|
|
|
|
# create the SAP server
|
|
|
|
def initialize(io)
|
|
super(io)
|
|
|
|
# for the server inifinite loop
|
|
@end = false
|
|
# state of the state machine
|
|
@state = nil
|
|
|
|
# open socket to listien to
|
|
log("server","created",3)
|
|
|
|
# initiate the state machine (connect_req)
|
|
set_state :not_connected
|
|
@max_msg_size = 0xffff
|
|
|
|
end
|
|
|
|
# implementing state machine (SAP figure 4.13)
|
|
def state_machine(message)
|
|
# check direction
|
|
raise "got server to client message" unless message[:client_to_server]
|
|
case message[:name]
|
|
when "CONNECT_REQ"
|
|
raise "msg #{message[:name]} in wrong state #{@state}" unless @state==:not_connected
|
|
set_state :connection_under_nogiciation
|
|
# 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)
|
|
# 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)
|
|
# connect is responsible to send the status
|
|
connect()
|
|
end
|
|
when "DISCONNECT_REQ"
|
|
raise "msg #{message[:name]} in wrong state #{@state}" unless @state!=:not_connected and @state!=:connection_under_nogiciation
|
|
log("server","client disconneting",3)
|
|
response = create_message("DISCONNECT_RESP")
|
|
send(response)
|
|
set_state :not_connected
|
|
when "TRANSFER_ATR_REQ"
|
|
raise "msg #{message[:name]} in wrong state #{@state}" unless @state==:idle
|
|
set_state :processing_atr_request
|
|
# atr should return ATR byte array, or error result code
|
|
atr_result = atr
|
|
payload = []
|
|
if atr_result.kind_of?(Array) then
|
|
# ["ResultCode",["OK, request processed correctly"]]
|
|
payload << [0x02,[0x00]]
|
|
# ["ATR",atr]
|
|
payload << [0x06,atr_result]
|
|
elsif atr_result.kind_of?(Integer) then
|
|
# ["ResultCode",[code from atr]]
|
|
payload << [0x02,[atr_result]]
|
|
else
|
|
raise "unexpected answer #{atr_result.class} from atr"
|
|
end
|
|
# send response
|
|
response = create_message("TRANSFER_ATR_RESP",payload)
|
|
send(response)
|
|
set_state :idle
|
|
when "TRANSFER_APDU_REQ"
|
|
raise "msg #{message[:name]} in wrong state #{@state}" unless @state==:idle
|
|
set_state :processing_apdu_request
|
|
# apdu should return APDU response byte array, or error result code
|
|
raise "no APDU request in message" unless message[:payload].size==1
|
|
apdu_result = apdu(message[:payload][0][:value])
|
|
payload = []
|
|
if apdu_result.kind_of?(Array) then
|
|
# ["ResultCode",["OK, request processed correctly"]]
|
|
payload << [0x02,[0x00]]
|
|
# ["ResponseAPDU",apdu]
|
|
payload << [0x05,apdu_result]
|
|
elsif atr_result.kind_of?(Integer) then
|
|
# ["ResultCode",[code from atr]]
|
|
payload << [0x02,[apdu_result]]
|
|
else
|
|
raise "unexpected answer #{atr_result.class} from atr"
|
|
end
|
|
# send response
|
|
response = create_message("TRANSFER_APDU_RESP",payload)
|
|
send(response)
|
|
set_state :idle
|
|
else
|
|
raise "not implemented or unknown message type : #{message[:name]}"
|
|
end
|
|
end
|
|
|
|
end
|