costan-tem_ruby 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. data/CHANGELOG +45 -0
  2. data/LICENSE +21 -0
  3. data/Manifest +75 -0
  4. data/README +8 -0
  5. data/Rakefile +23 -0
  6. data/bin/tem_bench +9 -0
  7. data/bin/tem_ca +13 -0
  8. data/bin/tem_irb +11 -0
  9. data/bin/tem_proxy +65 -0
  10. data/bin/tem_stat +35 -0
  11. data/dev_ca/ca_cert.cer +0 -0
  12. data/dev_ca/ca_cert.pem +32 -0
  13. data/dev_ca/ca_key.pem +27 -0
  14. data/dev_ca/config.yml +14 -0
  15. data/lib/tem/_cert.rb +158 -0
  16. data/lib/tem/apdus/buffers.rb +89 -0
  17. data/lib/tem/apdus/keys.rb +64 -0
  18. data/lib/tem/apdus/lifecycle.rb +13 -0
  19. data/lib/tem/apdus/tag.rb +38 -0
  20. data/lib/tem/auto_conf.rb +25 -0
  21. data/lib/tem/builders/abi.rb +482 -0
  22. data/lib/tem/builders/assembler.rb +314 -0
  23. data/lib/tem/builders/crypto.rb +124 -0
  24. data/lib/tem/builders/isa.rb +120 -0
  25. data/lib/tem/ca.rb +114 -0
  26. data/lib/tem/definitions/abi.rb +65 -0
  27. data/lib/tem/definitions/assembler.rb +23 -0
  28. data/lib/tem/definitions/isa.rb +188 -0
  29. data/lib/tem/ecert.rb +77 -0
  30. data/lib/tem/hive.rb +18 -0
  31. data/lib/tem/keys/asymmetric.rb +116 -0
  32. data/lib/tem/keys/key.rb +48 -0
  33. data/lib/tem/keys/symmetric.rb +47 -0
  34. data/lib/tem/sec_exec_error.rb +63 -0
  35. data/lib/tem/seclosures.rb +81 -0
  36. data/lib/tem/secpack.rb +107 -0
  37. data/lib/tem/tem.rb +31 -0
  38. data/lib/tem/toolkit.rb +101 -0
  39. data/lib/tem/transport/auto_configurator.rb +87 -0
  40. data/lib/tem/transport/java_card_mixin.rb +99 -0
  41. data/lib/tem/transport/jcop_remote_protocol.rb +59 -0
  42. data/lib/tem/transport/jcop_remote_server.rb +171 -0
  43. data/lib/tem/transport/jcop_remote_transport.rb +65 -0
  44. data/lib/tem/transport/pcsc_transport.rb +87 -0
  45. data/lib/tem/transport/transport.rb +10 -0
  46. data/lib/tem_ruby.rb +47 -0
  47. data/tem_ruby.gemspec +35 -0
  48. data/test/_test_cert.rb +70 -0
  49. data/test/builders/test_abi_builder.rb +298 -0
  50. data/test/tem_test_case.rb +26 -0
  51. data/test/tem_unit/test_tem_alu.rb +33 -0
  52. data/test/tem_unit/test_tem_bound_secpack.rb +51 -0
  53. data/test/tem_unit/test_tem_branching.rb +56 -0
  54. data/test/tem_unit/test_tem_crypto_asymmetric.rb +123 -0
  55. data/test/tem_unit/test_tem_crypto_hash.rb +35 -0
  56. data/test/tem_unit/test_tem_crypto_pstore.rb +53 -0
  57. data/test/tem_unit/test_tem_crypto_random.rb +25 -0
  58. data/test/tem_unit/test_tem_emit.rb +23 -0
  59. data/test/tem_unit/test_tem_memory.rb +48 -0
  60. data/test/tem_unit/test_tem_memory_compare.rb +65 -0
  61. data/test/tem_unit/test_tem_output.rb +32 -0
  62. data/test/tem_unit/test_tem_yaml_secpack.rb +47 -0
  63. data/test/test_driver.rb +108 -0
  64. data/test/test_exceptions.rb +35 -0
  65. data/test/transport/test_auto_configurator.rb +114 -0
  66. data/test/transport/test_java_card_mixin.rb +90 -0
  67. data/test/transport/test_jcop_remote.rb +82 -0
  68. data/timings/blank_bound_secpack.rb +18 -0
  69. data/timings/blank_sec.rb +14 -0
  70. data/timings/devchip_decrypt.rb +9 -0
  71. data/timings/post_buffer.rb +10 -0
  72. data/timings/simple_apdu.rb +5 -0
  73. data/timings/timings.rb +64 -0
  74. data/timings/vm_perf.rb +140 -0
  75. data/timings/vm_perf_bound.rb +141 -0
  76. metadata +201 -0
data/lib/tem/tem.rb ADDED
@@ -0,0 +1,31 @@
1
+ class Tem::Session
2
+ include Tem::Abi
3
+ include Tem::Apdus::Buffers
4
+ include Tem::Apdus::Keys
5
+ include Tem::Apdus::Lifecycle
6
+ include Tem::Apdus::Tag
7
+
8
+ include Tem::CA
9
+ include Tem::ECert
10
+ include Tem::SeClosures
11
+ include Tem::Toolkit
12
+
13
+ CAPPLET_AID = [0x19, 0x83, 0x12, 0x29, 0x10, 0xBA, 0xBE]
14
+
15
+ attr_reader :transport
16
+
17
+ def initialize(transport)
18
+ @transport = transport
19
+ @transport.select_applet CAPPLET_AID
20
+ end
21
+
22
+ def disconnect
23
+ return unless @transport
24
+ @transport.disconnect
25
+ @transport = nil
26
+ end
27
+
28
+ def tem_secpack_error(response)
29
+ raise "TEM refused the SECpack"
30
+ end
31
+ end
@@ -0,0 +1,101 @@
1
+ module Tem::Toolkit
2
+ def tk_firmware_ver
3
+ tag = get_tag
4
+ return { :major => read_tem_ubyte(tag, 0), :minor => read_tem_ubyte(tag, 1) }
5
+ end
6
+
7
+ def tk_gen_key(type = :asymmetric, authz = nil)
8
+ gen_sec = assemble do |s|
9
+ s.ldbc authz.nil? ? 24 : 4
10
+ s.outnew
11
+ if authz.nil?
12
+ # no authorization given, must generate one
13
+ s.ldbc 20
14
+ s.ldwc :key_auth
15
+ s.dupn :n => 2
16
+ s.rnd
17
+ s.outvb
18
+ end
19
+ s.genkp :type => (type == :asymmetric) ? 0x00 : 0x80
20
+ s.authk :auth => :key_auth
21
+ s.outw
22
+ s.authk :auth => :key_auth
23
+ s.outw
24
+ s.halt
25
+ s.label :key_auth
26
+ if authz.nil?
27
+ s.zeros :tem_ubyte, 20
28
+ else
29
+ s.data :tem_ubyte, authz
30
+ end
31
+ s.stack 4
32
+ end
33
+
34
+ kp_buffer = execute gen_sec
35
+ keys_offset = authz.nil? ? 20 : 0
36
+ k1id = read_tem_ushort kp_buffer, keys_offset
37
+ k2id = read_tem_ushort kp_buffer, keys_offset + 2
38
+ if type == :asymmetric
39
+ return_val = { :pubk_id => k1id, :privk_id => k2id }
40
+ else
41
+ return_val = { :key_id => k1id }
42
+ end
43
+ return { :authz => authz.nil? ? kp_buffer[0...20] : authz }.merge!(return_val)
44
+ end
45
+
46
+ def tk_read_key(key_id, authz)
47
+ read_sec = assemble do |s|
48
+ s.ldbc :const => key_id
49
+ s.authk :auth => :key_auth
50
+ s.ldkl
51
+ s.outnew
52
+ s.ldbc :const => key_id
53
+ s.ldbc(-1)
54
+ s.stk
55
+ s.halt
56
+ s.label :key_auth
57
+ s.data :tem_ubyte, authz
58
+ s.stack 4
59
+ end
60
+
61
+ key_string = execute read_sec
62
+ return read_tem_key(key_string, 0)
63
+ end
64
+
65
+ def tk_delete_key(key_id, authz)
66
+ del_sec = assemble do |s|
67
+ s.ldbc :const => key_id
68
+ s.authk :auth => :key_auth
69
+ s.relk
70
+ s.ldbc :const => 1
71
+ s.outnew
72
+ s.ldbc :const => key_id
73
+ s.outb
74
+ s.halt
75
+ s.label :key_auth
76
+ s.data :tem_ubyte, authz
77
+ s.stack 4
78
+ end
79
+
80
+ execute del_sec
81
+ end
82
+
83
+ def tk_post_key(key, authz)
84
+ post_sec = assemble do |s|
85
+ s.ldbc :const => 1
86
+ s.outnew
87
+ s.ldwc :const => :key_data
88
+ s.rdk
89
+ s.authk :auth => :key_auth
90
+ s.outb
91
+ s.halt
92
+ s.label :key_data
93
+ s.data :tem_ubyte, key.to_tem_key
94
+ s.label :key_auth
95
+ s.data :tem_ubyte, authz
96
+ s.stack 4
97
+ end
98
+ id_string = execute post_sec
99
+ return read_tem_ubyte(id_string, 0)
100
+ end
101
+ end
@@ -0,0 +1,87 @@
1
+ # :nodoc: namespace
2
+ module Tem::Transport
3
+
4
+ # Automatic configuration code.
5
+ module AutoConfigurator
6
+ # The name of the environment variable that might supply the transport
7
+ # configuration.
8
+ ENVIRONMENT_VARIABLE_NAME = 'TEM_PORT'
9
+
10
+ # The default configurations to be tried if no configuration is specified.
11
+ DEFAULT_CONFIGURATIONS = [
12
+ { :class => JcopRemoteTransport,
13
+ :opts => { :host => '127.0.0.1', :port => 8050} },
14
+ { :class => PcscTransport, :opts => { :reader_index => 0 }}
15
+ ]
16
+
17
+ # Creates a transport based on available configuration information.
18
+ def self.auto_transport
19
+ configuration = env_configuration
20
+ return try_transport(configuration) if configuration
21
+
22
+ DEFAULT_CONFIGURATIONS.each do |config|
23
+ transport = try_transport(config)
24
+ return transport if transport
25
+ end
26
+ return nil
27
+ end
28
+
29
+ # Retrieves transport configuration information from an environment variable.
30
+ #
31
+ # :call-seq:
32
+ # AutoConfigurator.env_configuration -> hash
33
+ #
34
+ # The returned configuration has the keys required by
35
+ # AutoConfigurator#try_transport
36
+ def self.env_configuration
37
+ return nil unless conf = ENV[ENVIRONMENT_VARIABLE_NAME]
38
+
39
+ case conf[0]
40
+ when ?:
41
+ # :8050 -- JCOP emulator at port 8050
42
+ transport_class = JcopRemoteTransport
43
+ transport_opts = { :host => '127.0.0.1' }
44
+ transport_opts[:port] = conf[1..-1].to_i
45
+ when ?@
46
+ # @127.0.0.1:8050 -- JCOP emulator at host 127.0.0.1 port 8050
47
+ transport_class = JcopRemoteTransport
48
+ port_index = conf.rindex(?:) || conf.length
49
+ transport_opts = { :host => conf[1...port_index] }
50
+ transport_opts[:port] = conf[(port_index + 1)..-1].to_i
51
+ when ?#
52
+ # #2 -- 2nd PC/SC reader in the system
53
+ transport_class = PcscTransport
54
+ transport_opts = { :reader_index => conf[1..-1].to_i - 1 }
55
+ else
56
+ # Reader Name -- the PC/SC reader with the given name
57
+ transport_class = PcscTransport
58
+ transport_opts = { :reader_name => conf }
59
+ end
60
+
61
+ transport_opts[:port] = 8050 if transport_opts[:port] == 0
62
+ if transport_opts[:reader_index] and transport_opts[:reader_index] < 0
63
+ transport_opts[:reader_index] = 0
64
+ end
65
+ { :class => transport_class, :opts => transport_opts }
66
+ end
67
+
68
+ # Attempts to create a new TEM transport with the given configuration.
69
+ # :call-seq:
70
+ # AutoConfigurator.try_transport(configuration) -> Transport or nil
71
+ #
72
+ # The configuration should have the following keys:
73
+ # class:: the Ruby class implementing the transport
74
+ # opts:: the options to be passed to the implementation's constructor
75
+ def self.try_transport(configuration)
76
+ raise 'No transport class specified' unless configuration[:class]
77
+ begin
78
+ transport = configuration[:class].new(configuration[:opts] || {})
79
+ transport.connect
80
+ return transport
81
+ rescue Exception
82
+ return nil
83
+ end
84
+ end
85
+ end # module AutoConfigurator
86
+
87
+ end # module Tem::Transport
@@ -0,0 +1,99 @@
1
+ # :nodoc: namespace
2
+ module Tem::Transport
3
+
4
+ # Module intended to be mixed into transport implementations to mediate between
5
+ # a high level format for Javacard-specific APDUs and the wire-level APDU
6
+ # request and response formats.
7
+ #
8
+ # The mix-in calls exchange_apdu in the transport implementation. It supplies
9
+ # the APDU data as an array of integers between 0 and 255, and expects a
10
+ # response in the same format.
11
+ module JavaCardMixin
12
+ # Selects a Javacard applet.
13
+ def select_applet(applet_id)
14
+ applet_apdu! :ins => 0xA4, :p1 => 0x04, :p2 => 0x00, :data => applet_id
15
+ end
16
+
17
+ # APDU exchange with the JavaCard applet, raising an exception if the return
18
+ # code is not success (0x9000).
19
+ #
20
+ # :call_seq:
21
+ # transport.applet_apdu!(apdu_data) -> array
22
+ #
23
+ # The apdu_data should be in the format expected by
24
+ # JavaCardMixin#serialize_apdu. Returns the response data, if the response
25
+ # status indicates success (0x9000). Otherwise, raises an exeception.
26
+ def applet_apdu!(apdu_data)
27
+ response = self.applet_apdu apdu_data
28
+ return response[:data] if response[:status] == 0x9000
29
+ raise "JavaCard response has error status 0x#{'%04x' % response[:status]}"
30
+ end
31
+
32
+ # Performs an APDU exchange with the JavaCard applet.
33
+ #
34
+ # :call-seq:
35
+ # transport.applet_apdu(apdu_data) -> hash
36
+ #
37
+ # The apdu_data should be in the format expected by
38
+ # JavaCardMixin#serialize_apdu. The response will be as specified in
39
+ # JavaCardMixin#deserialize_response.
40
+ def applet_apdu(apdu_data)
41
+ apdu = Tem::Transport::JavaCardMixin.serialize_apdu apdu_data
42
+ response = self.exchange_apdu apdu
43
+ JavaCardMixin.deserialize_response response
44
+ end
45
+
46
+ # Serializes an APDU for wire transmission.
47
+ #
48
+ # :call-seq:
49
+ # transport.wire_apdu(apdu_data) -> array
50
+ #
51
+ # The following keys are recognized in the APDU hash:
52
+ # cla:: the CLA byte in the APDU (optional, defaults to 0)
53
+ # ins:: the INS byte in the APDU -- the first byte seen by a JavaCard applet
54
+ # p::
55
+ # p1, p2:: the P1 and P2 bytes in the APDU (optional, both default to 0)
56
+ # data:: the extra data in the APDU (optional, defaults to nothing)
57
+ def self.serialize_apdu(apdu_data)
58
+ raise 'Unspecified INS in apdu_data' unless apdu_data[:ins]
59
+ apdu = [ apdu_data[:cla] || 0, apdu_data[:ins] ]
60
+ if apdu_data[:p12]
61
+ unless apdu_data[:p12].length == 2
62
+ raise "Malformed P1,P2 - #{apdu_data[:p12]}"
63
+ end
64
+ apdu += apdu_data[:p12]
65
+ else
66
+ apdu << (apdu_data[:p1] || 0)
67
+ apdu << (apdu_data[:p2] || 0)
68
+ end
69
+ if apdu_data[:data]
70
+ apdu << apdu_data[:data].length
71
+ apdu += apdu_data[:data]
72
+ else
73
+ apdu << 0
74
+ end
75
+ apdu
76
+ end
77
+
78
+ # De-serializes a JavaCard response APDU.
79
+ #
80
+ # :call-seq:
81
+ # transport.deserialize_response(response) -> hash
82
+ #
83
+ # The response contains the following keys:
84
+ # status:: the 2-byte status code (e.g. 0x9000 is OK)
85
+ # data:: the additional data in the response
86
+ def self.deserialize_response(response)
87
+ { :status => response[-2] * 256 + response[-1], :data => response[0...-2] }
88
+ end
89
+
90
+ # Installs a JavaCard applet on the JavaCard.
91
+ #
92
+ # This would be really, really nice to have. Sadly, it's a far away TBD right
93
+ # now.
94
+ def install_applet(cap_contents)
95
+ raise "Not implemeted; it'd be nice though, right?"
96
+ end
97
+ end # module Tem
98
+
99
+ end # module Tem::Transport
@@ -0,0 +1,59 @@
1
+ # :nodoc: namespace
2
+ module Tem::Transport
3
+
4
+ # Mixin implementing the JCOP simulator protocol.
5
+ #
6
+ # The (pretty informal) protocol specification is contained in the JavaDocs for
7
+ # the class com.ibm.jc.terminal.RemoteJCTerminal and should be easy to find by
8
+ # http://www.google.com/search?q=%22com.ibm.jc.terminal.RemoteJCTerminal%22
9
+ module JcopRemoteProtocol
10
+ # Encodes and sends a JCOP simulator message to a TCP socket.
11
+ #
12
+ # The message must contain the following keys:
13
+ # type:: Integer expressing the message type (e.g. 1 = APDU exchange)
14
+ # node:: Integer expressing the node address (e.g. 0 for most purposes)
15
+ # data:: message payload, as an array of Integers ranging from 0 to 255
16
+ def send_message(socket, message)
17
+ raw_message = [message[:type], message[:node], message[:data].length].
18
+ pack('CCn') + message[:data].pack('C*')
19
+ socket.send raw_message, 0
20
+ end
21
+
22
+ # Reads and decodes a JCOP simulator message from a TCP socket.
23
+ #
24
+ # :call_seq:
25
+ # client.read_message(socket) -> Hash or nil
26
+ #
27
+ # If the other side of the TCP socket closes the connection, this method
28
+ # returns nil. Otherwise, a Hash is returned, with the format required by the
29
+ # JcopRemoteProtocol#send_message.
30
+ def recv_message(socket)
31
+ header = ''
32
+ while header.length < 4
33
+ begin
34
+ partial = socket.recv 4 - header.length
35
+ rescue # Abrupt hangups result in exceptions that we catch here.
36
+ return nil
37
+ end
38
+ return false if partial.length == 0
39
+ header += partial
40
+ end
41
+ message_type, node_address, data_length = *header.unpack('CCn')
42
+ raw_data = ''
43
+ while raw_data.length < data_length
44
+ begin
45
+ partial = socket.recv data_length - raw_data.length
46
+ rescue # Abrupt hangups result in exceptions that we catch here.
47
+ return nil
48
+ end
49
+ return false if partial.length == 0
50
+ raw_data += partial
51
+ end
52
+
53
+ return false unless raw_data.length == data_length
54
+ data = raw_data.unpack('C*')
55
+ return { :type => message_type, :node => node_address, :data => data }
56
+ end
57
+ end # module JcopRemoteProtocol
58
+
59
+ end # namespace Tem::Transport
@@ -0,0 +1,171 @@
1
+ require 'socket'
2
+
3
+ # :nodoc: namespace
4
+ module Tem::Transport
5
+
6
+ # Stubs out the methods that can be implemented by the serving logic in a
7
+ # JCOP remote server. Serving logic classes should mix in this module, to
8
+ # avoid having unimplemented methods.
9
+ module JcopRemoteServingStubs
10
+ # Called when a client connection accepted.
11
+ #
12
+ # This method serves as a notification to the serving logic implementation.
13
+ # Its return value is discarded.
14
+ def connection_start
15
+ nil
16
+ end
17
+
18
+ # Called when a client connection is closed.
19
+ #
20
+ # This method serves as a notification to the serving logic implementation.
21
+ # Its return value is discarded.
22
+ def connection_end
23
+ nil
24
+ end
25
+
26
+ # Serving logic handling an APDU exchange.
27
+ #
28
+ # :call-seq:
29
+ # logic.exchange_apdu(apdu) -> array
30
+ #
31
+ # The |apdu| parameter is the request APDU, formatted as an array of
32
+ # integers between 0 and 255. The method should return the response APDU,
33
+ # formatted in a similar manner.
34
+ def exchange_apdu(apdu)
35
+ # Dumb implementation that always returns OK.
36
+ [0x90, 0x00]
37
+ end
38
+ end # module JcopRemoteServingStubs
39
+
40
+
41
+ # A server for the JCOP simulator protocol.
42
+ #
43
+ # The JCOP simulator protocol is generally useful when talking to a real JCOP
44
+ # simulator. This server is only handy for testing, and for forwarding
45
+ # connections (JCOP's Eclipse plug-in makes the simulator listen to 127.0.0.1,
46
+ # and sometimes you want to use it from another box).
47
+ class JcopRemoteServer
48
+ include JcopRemoteProtocol
49
+
50
+ # Creates a new JCOP server.
51
+ #
52
+ # The options hash supports the following keys:
53
+ # port:: the port to serve on
54
+ # ip:: the IP of the interface to serve on (defaults to all interfaces)
55
+ # reusable:: if set, the serving port can be shared with another
56
+ # application (REUSEADDR flag will be set on the socket)
57
+ #
58
+ # If the |serving_logic| parameter is nil, a serving logic implementation
59
+ # must be provided when calling JcopRemoteServer#run. The server will crash
60
+ # otherwise.
61
+ def initialize(options, serving_logic = nil)
62
+ @logic = serving_logic
63
+ @running = false
64
+ @options = options
65
+ @mutex = Mutex.new
66
+ end
67
+
68
+ # Runs the serving loop indefinitely.
69
+ #
70
+ # This method serves incoming conenctions until #stop is called.
71
+ #
72
+ # If |serving_logic| contains a non-nil value, it overrides any previously
73
+ # specified serving logic implementation. If no implementation is specified
74
+ # when the server is instantiated via JcopRemoteServer#new, one must be
75
+ # passed into |serving_logic|.
76
+ def run(serving_logic = nil)
77
+ @mutex.synchronize do
78
+ @logic ||= serving_logic
79
+ @serving_socket = serving_socket @options
80
+ @running = true
81
+ end
82
+ loop do
83
+ break unless @mutex.synchronize { @running }
84
+ begin
85
+ client_socket, client_address = @serving_socket.accept
86
+ rescue
87
+ # An exception will occur if the socket is closed
88
+ break
89
+ end
90
+ @logic.connection_start
91
+ loop do
92
+ break unless @mutex.synchronize { @running }
93
+ break unless process_request client_socket
94
+ end
95
+ client_socket.close rescue nil
96
+ @logic.connection_end # implemented by subclass
97
+ end
98
+ @mutex.synchronize do
99
+ @serving_socket.close if @serving_socket
100
+ @serving_socket = nil
101
+ end
102
+ end
103
+
104
+ # Stops the serving loop.
105
+ def stop
106
+ @mutex.synchronize do
107
+ if @running
108
+ @serving_socket.close rescue nil
109
+ @serving_socket = nil
110
+ @running = false
111
+ end
112
+ end
113
+
114
+ # TODO(costan): figure out a way to let serving logic reach this directly.
115
+ end
116
+
117
+
118
+ # Creates a socket listening to incoming connections to this server.
119
+ #
120
+ # :call-seq:
121
+ # server.establish_socket(options) -> Socket
122
+ #
123
+ # The |options| parameter supports the same keys as the options parameter
124
+ # of JcopRemoteServer#new.
125
+ #
126
+ # Returns a Socket configured to accept incoming connections.
127
+ def serving_socket(options)
128
+ port = options[:port] || 0
129
+ interface_ip = options[:ip] || '0.0.0.0'
130
+ serving_address = Socket.pack_sockaddr_in port, interface_ip
131
+
132
+ socket = Socket.new Socket::AF_INET, Socket::SOCK_STREAM,
133
+ Socket::PF_UNSPEC
134
+
135
+ if options[:reusable]
136
+ socket.setsockopt Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true
137
+ end
138
+ socket.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true
139
+ socket.bind serving_address
140
+ socket.listen 5
141
+ socket
142
+ end
143
+ private :serving_socket
144
+
145
+ # Performs a request/response cycle.
146
+ #
147
+ # :call-seq:
148
+ # server.process_request(socket) -> Boolean
149
+ #
150
+ # Returns true if the server should do another request/response cycle, or
151
+ # false if this client indicated it's done talking to the server.
152
+ def process_request(socket)
153
+ return false unless request = recv_message(socket)
154
+
155
+ case request[:type]
156
+ when 0
157
+ # Wait for card; no-op, because that should have happen when the client
158
+ # connected.
159
+ send_message socket, :type => 0, :node => 0, :data => [3, 1, 4, 1, 5, 9]
160
+ when 1
161
+ # ATR exchange; the class' bread and butter
162
+ response = @logic.exchange_apdu request[:data]
163
+ send_message socket, :type => 1, :node => 0, :data => response
164
+ else
165
+ send_message socket, :type => request[:type], :node => 0, :data => []
166
+ end
167
+ end
168
+ private :process_request
169
+ end # module JcopRemoteServer
170
+
171
+ end # namespace Tem::Transport