costan-tem_ruby 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
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