From 863a4a26e52d01593e162063dd338ceffbf350c6 Mon Sep 17 00:00:00 2001 From: Kevin Redon Date: Mon, 2 May 2011 11:39:55 +0200 Subject: [PATCH] restructured files organisation --- lib/sim_server.rb | 67 --------- nbproject/project.properties | 7 - nbproject/project.xml | 16 --- {lib => src}/apdu_client.rb | 2 +- {lib => src}/bluetooth_client.rb | 4 +- {lib => src}/demo_client.rb | 11 +- {lib => src/lib}/client.rb | 4 +- {lib => src/lib}/common.rb | 0 {lib => src/lib}/server.rb | 2 +- {lib => src}/pcsc_server.rb | 10 +- src/sim_server.rb | 234 +++++++++++++++++++++++++++++++ 11 files changed, 252 insertions(+), 105 deletions(-) delete mode 100755 lib/sim_server.rb delete mode 100644 nbproject/project.properties delete mode 100644 nbproject/project.xml rename {lib => src}/apdu_client.rb (97%) rename {lib => src}/bluetooth_client.rb (99%) rename {lib => src}/demo_client.rb (95%) rename {lib => src/lib}/client.rb (99%) rename {lib => src/lib}/common.rb (100%) rename {lib => src/lib}/server.rb (99%) rename {lib => src}/pcsc_server.rb (95%) create mode 100755 src/sim_server.rb diff --git a/lib/sim_server.rb b/lib/sim_server.rb deleted file mode 100755 index a6e2f97..0000000 --- a/lib/sim_server.rb +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env ruby -require 'server' -require 'socket' -require 'xml' - -class String - # convert a hexadecimal string into binary array - def hex2arr - arr = [] - (self.length/2).times do |i| - arr << self[i*2,2].to_i(16) - end - return arr - end -end - -# SAP server using a SIM backup file -class SIMServer < Server - - def initialize(io,path="sim.xml") - super(io) - @xml_path = path - end - - # read file - def connect - - begin - xml = IO.read(@xml_path) - doc = XML::Parser.string(xml) - @card = doc.parse - rescue - puts "can't read #{@xml_path}" - status = create_message("STATUS_IND",[[0x08,[0x02]]]) - send(status) - sleep 1 - redo - end - - # card ready - # ["StatusChange",["Card reset"]] - status = create_message("STATUS_IND",[[0x08,[0x01]]]) - send(status) - log("server","connection established. SIM loaded",3) - end - - # get ATR - def atr - raise "connect to card to get ATR" unless @card - return @card.find_first("/sim")["atr"].hex2arr - end - - # send APDU and get response - def apdu(request) - raise "connect to card to send APDU" unless @card - raise "not implemented" - response = @card.transmit(request.pack('C*')).unpack("C*") - return response - end - -end - -# demo application, using TCP socket -socket = TCPServer.new("localhost",1337) -io = socket.accept -server = SIMServer.new(io) -server.start \ No newline at end of file diff --git a/nbproject/project.properties b/nbproject/project.properties deleted file mode 100644 index 38e19b2..0000000 --- a/nbproject/project.properties +++ /dev/null @@ -1,7 +0,0 @@ -javac.classpath= -main.file=main.rb -platform.active=default -source.encoding=UTF-8 -spec.src.dir=spec -src.dir=lib -test.src.dir=test diff --git a/nbproject/project.xml b/nbproject/project.xml deleted file mode 100644 index bf56132..0000000 --- a/nbproject/project.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - org.netbeans.modules.ruby.rubyproject - - - SAP - - - - - - - - - - diff --git a/lib/apdu_client.rb b/src/apdu_client.rb similarity index 97% rename from lib/apdu_client.rb rename to src/apdu_client.rb index 68042ec..8514bcf 100755 --- a/lib/apdu_client.rb +++ b/src/apdu_client.rb @@ -1,7 +1,7 @@ #!/usr/bin/env ruby # This programm will forward APDU from a TCP port to a SAP server +require 'lib/client' require 'socket' -require 'client' SAP_HOST = "localhost" SAP_PORT = "1337" diff --git a/lib/bluetooth_client.rb b/src/bluetooth_client.rb similarity index 99% rename from lib/bluetooth_client.rb rename to src/bluetooth_client.rb index 0b6290e..bf69a0a 100644 --- a/lib/bluetooth_client.rb +++ b/src/bluetooth_client.rb @@ -1,4 +1,4 @@ -require 'client' # SAP client +require 'lib/client' # SAP client require 'dbus' # libdbus-ruby # class to connect to BT SAP server using BlueZ over dbus @@ -254,4 +254,4 @@ class BluetoothClient @bt_adapter.RemoveDevice(@bt_sap.path) unless @trusted or @paired end -end \ No newline at end of file +end diff --git a/lib/demo_client.rb b/src/demo_client.rb similarity index 95% rename from lib/demo_client.rb rename to src/demo_client.rb index 44b93f5..264ee73 100755 --- a/lib/demo_client.rb +++ b/src/demo_client.rb @@ -1,6 +1,6 @@ #!/usr/bin/env ruby # This programm will create a client which can be used to test servers -require 'client' +require 'lib/client' #================= #== client type == @@ -62,6 +62,11 @@ A38 = [CLASS,0x88,0x00,0x00,0x10] # file address (TS 51.011 10.7, page 105) MF = [0x3F,0x00] + EF_ICCID = [0x2F,0xE2] + DF_GSM = [0x7F,0x20] + EF_IMSI = [0x6F,0x07] # TS 51.011 10.3.2 + DF_TELECOM = [0x7F,0x10] + EF_MSISDN = [0x6F,0x40] # TS 51.011 10.5.5 #========================= #== additionnal methods == @@ -217,8 +222,8 @@ end atr = @client.atr puts atr ? "ATR : #{atr.to_hex_disp}" : "could not get ATR" # select MF -apdu_req = [0xA0,0xA4,0x00,0x00,0x02,0x3F,0x00] -transmit(SELECT+MF) +transmit(GET_RESPONSE+[0x1a]) +select(MF) @client.disconnect # close client_io diff --git a/lib/client.rb b/src/lib/client.rb similarity index 99% rename from lib/client.rb rename to src/lib/client.rb index c6dff7a..2edd815 100644 --- a/lib/client.rb +++ b/src/lib/client.rb @@ -1,6 +1,6 @@ # this is the client part of the SAP # it implements the state machine for the client -require 'common' +require 'lib/common' # this is an abstract class # TODO : @@ -198,4 +198,4 @@ class Client < SAP return nil end end -end \ No newline at end of file +end diff --git a/lib/common.rb b/src/lib/common.rb similarity index 100% rename from lib/common.rb rename to src/lib/common.rb diff --git a/lib/server.rb b/src/lib/server.rb similarity index 99% rename from lib/server.rb rename to src/lib/server.rb index fa043ca..68c073a 100644 --- a/lib/server.rb +++ b/src/lib/server.rb @@ -1,7 +1,7 @@ # this is the server part of the SAP # it implements the state machine for the server # this is an abstract class -require 'common' +require 'lib/common' # this is an bastract class # TODO (not implemented) : diff --git a/lib/pcsc_server.rb b/src/pcsc_server.rb similarity index 95% rename from lib/pcsc_server.rb rename to src/pcsc_server.rb index b38b264..641560e 100755 --- a/lib/pcsc_server.rb +++ b/src/pcsc_server.rb @@ -1,16 +1,14 @@ #!/usr/bin/env ruby -require 'server' +require 'lib/server' require 'socket' +require 'rubygems' +require 'smartcard' =begin need to install sudo aptitude install ruby ruby-dev rubygems sudo aptitude install libpcsclite1 libpcsclite-dev libruby sudo gem install smartcard (http://www.rubygems.org/gems/smartcard) =end -require 'rubygems' -# smartcard 0.5.1 can not handle T=0 because of a FFI::Enum bug -# patch existing -require 'smartcard' # SAP server using PCSC for the card class PCSCServer < Server @@ -98,4 +96,4 @@ end socket = TCPServer.new("localhost",1337) io = socket.accept server = PCSCServer.new(io) -server.start \ No newline at end of file +server.start diff --git a/src/sim_server.rb b/src/sim_server.rb new file mode 100755 index 0000000..ae4826b --- /dev/null +++ b/src/sim_server.rb @@ -0,0 +1,234 @@ +#!/usr/bin/env ruby +require 'server' +require 'socket' +require 'xml' + +# transform binary string into readable hex string +class String + def to_hex_disp + to_return = "" + each_byte do |b| + to_return += b.to_s(16).rjust(2,"0") + to_return += " " + end + return to_return[0..-2].upcase + end + + def to_hex + to_return = "" + each_byte do |b| + to_return += b.to_s(16).rjust(2,"0") + end + #to_return = "0x"+to_return + return to_return.downcase + end + + # convert a hexadecimal string into binary array + def hex2arr + arr = [] + (self.length/2).times do |i| + arr << self[i*2,2].to_i(16) + end + return arr + end +end + +# reverse the nibbles of each byte +class Array + # print the nibbles (often BCD) + # - padding : the 0xf can be ignored (used as padding in BCD) + def nibble_str(padding=false) + # get nibble representation + to_return = collect { |b| (b&0x0F).to_s(16)+(b>>4).to_s(16) } + to_return = to_return.join + # remove the padding + to_return.gsub!('f',"") if padding + return to_return + end + + def to_hex_disp + to_return = "" + each do |b| + to_return += b.to_s(16).rjust(2,"0") + to_return += " " + end + return to_return[0..-2].upcase + end + + def to_hex + to_return = "" + each do |b| + to_return += b.to_s(16).rjust(2,"0") + end + #to_return = "0x"+to_return + return to_return.downcase + end +end + +# SAP server using a SIM backup file +class SIMServer < Server + + def initialize(io,path="sim.xml") + super(io) + @xml_path = path + end + +#==================== +#== main functions == +#==================== + + # read file + def connect + + begin + xml = IO.read(@xml_path) + doc = XML::Parser.string(xml) + @card = doc.parse + rescue + puts "can't read #{@xml_path}" + status = create_message("STATUS_IND",[[0x08,[0x02]]]) + send(status) + sleep 1 + redo + end + + # select MF + node = select([0x3f,0x00]) + @response = node.find_first("./header").content.hex2arr + + # card ready + # ["StatusChange",["Card reset"]] + status = create_message("STATUS_IND",[[0x08,[0x01]]]) + send(status) + log("server","connection established. SIM loaded",3) + end + + # get ATR + def atr + raise "connect to card to get ATR" unless @card + return @card.find_first("/sim")["atr"].hex2arr + end + + # send APDU and get response + def apdu(request) + raise "connect to card to send APDU" unless @card + # check the size + return [0x6f,0x00] unless request.length>=5 + # I can only handle SIM APDU (class A0) + return [0x6e,0x00] unless request[0]==0xa0 + # default + data = [] + sw = [0x6f,0x00] + # the instruction + case request[1] + when 0xa4 # SELECT + # remove the last response + @response = nil + # verify the apdu + if request[2,2]!=[0x00,0x00] then + # incorrect parameter P1 or P2 + sw = [0x6b,0x00] + elsif request[4]!=0x02 then + # incorrect parameter P3 + sw = [0x67,0x02] + else + # is the file ID present ? + if request.length==7 then + # check if the directory can be selected + file_id = request[5,2] + node = select(file_id) + if node then + # file selected, response awaiting + @response = node.find_first("./header").content.hex2arr + sw = [0x9f,@response.length] + log("APDU select","file selected : #{file_id.to_hex_disp}",3) + else + # out of range (invalid address) + sw = [0x94,0x02] + log("APDU select","file not found/accessible : #{file_id.to_hex_disp}",3) + end + else + # out of range (invalid address) + sw = [0x94,0x02] + log("APDU select","file not found/accessible : #{file_id.to_hex_disp}",3) + end + end + when 0xc0 # GET RESPONSE + # verify the apdu + if request[2,2]!=[0x00,0x00] then + # incorrect parameter P1 or P2 + sw = [0x6b,0x00] + elsif !@response then + # technical problem with no diagnostic given + sw = [0x6f,0x00] + elsif request[4]!=@response.length then + # incorrect parameter P3 + sw = [0x67,@response.length] + else + # return the response + data = @response + sw = [0x90,0x00] + end + else # unknown instruction byte + sw = [0x6d,0x00] + end + return data+sw + end + +#=================== +#== SIM functions == +#=================== + + # select file using the file ID + # node representing the file is returned + # nil is return if file does not exist or is unaccessible + def select (id) + response = nil + # get the current location + if @pwd then + # get current directory + dfs = [0x3f,0x5f,0x7f] + if dfs.include? @pwd["id"][0,2].to_i(16) then + current_directory = @pwd + else + current_directory = @pwd.find_first("..") + end + # find file + if result=current_directory.find_first("./file[@id='#{id.to_hex}']") then + # any file which is an immediate child of the current directory + response = result + elsif dfs.include? current_directory["id"][0,2].to_i(16) and result=current_directory.find_first("../file[@id='#{id.to_hex}']") then + # any DF which is an immediate child of the parent of the current DF + response = result + elsif result=current_directory.find_first("..") and result["id"]==id.to_hex then + # the parent of the current directory + response = result + elsif current_directory["id"]==id.to_hex then + # the current DF + response = current_directory + elsif id==[0x3f,0x00] then + # the MF + response = @card.find_first("/sim/file[@id='3f00']") + else + response = nil + end + @pwd = response if response + else + # only MF is accessible + if id==[0x3f,0x00] then + @pwd = @card.find_first("/sim/file[@id='3f00']") + response = @pwd + else + response=nil + end + end + return response + end + +end + +# demo application, using TCP socket +socket = TCPServer.new("localhost",1337) +io = socket.accept +server = SIMServer.new(io) +server.start