pubnub 5.2.2 → 5.3.0

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +2 -2
  3. data/.pubnub.yml +11 -4
  4. data/.tool-versions +1 -1
  5. data/CHANGELOG.md +9 -0
  6. data/Gemfile +1 -1
  7. data/Gemfile.lock +6 -6
  8. data/LICENSE +29 -0
  9. data/VERSION +1 -1
  10. data/features/step_definitions/access_steps.rb +0 -2
  11. data/features/step_definitions/crypto_steps.rb +99 -0
  12. data/features/support/cryptor.rb +58 -0
  13. data/features/support/hooks.rb +0 -1
  14. data/lib/pubnub/client.rb +30 -1
  15. data/lib/pubnub/error.rb +3 -0
  16. data/lib/pubnub/event.rb +13 -5
  17. data/lib/pubnub/events/add_message_action.rb +2 -2
  18. data/lib/pubnub/events/grant_token.rb +1 -1
  19. data/lib/pubnub/events/history.rb +18 -6
  20. data/lib/pubnub/events/publish.rb +7 -3
  21. data/lib/pubnub/events/remove_channel_members.rb +3 -3
  22. data/lib/pubnub/events/remove_channel_metadata.rb +1 -1
  23. data/lib/pubnub/events/remove_memberships.rb +3 -3
  24. data/lib/pubnub/events/remove_uuid_metadata.rb +1 -1
  25. data/lib/pubnub/events/set_channel_members.rb +3 -3
  26. data/lib/pubnub/events/set_channel_metadata.rb +2 -2
  27. data/lib/pubnub/events/set_memberships.rb +3 -3
  28. data/lib/pubnub/events/set_uuid_metadata.rb +2 -2
  29. data/lib/pubnub/events/signal.rb +1 -1
  30. data/lib/pubnub/events/subscribe.rb +5 -0
  31. data/lib/pubnub/formatter.rb +22 -11
  32. data/lib/pubnub/modules/crypto/crypto_module.rb +159 -0
  33. data/lib/pubnub/modules/crypto/crypto_provider.rb +31 -0
  34. data/lib/pubnub/modules/crypto/cryptor.rb +73 -0
  35. data/lib/pubnub/modules/crypto/cryptor_header.rb +251 -0
  36. data/lib/pubnub/modules/crypto/cryptors/aes_cbc_cryptor.rb +67 -0
  37. data/lib/pubnub/modules/crypto/cryptors/legacy_cryptor.rb +84 -0
  38. data/lib/pubnub/modules/crypto/module.rb +8 -0
  39. data/lib/pubnub/subscribe_event/formatter.rb +8 -8
  40. data/lib/pubnub/version.rb +1 -1
  41. data/pubnub.gemspec +2 -2
  42. metadata +16 -5
  43. data/LICENSE.txt +0 -30
  44. data/lib/pubnub/crypto.rb +0 -70
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubnub
4
+ module Crypto
5
+ # AES-256-CBC cryptor.
6
+ #
7
+ # The cryptor provides _encryption_ and _decryption_ using <i>AES-256</i> in
8
+ # <i>CBC</i> mode with a cipher key and random initialization vector.
9
+ # When it is registered as a secondary with other cryptors, it will provide
10
+ # backward compatibility with previously encrypted data.
11
+ class AesCbcCryptor < Cryptor
12
+ # AES-128 CBC block size.
13
+ BLOCK_SIZE = 16
14
+
15
+ # Create AES-256-CBC cryptor instance.
16
+ #
17
+ # @param cipher_key [String] Key for data <i>encryption</i> and
18
+ # <i>decryption</i>.
19
+ def initialize(cipher_key)
20
+ @cipher_key = Digest::SHA256.digest(cipher_key)
21
+ @alg = 'AES-256-CBC'
22
+ super()
23
+ end
24
+
25
+ def identifier
26
+ 'ACRH'
27
+ end
28
+
29
+ def encrypt(data)
30
+ if data.nil? || data.empty?
31
+ puts 'Pubnub :: ENCRYPTION ERROR: Empty data for encryption'
32
+ return nil
33
+ end
34
+
35
+ iv = OpenSSL::Random.random_bytes BLOCK_SIZE
36
+ cipher = OpenSSL::Cipher.new(@alg).encrypt
37
+ cipher.key = @cipher_key
38
+ cipher.iv = iv
39
+
40
+ encoded_message = cipher.update data
41
+ encoded_message << cipher.final
42
+ Crypto::EncryptedData.new(encoded_message, iv)
43
+ rescue StandardError => e
44
+ puts "Pubnub :: ENCRYPTION ERROR: #{e}"
45
+ nil
46
+ end
47
+
48
+ def decrypt(data)
49
+ if data.metadata.length != BLOCK_SIZE
50
+ puts "Pubnub :: DECRYPTION ERROR: Unexpected initialization vector length:
51
+ #{data.metadata.length} bytes (#{BLOCK_SIZE} bytes is expected)"
52
+ return nil
53
+ end
54
+
55
+ cipher = OpenSSL::Cipher.new(@alg).decrypt
56
+ cipher.key = @cipher_key
57
+ cipher.iv = data.metadata
58
+
59
+ decrypted = cipher.update data.data
60
+ decrypted << cipher.final
61
+ rescue StandardError => e
62
+ puts "Pubnub :: DECRYPTION ERROR: #{e}"
63
+ nil
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,84 @@
1
+ module Pubnub
2
+ module Crypto
3
+ # Legacy cryptor.
4
+ #
5
+ # The cryptor provides _encryption_ and _decryption_ using <i>`AES-256</i> in
6
+ # <i>CBC</i> mode with a cipher key and configurable initialization vector
7
+ # randomness.
8
+ # When it is registered as a secondary with other cryptors, it will provide
9
+ # backward compatibility with previously encrypted data.
10
+ #
11
+ # <b>Important</b>: It has been reported that the digest from cipherKey has
12
+ # low entropy, and it is suggested to use <i>AesCbcCryptor</i> instead.
13
+ class LegacyCryptor < Cryptor
14
+ # AES-128 CBC block size.
15
+ BLOCK_SIZE = 16
16
+
17
+ # Create legacy cryptor instance.
18
+ #
19
+ # @param cipher_key [String] Key for data <i>encryption</i> and
20
+ # <i>decryption</i>.
21
+ # @param use_random_iv [Boolean] Whether random IV should be used.
22
+ def initialize(cipher_key, use_random_iv = true)
23
+ @alg = 'AES-256-CBC'
24
+ @original_cipher_key = cipher_key
25
+ @cipher_key = Digest::SHA256.hexdigest(cipher_key.to_s).slice(0, 32)
26
+ @iv = use_random_iv ? nil : '0123456789012345'
27
+ super()
28
+ end
29
+
30
+ def identifier
31
+ '\x00\x00\x00\x00'
32
+ end
33
+
34
+ def encrypt(data)
35
+ if data.nil? || data.empty?
36
+ puts 'Pubnub :: ENCRYPTION ERROR: Empty data for encryption'
37
+ return nil
38
+ end
39
+
40
+ iv = @iv || OpenSSL::Random.random_bytes(BLOCK_SIZE)
41
+ cipher = OpenSSL::Cipher.new(@alg).encrypt
42
+ cipher.key = @cipher_key
43
+ cipher.iv = iv
44
+
45
+ encoded_message = ''
46
+ encoded_message << iv if @iv.nil? && iv
47
+ encoded_message << cipher.update(data)
48
+ encoded_message << cipher.final
49
+ Crypto::EncryptedData.new(encoded_message)
50
+ rescue StandardError => e
51
+ puts "Pubnub :: ENCRYPTION ERROR: #{e}"
52
+ nil
53
+ end
54
+
55
+ def decrypt(data)
56
+ encrypted_data = data.data
57
+ iv = if @iv.nil? && encrypted_data.length >= BLOCK_SIZE
58
+ encrypted_data.slice!(0..(BLOCK_SIZE - 1)) if encrypted_data.length >= BLOCK_SIZE
59
+ else
60
+ @iv
61
+ end
62
+ if iv.length != BLOCK_SIZE
63
+ puts "Pubnub :: DECRYPTION ERROR: Unexpected initialization vector length: #{data.metadata.length} bytes (#{BLOCK_SIZE} bytes is expected)"
64
+ return nil
65
+ end
66
+
67
+ unless encrypted_data.length.positive?
68
+ puts 'Pubnub :: DECRYPTION ERROR: Empty data for decryption'
69
+ return nil
70
+ end
71
+
72
+ cipher = OpenSSL::Cipher.new(@alg).decrypt
73
+ cipher.key = @cipher_key
74
+ cipher.iv = iv
75
+
76
+ decrypted = cipher.update encrypted_data
77
+ decrypted << cipher.final
78
+ rescue StandardError => e
79
+ puts "Pubnub :: DECRYPTION ERROR: #{e}"
80
+ nil
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pubnub/modules/crypto/cryptor'
4
+ require 'pubnub/modules/crypto/cryptors/aes_cbc_cryptor'
5
+ require 'pubnub/modules/crypto/cryptors/legacy_cryptor'
6
+ require 'pubnub/modules/crypto/crypto_provider'
7
+ require 'pubnub/modules/crypto/cryptor_header'
8
+ require 'pubnub/modules/crypto/crypto_module'
@@ -33,13 +33,12 @@ module Pubnub
33
33
  end
34
34
 
35
35
  def decipher_payload(message)
36
- return message[:payload] if message[:channel].end_with?('-pnpres') || (@app.env[:cipher_key].nil? && @cipher_key.nil? && @cipher_key_selector.nil? && @env[:cipher_key_selector].nil?)
37
- data = message.reject { |k, _v| k == :payload }
38
- cipher_key = compute_cipher_key(data)
39
- random_iv = compute_random_iv(data)
40
- crypto = Pubnub::Crypto.new(cipher_key, random_iv)
41
- JSON.parse(crypto.decrypt(message[:payload]), quirks_mode: true)
42
- rescue StandardError
36
+ # TODO: Uncomment code below when cryptor implementations will be added.
37
+ return message[:payload] if message[:channel].end_with?('-pnpres') || crypto_module.nil?
38
+
39
+ encrypted_message = Base64.decode64(message[:payload])
40
+ JSON.parse(crypto_module.decrypt(encrypted_message), quirks_mode: true)
41
+ rescue StandardError, UnknownCryptorError
43
42
  message[:payload]
44
43
  end
45
44
 
@@ -51,7 +50,8 @@ module Pubnub
51
50
  # STATUS
52
51
  envelopes = if messages.empty?
53
52
  [plain_envelope(req_res_objects, timetoken)]
54
- else # RESULT
53
+ else
54
+ # RESULT
55
55
  messages.map do |message|
56
56
  encrypted_envelope(req_res_objects, message, timetoken)
57
57
  end
@@ -1,4 +1,4 @@
1
1
  # Toplevel Pubnub module.
2
2
  module Pubnub
3
- VERSION = '5.2.2'.freeze
3
+ VERSION = '5.3.0'.freeze
4
4
  end
data/pubnub.gemspec CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
9
9
  spec.email = ['support@pubnub.com']
10
10
  spec.summary = 'PubNub Official Ruby gem.'
11
11
  spec.description = 'Ruby anywhere in the world in 250ms with PubNub!'
12
- spec.homepage = 'http://github.com/pubnub/ruby'
13
- spec.license = 'MIT'
12
+ spec.homepage = 'https://github.com/pubnub/ruby'
13
+ spec.licenses = %w[MIT LicenseRef-LICENSE]
14
14
 
15
15
  spec.files = `git ls-files -z`.split("\x0").grep_v(/^(test|spec|fixtures)/)
16
16
  spec.executables = spec.files.grep(%r{^bin\/}) { |f| File.basename(f) }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pubnub
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.2
4
+ version: 5.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - PubNub
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-15 00:00:00.000000000 Z
11
+ date: 2023-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -143,7 +143,7 @@ files:
143
143
  - CHANGELOG.md
144
144
  - Gemfile
145
145
  - Gemfile.lock
146
- - LICENSE.txt
146
+ - LICENSE
147
147
  - Pubnub Ruby SDK upgrade guide.md
148
148
  - README.md
149
149
  - Rakefile
@@ -151,6 +151,8 @@ files:
151
151
  - config/cucumber.yml
152
152
  - docs.md
153
153
  - features/step_definitions/access_steps.rb
154
+ - features/step_definitions/crypto_steps.rb
155
+ - features/support/cryptor.rb
154
156
  - features/support/env.rb
155
157
  - features/support/helper.rb
156
158
  - features/support/hooks.rb
@@ -171,7 +173,6 @@ files:
171
173
  - lib/pubnub/client/paged_history.rb
172
174
  - lib/pubnub/configuration.rb
173
175
  - lib/pubnub/constants.rb
174
- - lib/pubnub/crypto.rb
175
176
  - lib/pubnub/envelope.rb
176
177
  - lib/pubnub/error.rb
177
178
  - lib/pubnub/error_envelope.rb
@@ -222,6 +223,13 @@ files:
222
223
  - lib/pubnub/format.rb
223
224
  - lib/pubnub/formatter.rb
224
225
  - lib/pubnub/heart.rb
226
+ - lib/pubnub/modules/crypto/crypto_module.rb
227
+ - lib/pubnub/modules/crypto/crypto_provider.rb
228
+ - lib/pubnub/modules/crypto/cryptor.rb
229
+ - lib/pubnub/modules/crypto/cryptor_header.rb
230
+ - lib/pubnub/modules/crypto/cryptors/aes_cbc_cryptor.rb
231
+ - lib/pubnub/modules/crypto/cryptors/legacy_cryptor.rb
232
+ - lib/pubnub/modules/crypto/module.rb
225
233
  - lib/pubnub/origin_manager.rb
226
234
  - lib/pubnub/pam.rb
227
235
  - lib/pubnub/schemas/envelope_schema.rb
@@ -281,9 +289,10 @@ files:
281
289
  - lib/pubnub/validators/where_now.rb
282
290
  - lib/pubnub/version.rb
283
291
  - pubnub.gemspec
284
- homepage: http://github.com/pubnub/ruby
292
+ homepage: https://github.com/pubnub/ruby
285
293
  licenses:
286
294
  - MIT
295
+ - LicenseRef-LICENSE
287
296
  metadata: {}
288
297
  post_install_message:
289
298
  rdoc_options: []
@@ -306,6 +315,8 @@ specification_version: 4
306
315
  summary: PubNub Official Ruby gem.
307
316
  test_files:
308
317
  - features/step_definitions/access_steps.rb
318
+ - features/step_definitions/crypto_steps.rb
319
+ - features/support/cryptor.rb
309
320
  - features/support/env.rb
310
321
  - features/support/helper.rb
311
322
  - features/support/hooks.rb
data/LICENSE.txt DELETED
@@ -1,30 +0,0 @@
1
- PubNub Real-time Cloud-Hosted Push API and Push Notification Client Frameworks
2
- Copyright (c) 2013-2014 PubNub Inc.
3
- http://www.pubnub.com/
4
- http://www.pubnub.com/terms
5
-
6
- MIT License
7
-
8
- Permission is hereby granted, free of charge, to any person obtaining
9
- a copy of this software and associated documentation files (the
10
- "Software"), to deal in the Software without restriction, including
11
- without limitation the rights to use, copy, modify, merge, publish,
12
- distribute, sublicense, and/or sell copies of the Software, and to
13
- permit persons to whom the Software is furnished to do so, subject to
14
- the following conditions:
15
-
16
- The above copyright notice and this permission notice shall be
17
- included in all copies or substantial portions of the Software.
18
-
19
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
-
27
- PubNub Real-time Cloud-Hosted Push API and Push Notification Client Frameworks
28
- Copyright (c) 2013-2014 PubNub Inc.
29
- http://www.pubnub.com/
30
- http://www.pubnub.com/terms
data/lib/pubnub/crypto.rb DELETED
@@ -1,70 +0,0 @@
1
- # Toplevel Pubnub module.
2
- module Pubnub
3
- # Internal Crypto class used for message encryption and decryption
4
- class Crypto
5
- def initialize(cipher_key, use_random_iv)
6
- @alg = 'AES-256-CBC'
7
- sha256_key = Digest::SHA256.hexdigest(cipher_key.to_s)
8
- @key = sha256_key.slice(0, 32)
9
- @using_random_iv = use_random_iv
10
- @iv = @using_random_iv == true ? random_iv : '0123456789012345'
11
- end
12
-
13
- def encrypt(message)
14
- aes = OpenSSL::Cipher.new(@alg)
15
- aes.encrypt
16
- aes.key = @key
17
- aes.iv = @iv
18
-
19
- json_message = message.to_json
20
- cipher = @using_random_iv == true ? @iv : ''
21
- cipher << aes.update(json_message)
22
- cipher << aes.final
23
-
24
- Base64.strict_encode64(cipher)
25
- end
26
-
27
- def decrypt(cipher_text)
28
- undecoded_text = Base64.decode64(cipher_text)
29
- iv = @iv
30
-
31
- if cipher_text.length > 16 && @using_random_iv == true
32
- iv = undecoded_text.slice!(0..15)
33
- end
34
-
35
- decode_cipher = OpenSSL::Cipher.new(@alg).decrypt
36
- decode_cipher.key = @key
37
- decode_cipher.iv = iv
38
-
39
- plain_text = decryption(undecoded_text, decode_cipher)
40
- load_json(plain_text)
41
-
42
- Pubnub.logger.debug('Pubnub') { 'Finished decrypting' }
43
-
44
- plain_text
45
- end
46
-
47
- private
48
-
49
- def decryption(cipher_text, decode_cipher)
50
- plain_text = decode_cipher.update(cipher_text)
51
- plain_text << decode_cipher.final
52
- rescue StandardError => e
53
- Pubnub.error('Pubnub') { "DECRYPTION ERROR #{e}" }
54
- '"DECRYPTION ERROR"'
55
- end
56
-
57
- def load_json(plain_text)
58
- JSON.load(plain_text)
59
- rescue JSON::ParserError
60
- JSON.load("[#{plain_text}]")[0]
61
- end
62
-
63
- private
64
-
65
- def random_iv
66
- random_bytes = Random.new.bytes(16).unpack('NnnnnN')
67
- format('%08x%04x%04x', *random_bytes)
68
- end
69
- end
70
- end