10
0
Fork 0
softsim/src/sap/server.rb

166 lines
5.9 KiB
Ruby

# encoding: UTF-8
=begin
This file is part of softSIM.
softSIM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
softSIM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with sofSIM. If not, see <http://www.gnu.org/licenses/>.
Copyright (C) 2011 Kevin "tsaitgaist" Redon kevredon@mail.tsaitgaist.info
=end
# this is the server part of the SAP
# it implements the state machine for the server
# this is an abstract class
require_relative '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)
# try to connect until connected
until connect() do
# ["StatusChange",["Card not accessible"]]
status = create_message("STATUS_IND",[[0x08,[0x02]]])
send(status)
sleep 1
end
# card ready
# ["StatusChange",["Card reset"]]
status = create_message("STATUS_IND",[[0x08,[0x01]]])
send(status)
log("server","card ready",3)
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)
disconnect()
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, nil if not available
atr_result = atr
payload = []
if atr_result then
log("ATR","#{hex(atr_result)}",1)
# ["ResultCode",["OK, request processed correctly"]]
payload << [0x02,[0x00]]
# ["ATR",atr]
payload << [0x06,atr_result]
else
# ["ResultCode",["Error, data not available"]]
payload << [0x02,[0x06]]
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
log("APDU","> #{hex(message[:payload][0][:value])}",1)
apdu_result = apdu(message[:payload][0][:value])
log("APDU","< #{hex(apdu_result)}",1)
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