rack-tctp 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9603392ec9a82dbf2dfec8febe170418ed2643b0
4
+ data.tar.gz: 642244102dd91252cf6b32c248c4381bb15779ca
5
+ SHA512:
6
+ metadata.gz: 3e641bd533743f789a3259dc12de7c84bf613341f7478c5c427847384db4cd4c384defcf49e8e6871da0189aab4421f644df0c6c80638657afb804777e2fbeb9
7
+ data.tar.gz: 17e8a9056248af9091c43e23bc1c7c691f7e1d287ae2c70e53a075328a1e07f636745c47e516974e0f632d0d50839d41956eb9726a76642611148c5e4afab8fd
@@ -0,0 +1,116 @@
1
+ require 'openssl'
2
+ require 'socket'
3
+
4
+ # HTTP application layer encryption channel. Used for the Trusted Cloud Transfer Protocol (TCTP)
5
+ class HALEC
6
+ # The URL of this HALEC
7
+ attr_reader :url
8
+
9
+ # The plaintext socket
10
+ attr_reader :socket_here
11
+
12
+ # The SSL socket
13
+ attr_reader :ssl_socket
14
+
15
+ # The encrypted socket
16
+ attr_reader :socket_there
17
+
18
+ # The private key for a certificate (if any)
19
+ attr_reader :private_key
20
+
21
+ # A server or client certificate (if any)
22
+ attr_reader :certificate
23
+
24
+ # The TLS context
25
+ attr_reader :ctx
26
+
27
+ def initialize(options = {})
28
+ @url = options[:url] || ''
29
+ @ctx = options[:ssl_context] || OpenSSL::SSL::SSLContext.new()
30
+
31
+ @ctx.ssl_version = :TLSv1
32
+
33
+ @socket_here, @socket_there = socket_pair
34
+ [@socket_here, @socket_there].each do |socket|
35
+ socket.set_encoding(Encoding::BINARY)
36
+ end
37
+ end
38
+
39
+ private
40
+ def socket_pair
41
+ Socket.pair(:UNIX, :STREAM, 0) # Linux
42
+ rescue Errno::EAFNOSUPPORT
43
+ Socket.pair(:INET, :STREAM, 0) # Windows
44
+ end
45
+ end
46
+
47
+ # The Client end of an HALEC
48
+ class ClientHALEC < HALEC
49
+ def initialize(options = {})
50
+ super(options)
51
+
52
+ @ssl_socket = OpenSSL::SSL::SSLSocket.new(@socket_here, @ctx)
53
+ end
54
+ end
55
+
56
+ # The Server end of an HALEC
57
+ class ServerHALEC < HALEC
58
+ def initialize(options = {})
59
+ super(options)
60
+
61
+ if(options[:private_key] && options[:certificate])
62
+ @private_key = options[:private_key]
63
+ @certificate = options[:certificate]
64
+ else
65
+ @private_key = ServerHALEC.default_key
66
+ @certificate = ServerHALEC.default_self_signed_certificate
67
+ end
68
+
69
+ @ctx.cert = @certificate
70
+ @ctx.key = @private_key
71
+
72
+ @ssl_socket = OpenSSL::SSL::SSLSocket.new(@socket_here, @ctx)
73
+ Thread.new {
74
+ begin
75
+ s = @ssl_socket.accept
76
+ rescue Exception => e
77
+ puts e
78
+ end
79
+ }
80
+ end
81
+
82
+ class << self
83
+ @default_key
84
+ @default_self_signed_certificate
85
+
86
+ def initialize
87
+ default_key
88
+ default_self_signed_certificate
89
+
90
+ self
91
+ end
92
+
93
+ def default_key
94
+ @default_key ||= OpenSSL::PKey::RSA.new 2048
95
+ end
96
+
97
+ def default_self_signed_certificate
98
+ @default_self_signed_certificate ||= generate_self_signed_certificate
99
+ end
100
+
101
+ def generate_self_signed_certificate
102
+ name = OpenSSL::X509::Name.parse 'CN=tctp-server/DC=tctp'
103
+
104
+ cert = OpenSSL::X509::Certificate.new
105
+ cert.version = 2
106
+ cert.serial = 0
107
+ cert.not_before = Time.now
108
+ cert.not_after = Time.now + 3600
109
+
110
+ cert.public_key = @default_key.public_key
111
+ cert.subject = name
112
+
113
+ cert
114
+ end
115
+ end
116
+ end
data/lib/rack/tctp.rb ADDED
@@ -0,0 +1,185 @@
1
+ require 'radix'
2
+ require 'ruby-prof'
3
+
4
+ require_relative 'tctp/halec'
5
+
6
+ module Rack
7
+ # This middleware enables Rack to make use of the Trusted Cloud Transfer Protocol (TCTP) for HTTP end-to-end
8
+ # body confidentiality and integrity.
9
+ class TCTP
10
+ DEFAULT_TCTP_DISCOVERY_INFORMATION = '/.*:/halecs'
11
+ TCTP_DISCOVERY_MEDIA_TYPE = 'text/prs.tctp-discovery'
12
+
13
+ # The slug URI can contain any HTTP compatible characters
14
+ def self.slug_base
15
+ Radix::Base.new(Radix::BASE::B62 + ['-', '_'])
16
+ end
17
+
18
+ # Generate a new random slug (2^64 possibilities)
19
+ def self.new_slug
20
+ slug_base.convert(rand(2**64), 10)
21
+ end
22
+
23
+ # The TCTP sessions
24
+ attr_reader :sessions
25
+
26
+ # Initializes TCTP middleware
27
+ def initialize(app)
28
+ @app = app
29
+ @sessions = {}
30
+ end
31
+
32
+ # Middleware call. Supports all TCTP use cases:
33
+ # * TCTP discovery
34
+ # * HALEC creation
35
+ # * HALEC handshake
36
+ # * Decrypting TCTP secured entity-bodies
37
+ # * Encrypting entity-bodies using TCTP
38
+ def call(env)
39
+ begin
40
+ req = Rack::Request.new(env)
41
+
42
+ # Switch through TCTP use cases
43
+ case
44
+ when is_tctp_discovery?(req)
45
+ # TCTP discovery
46
+ # TODO Parameterize discovery information
47
+ [200, {"Content-Type" => TCTP_DISCOVERY_MEDIA_TYPE, "Content-Length" => DEFAULT_TCTP_DISCOVERY_INFORMATION.length.to_s}, DEFAULT_TCTP_DISCOVERY_INFORMATION]
48
+ when is_halec_creation?(req)
49
+ # HALEC creation
50
+ halec = ServerHALEC.new(url: '/halecs/' + TCTP::new_slug)
51
+
52
+ # TODO Allow creation using predefined cookie
53
+ session = TCTPSession.new
54
+
55
+ # Send client_hello to server HALEC and read handshake_response
56
+ client_hello = req.body.read
57
+ halec.socket_there.write(client_hello)
58
+ handshake_response = [halec.socket_there.recv(2048)]
59
+
60
+ # Set location header and content-length
61
+ header = {'Location' => halec.url, 'Content-Length' => handshake_response[0].length.to_s}
62
+
63
+ # Set the TCTP session cookie header
64
+ Rack::Utils.set_cookie_header!(header, "tctp_session_cookie", {:value => session.session_id, :path => '/', :expires => Time.now+24*60*60})
65
+
66
+ # Persist session and HALEC
67
+ session.halecs[halec.url] = halec
68
+ sessions[session.session_id] = session
69
+
70
+ [201, header, handshake_response]
71
+ when is_halec_handshake?(req)
72
+ # Get persisted server HALEC
73
+ halec = @sessions[req.cookies['tctp_session_cookie']].halecs[req.path_info]
74
+
75
+ # Write handshake message to server HALEC
76
+ halec.socket_there.write(req.body.read)
77
+
78
+ # Receive handshake response
79
+ handshake_response = halec.socket_there.recv(2048)
80
+
81
+ # Send back server HALEC response
82
+ [200, {'Content-Length' => handshake_response.length.to_s}, [handshake_response]]
83
+ else
84
+ # Decrypt TCTP secured bodies
85
+ if is_tctp_encrypted_body?(req) then
86
+ decrypted_body = StringIO.new
87
+
88
+ halec_url = req.body.readline.chomp
89
+
90
+ # Gets the HALEC
91
+ halec = @sessions[req.cookies['tctp_session_cookie']].halecs[halec_url]
92
+
93
+ halec.socket_there.write(req.body.read)
94
+ decrypted_body.write(halec.ssl_socket.readpartial(2 ** 26 - 1))
95
+
96
+ req.body.string = decrypted_body.string
97
+ end
98
+
99
+ status, headers, body = @app.call(env)
100
+
101
+ if is_tctp_response_requested?(req)
102
+ # Gets the first free server HALEC for encryption
103
+ # TODO Send error if cookie is missing
104
+ halec = @sessions[req.cookies['tctp_session_cookie']].free_halec
105
+
106
+ # The length of the content body
107
+ content_body_length = 0
108
+
109
+ # The first line
110
+ first_line = halec.url + "\r\n"
111
+ content_body_length += first_line.length
112
+
113
+ # Encrypt the body. The first line of the response specifies the used HALEC
114
+ encrypted_body = []
115
+ encrypted_body << first_line
116
+
117
+ # Encrypt each body fragment
118
+ body.each do |fragment|
119
+ bodyio = StringIO.new(fragment)
120
+
121
+ until bodyio.eof? do
122
+ chunk = bodyio.read(16 * 1024)
123
+ halec.ssl_socket.write(chunk)
124
+ encrypted_chunk = halec.socket_there.readpartial(32 * 1024)
125
+ encrypted_body << encrypted_chunk
126
+ content_body_length += encrypted_chunk.length
127
+ end
128
+ end
129
+
130
+ # Sets the content length and encoding
131
+ headers['Content-Length'] = content_body_length.to_s
132
+ headers['Content-Encoding'] = 'encrypted'
133
+
134
+ [status, headers, encrypted_body]
135
+ else
136
+ [status, headers, body]
137
+ end
138
+ end
139
+ rescue Exception => e
140
+ puts e
141
+ end
142
+ end
143
+
144
+ private
145
+ def is_tctp_discovery?(req)
146
+ req.options? && !req.env['HTTP_ACCEPT'].nil? && req.env['HTTP_ACCEPT'].eql?(TCTP_DISCOVERY_MEDIA_TYPE)
147
+ end
148
+
149
+ def is_halec_creation?(req)
150
+ req.post? && req.path_info.eql?('/halecs')
151
+ end
152
+
153
+ def is_halec_handshake?(req)
154
+ req.post? &&
155
+ !req.cookies.nil? &&
156
+ req.cookies.has_key?('tctp_session_cookie') &&
157
+ sessions.has_key?(req.cookies['tctp_session_cookie']) &&
158
+ sessions[req.cookies['tctp_session_cookie']].halecs.has_key?(req.path_info)
159
+ end
160
+
161
+ def is_tctp_response_requested? (req)
162
+ req.env['HTTP_ACCEPT_ENCODING'].eql?('encrypted')
163
+ end
164
+
165
+ def is_tctp_encrypted_body? (req)
166
+ req.env['HTTP_CONTENT_ENCODING'].eql?('encrypted')
167
+ end
168
+ end
169
+
170
+ class TCTPSession
171
+ attr_reader :session_id
172
+
173
+ attr_reader :halecs
174
+
175
+ def initialize(session_id = TCTP::new_slug)
176
+ @session_id = session_id
177
+ @halecs = {}
178
+ end
179
+
180
+ def free_halec
181
+ # TODO free HALEC handling
182
+ @halecs.first[1]
183
+ end
184
+ end
185
+ end
data/lib/rack-tctp.rb ADDED
@@ -0,0 +1 @@
1
+ require_relative 'rack/tctp'
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-tctp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Mathias Slawik
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-21 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Rack middleware for end-to-end security through TCTP
14
+ email: mathias.slawik@tu-berlin.de
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/rack-tctp.rb
20
+ - lib/rack/tctp.rb
21
+ - lib/rack/tctp/halec.rb
22
+ homepage: https://github.com/mathiasslawik/rack-tctp
23
+ licenses:
24
+ - Apache-2.0
25
+ metadata: {}
26
+ post_install_message:
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubyforge_project:
42
+ rubygems_version: 2.0.2
43
+ signing_key:
44
+ specification_version: 4
45
+ summary: Rack TCTP middleware
46
+ test_files: []