pubnub 0.1.12 → 3.3.0.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of pubnub might be problematic. Click here for more details.

data/lib/pubnub_crypto.rb CHANGED
@@ -1,142 +1,53 @@
1
- ## Pubnub cryptography
2
-
3
- ## including required libraries
4
- require "openssl"
5
- require 'digest'
6
- require 'base64'
7
- require 'rubygems'
8
- require 'json'
9
-
10
- class PubnubCrypto
11
- #**
12
- #* initialize encryption with cipher key, IV and algorithm
13
- #*
14
- #* @param cipher key (plain text password)
15
- #*
16
- def initialize(cipher_key)
17
- @@alg = "AES-128-CBC"
18
- digest = Digest::MD5.new
19
- digest.update(cipher_key)
20
- @@key = digest.digest
21
- @@iv = '0123456789012345'
22
- raise 'Key Error' if(@@key.nil? or @@key.size != 16)
23
- end
24
-
25
- #**
26
- #* encrypt object
27
- #*
28
- #* @param plain text(message)
29
- #* @return cipher text (encrypted text)
30
- #*
31
- def encryptObject(message)
32
- params = Hash.new
33
- if message.is_a? String
34
- return encrypt(message)
35
- else
36
- message.each do |key,value|
37
- case(key)
38
- when(key)
39
- params[key] = encrypt(value).chop.reverse.chop.reverse();
40
- end
41
- end
42
- params = params.to_json
43
- return params
44
- end
45
- end
46
-
47
- #**
48
- #* decrypt object
49
- #*
50
- #* @param cipher object (cipher to decrypt)
51
- #* @return plain text (decrypted text)
52
- #*
53
- def decryptObject(cipher_Object)
54
- params = {};
55
- if cipher_Object.is_a? String
56
- return decrypt(cipher_Object)
57
- else
58
- cipher_Object.each do |key,value|
59
- case(key)
60
- when(key)
61
- params[key] = decrypt(value);
62
- end
63
- end
64
- return params
65
- end
66
- end
67
-
68
- #**
69
- #* encrypt array
70
- #*
71
- #* @param message to encrypt (array)
72
- #* @return cipher text array (encrypted array)
73
- #*
74
- def encryptArray(message)
75
- params = []
76
- i=0
77
- message.each do |val|
78
- case(val)
79
- when(val)
80
- params[i] = encrypt(val).chop.reverse.chop.reverse();
81
- i = i+1
82
- end
83
- end
84
- params = params.to_json
85
- return params
86
- end
87
-
88
- #**
89
- #* decrypt array
90
- #*
91
- #* @param cipher array (cipher text array to decrypt)
92
- #* @return message decrypted (decrypted array)
93
- #*
94
- def decryptArray(message)
95
- params = []
96
- i=0
97
- message.each do |val|
98
- case(val)
99
- when(val)
100
- params[i] = decrypt(val);
101
- i = i+1
102
- end
103
- end
104
- return params
105
- end
106
-
107
- #**
108
- #* encrypt plain text
109
- #*
110
- #* @param plain text (string to encrypt)
111
- #* @return cipher text (encrypted text)
112
- #*
113
- def encrypt(message)
114
- aes = OpenSSL::Cipher::Cipher.new(@@alg)
115
- aes.encrypt
116
- aes.key = @@key
117
- aes.iv = @@iv
118
- @@cipher = aes.update(message)
119
- @@cipher << aes.final
120
- @@ciphertext = [@@cipher].pack('m')
121
- @@ciphertext = @@ciphertext.strip
122
- @@ciphertext = @@ciphertext.gsub(/\n/,"")
123
- @@ciphertext = '"' + @@ciphertext + '"'
124
- return @@ciphertext
125
- end
126
-
127
- #**
128
- #* decrypt plain text
129
- #*
130
- #* @param cipher text
131
- #* @return plain text (decrypted text)
132
- #*
133
- def decrypt(cipher_text)
134
- decode_cipher = OpenSSL::Cipher::Cipher.new(@@alg)
135
- decode_cipher.decrypt
136
- decode_cipher.key = @@key
137
- decode_cipher.iv = @@iv
138
- plain_text = decode_cipher.update(cipher_text.unpack('m')[0])
139
- plain_text << decode_cipher.final
140
- return plain_text
141
- end
142
- end
1
+ class PubnubCrypto
2
+ require 'yajl'
3
+
4
+ def initialize(cipher_key)
5
+ @alg = "AES-256-CBC"
6
+ sha256_key = Digest::SHA256.hexdigest(cipher_key)
7
+ @key = sha256_key.slice(0,32)
8
+
9
+ #puts("\nraw sha cipher_key is: #{cipher_key}")
10
+ #puts("raw sha cipher_key is: #{sha256_key}")
11
+ #puts("padded cipher_key is: #{@key}\n")
12
+
13
+ @iv = '0123456789012345'
14
+ end
15
+
16
+
17
+ def encrypt(message)
18
+
19
+ aes = OpenSSL::Cipher::Cipher.new(@alg)
20
+ aes.encrypt
21
+ aes.key = @key
22
+ aes.iv = @iv
23
+
24
+ json_message = Yajl.dump(message)
25
+ cipher = aes.update(json_message)
26
+ cipher << aes.final
27
+
28
+ Base64.strict_encode64(cipher)
29
+
30
+ end
31
+
32
+
33
+ def decrypt(cipher_text)
34
+ decode_cipher = OpenSSL::Cipher::Cipher.new(@alg)
35
+ decode_cipher.decrypt
36
+ decode_cipher.key = @key
37
+ decode_cipher.iv = @iv
38
+
39
+ plain_text = ""
40
+
41
+ begin
42
+ undecoded_text = Base64.decode64(cipher_text)
43
+ plain_text = decode_cipher.update(undecoded_text)
44
+ plain_text << decode_cipher.final
45
+ rescue => e
46
+
47
+ return "DECRYPTION_ERROR"
48
+
49
+ end
50
+
51
+ return Yajl.load(plain_text)
52
+ end
53
+ end
@@ -0,0 +1,37 @@
1
+ require 'eventmachine'
2
+
3
+ class PubnubDeferrable < EM::Protocols::HttpClient2
4
+
5
+ include EM::Deferrable
6
+
7
+ attr_accessor :start_time, :end_time, :elapsed_time, :pubnub_request
8
+
9
+ def initialize
10
+ super
11
+ end
12
+
13
+ def post_init
14
+ #puts("deferrable says my query is: #{pubnub_request.query}")
15
+ super
16
+ end
17
+
18
+ def connection_completed
19
+ @start_time = Time.now
20
+ #puts("\n--- #{@start_time}: Connected with string: #{self.pubnub_request.query}")
21
+ super
22
+ end
23
+
24
+ def unbind
25
+ @end_time = Time.now
26
+
27
+ if @start_time == nil
28
+ @start_time = Time.now
29
+ end
30
+
31
+ #puts("-- #{@end_time}: Disconnected.")
32
+ #puts("--- Elapsed connection time: #{@end_time - @start_time}s")
33
+ super
34
+ end
35
+
36
+
37
+ end
@@ -0,0 +1,287 @@
1
+ class PubnubRequest
2
+ attr_accessor :cipher_key, :host, :query, :response, :timetoken, :url, :operation, :callback, :publish_key, :subscribe_key, :secret_key, :channel, :jsonp, :message, :ssl, :port
3
+ attr_accessor :history_limit, :history_count, :history_start, :history_end, :history_reverse, :session_uuid
4
+
5
+ class RequestError < RuntimeError;
6
+ end
7
+
8
+ def initialize(args = {})
9
+ args = HashWithIndifferentAccess.new(args)
10
+
11
+ @operation = args[:operation].to_s
12
+ @callback = args[:callback]
13
+ @cipher_key = args[:cipher_key]
14
+ @session_uuid = args[:session_uuid]
15
+ @publish_key = args[:publish_key]
16
+ @subscribe_key = args[:subscribe_key]
17
+ @channel = args[:channel]
18
+ @jsonp = args[:jsonp].present? ? "1" : "0"
19
+ @message = args[:message]
20
+ @secret_key = args[:secret_key] || "0"
21
+ @timetoken = args[:timetoken] || "0"
22
+ @ssl = args[:ssl]
23
+
24
+ @port = args[:port]
25
+ @url = args[:url]
26
+ @host = args[:host]
27
+ @query = args[:query]
28
+ end
29
+
30
+ def op_exception
31
+ if @operation.present?
32
+ ("Pubnub::" + @operation.to_s.capitalize + "Error").constantize
33
+ else
34
+ PubnubRequest::RequestError
35
+ end
36
+ end
37
+
38
+ def ==(another)
39
+ self.operation == another.operation && self.callback == another.callback &&
40
+ self.channel == another.channel && self.message == another.message
41
+ end
42
+
43
+ def set_channel(options)
44
+ options = HashWithIndifferentAccess.new(options)
45
+
46
+ if options[:channel].blank?
47
+ raise(op_exception, "channel is a required parameter.")
48
+ else
49
+ self.channel = options[:channel].to_s
50
+ self
51
+ end
52
+ end
53
+
54
+ def set_callback(options)
55
+ options = HashWithIndifferentAccess.new(options)
56
+
57
+ if options[:callback].blank?
58
+ raise(op_exception, "callback is a required parameter.")
59
+ elsif !options[:callback].try(:respond_to?, "call")
60
+ raise(op_exception, "callback is invalid.")
61
+ else
62
+ self.callback = options[:callback]
63
+ self
64
+ end
65
+ end
66
+
67
+
68
+ def set_cipher_key(options, self_cipher_key)
69
+ options = HashWithIndifferentAccess.new(options)
70
+
71
+ if self_cipher_key.present? && options['cipher_key'].present?
72
+ raise(op_exception, "existing cipher_key #{self_cipher_key} cannot be overridden at publish-time.")
73
+
74
+ elsif (self_cipher_key.present? && options[:cipher_key].blank?) || (self_cipher_key.blank? && options[:cipher_key].present?)
75
+
76
+ this_cipher_key = self_cipher_key || options[:cipher_key]
77
+ raise(Pubnub::PublishError, "secret key must be a string.") if this_cipher_key.class != String
78
+ self.cipher_key = this_cipher_key
79
+ end
80
+ end
81
+
82
+ def set_secret_key(options, self_secret_key)
83
+ options = HashWithIndifferentAccess.new(options)
84
+
85
+ if self_secret_key.present? && options['secret_key'].present?
86
+ raise(Pubnub::PublishError, "existing secret_key #{self_secret_key} cannot be overridden at publish-time.")
87
+
88
+ elsif (self_secret_key.present? && options[:secret_key].blank?) || (self_secret_key.blank? && options[:secret_key].present?)
89
+
90
+ my_secret_key = self_secret_key || options[:secret_key]
91
+ raise(Pubnub::PublishError, "secret key must be a string.") if my_secret_key.class != String
92
+
93
+ signature = "{ @publish_key, @subscribe_key, @secret_key, channel, message}"
94
+ digest = OpenSSL::Digest.new("sha256")
95
+ key = [my_secret_key]
96
+ hmac = OpenSSL::HMAC.hexdigest(digest, key.pack("H*"), signature)
97
+ self.secret_key = hmac
98
+ else
99
+ self.secret_key = "0"
100
+ end
101
+ end
102
+
103
+ def set_message(options, self_cipher_key)
104
+ options = HashWithIndifferentAccess.new(options)
105
+
106
+ if options[:message].blank? && options[:message] != ""
107
+ raise(op_exception, "message is a required parameter.")
108
+ else
109
+ my_cipher_key = options[:cipher_key] || self_cipher_key
110
+
111
+ if my_cipher_key.present?
112
+ self.message = Yajl.dump(aes_encrypt(my_cipher_key, options, self))
113
+ else
114
+ self.message = Yajl.dump(options[:message])
115
+ end
116
+ end
117
+ end
118
+
119
+ def set_publish_key(options, self_publish_key)
120
+ options = HashWithIndifferentAccess.new(options)
121
+
122
+ if options[:publish_key].blank? && self_publish_key.blank?
123
+ raise(Pubnub::PublishError, "publish_key is a required parameter.")
124
+ elsif self_publish_key.present? && options['publish_key'].present?
125
+ raise(Pubnub::PublishError, "existing publish_key #{self_publish_key} cannot be overridden at publish-time.")
126
+ else
127
+ self.publish_key = (self_publish_key || options[:publish_key]).to_s
128
+ end
129
+ end
130
+
131
+ def set_subscribe_key(options, self_subscribe_key)
132
+ options = HashWithIndifferentAccess.new(options)
133
+
134
+ if options[:subscribe_key].blank? && self_subscribe_key.blank?
135
+ raise(op_exception, "subscribe_key is a required parameter.")
136
+ elsif self_subscribe_key.present? && options['subscribe_key'].present?
137
+ raise(op_exception, "existing subscribe_key #{self_subscribe_key} cannot be overridden at subscribe-time.")
138
+ else
139
+ self.subscribe_key = (self_subscribe_key || options[:subscribe_key]).to_s
140
+ end
141
+ end
142
+
143
+ def package_response!(response_data)
144
+ self.response = response_data.respond_to?(:content) ? Yajl.load(response_data.content) : Yajl.load(response_data)
145
+ self.timetoken = self.response[1] unless self.operation == "time"
146
+
147
+ if self.cipher_key.present? && %w(subscribe history detailed_history).include?(self.operation)
148
+
149
+ myarr = Array.new
150
+ pc = PubnubCrypto.new(@cipher_key)
151
+
152
+ case @operation
153
+ when "publish"
154
+ iterate = self.response.first
155
+ when "subscribe"
156
+ iterate = self.response.first
157
+ when "history"
158
+ iterate = self.response
159
+ when "detailed_history"
160
+ iterate = self.response.first
161
+
162
+ else
163
+ raise(RequestError, "Don't know how to iterate on this operation.")
164
+ end
165
+
166
+ iterate.each do |message|
167
+ message = pc.decrypt(message)
168
+ myarr.push(message)
169
+ end
170
+
171
+ if %w(publish subscribe).include?(@operation)
172
+ self.response[0] = myarr
173
+ elsif @operation == "detailed_history"
174
+ json_response_data = Yajl.load(response_data)
175
+ self.response = [myarr, json_response_data[1], json_response_data[2]]
176
+ else
177
+ self.response = myarr
178
+ end
179
+
180
+ end
181
+ end
182
+
183
+ def format_url!(override_timetoken = nil)
184
+
185
+ raise(Pubnub::PublishError, "Missing .operation in PubnubRequest object") if self.operation.blank?
186
+
187
+ if @ssl.present?
188
+ origin = 'https://' + Pubnub::ORIGIN_HOST
189
+ @port = 443
190
+ else
191
+ origin = 'http://' + Pubnub::ORIGIN_HOST
192
+ @port = 80
193
+ end
194
+
195
+ if override_timetoken.present?
196
+ self.timetoken = override_timetoken.to_s
197
+ end
198
+
199
+ case self.operation.to_s
200
+ when "publish"
201
+ url_array = [self.operation.to_s, self.publish_key.to_s, self.subscribe_key.to_s,
202
+ self.secret_key.to_s, self.channel.to_s, "0", self.message]
203
+
204
+ when "subscribe"
205
+ url_array = [self.operation.to_s, self.subscribe_key.to_s, self.channel.to_s, "0", @timetoken]
206
+
207
+ when "presence"
208
+ url_array = ["subscribe", self.subscribe_key.to_s, ((self.channel.to_s) + "-pnpres"), "0", @timetoken]
209
+
210
+ when "time"
211
+ url_array = [self.operation.to_s, "0"]
212
+
213
+ when "history"
214
+ url_array = [self.operation.to_s, self.subscribe_key.to_s, self.channel.to_s, "0", self.history_limit.to_s]
215
+
216
+ when "detailed_history"
217
+ url_array = ["v2", "history", "sub-key", self.subscribe_key.to_s, "channel", self.channel.to_s]
218
+
219
+ when "here_now"
220
+ url_array = ["v2", "presence", "sub-key", self.subscribe_key.to_s, "channel", self.channel.to_s]
221
+
222
+ else
223
+
224
+ raise(PubnubRequest::RequestError, "I can't create that URL for you due to unknown operation type.")
225
+ end
226
+
227
+ self.url = origin + encode_URL(url_array)
228
+
229
+ uri = URI.parse(self.url)
230
+
231
+ self.host = uri.host
232
+ url_params = ""
233
+
234
+ if %w(subscribe presence).include?(@operation)
235
+ uri.query = uri.query.blank? ? "uuid=#{@session_uuid}" : (uri.query + "uuid=#{@session_uuid}")
236
+
237
+ elsif @operation == "detailed_history"
238
+ url_sep = "?"
239
+
240
+ if @history_count || @history_start || @history_end || @history_reverse
241
+
242
+ if @history_count
243
+ url_params += url_sep + "count=" + @history_count.to_s
244
+ url_sep = "&"
245
+ end
246
+
247
+ if @history_start
248
+ url_params += url_sep + "start=" + @history_start.to_s
249
+ url_sep = "&"
250
+ end
251
+
252
+ if @history_end
253
+ url_params += url_sep + "end=" + @history_end.to_s
254
+ url_sep = "&"
255
+ end
256
+
257
+ if @history_reverse
258
+ url_params += url_sep + "reverse=true"
259
+ url_sep = "&"
260
+ end
261
+ end
262
+ end
263
+
264
+ self.query = uri.path + (uri.query.present? ? ("?" + uri.query) : "") + url_params
265
+ self.url += url_params
266
+ self
267
+
268
+ end
269
+
270
+ def aes_encrypt(cipher_key, options, publish_request)
271
+ options = HashWithIndifferentAccess.new(options)
272
+
273
+ pc = PubnubCrypto.new(cipher_key)
274
+ publish_request.message = pc.encrypt(options[:message])
275
+
276
+ end
277
+
278
+ def encode_URL(request)
279
+ ## Construct Request URL
280
+ url = '/' + request.map { |bit| bit.split('').map { |ch|
281
+ ' ~`!@#$%^&*()+=[]\\{}|;\':",./<>?'.index(ch) ?
282
+ '%' + ch.unpack('H2')[0].to_s.upcase : URI.encode(ch)
283
+ }.join('') }.join('/')
284
+ return url
285
+ end
286
+
287
+ end