pubnub 5.2.2 → 5.3.1
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.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +2 -3
- data/.pubnub.yml +16 -4
- data/.tool-versions +1 -1
- data/CHANGELOG.md +15 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +6 -6
- data/LICENSE.txt +24 -25
- data/VERSION +1 -1
- data/features/step_definitions/access_steps.rb +0 -2
- data/features/step_definitions/crypto_steps.rb +99 -0
- data/features/support/cryptor.rb +58 -0
- data/features/support/hooks.rb +0 -1
- data/lib/pubnub/client.rb +30 -1
- data/lib/pubnub/error.rb +3 -0
- data/lib/pubnub/event.rb +13 -5
- data/lib/pubnub/events/add_message_action.rb +2 -2
- data/lib/pubnub/events/grant_token.rb +1 -1
- data/lib/pubnub/events/history.rb +18 -6
- data/lib/pubnub/events/publish.rb +7 -3
- data/lib/pubnub/events/remove_channel_members.rb +3 -3
- data/lib/pubnub/events/remove_channel_metadata.rb +1 -1
- data/lib/pubnub/events/remove_memberships.rb +3 -3
- data/lib/pubnub/events/remove_uuid_metadata.rb +1 -1
- data/lib/pubnub/events/set_channel_members.rb +3 -3
- data/lib/pubnub/events/set_channel_metadata.rb +2 -2
- data/lib/pubnub/events/set_memberships.rb +3 -3
- data/lib/pubnub/events/set_uuid_metadata.rb +2 -2
- data/lib/pubnub/events/signal.rb +1 -1
- data/lib/pubnub/events/subscribe.rb +5 -0
- data/lib/pubnub/formatter.rb +22 -11
- data/lib/pubnub/modules/crypto/crypto_module.rb +159 -0
- data/lib/pubnub/modules/crypto/crypto_provider.rb +31 -0
- data/lib/pubnub/modules/crypto/cryptor.rb +73 -0
- data/lib/pubnub/modules/crypto/cryptor_header.rb +251 -0
- data/lib/pubnub/modules/crypto/cryptors/aes_cbc_cryptor.rb +67 -0
- data/lib/pubnub/modules/crypto/cryptors/legacy_cryptor.rb +84 -0
- data/lib/pubnub/modules/crypto/module.rb +8 -0
- data/lib/pubnub/subscribe_event/formatter.rb +8 -8
- data/lib/pubnub/version.rb +1 -1
- data/pubnub.gemspec +2 -2
- metadata +15 -5
- 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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
53
|
+
else
|
54
|
+
# RESULT
|
55
55
|
messages.map do |message|
|
56
56
|
encrypted_envelope(req_res_objects, message, timetoken)
|
57
57
|
end
|
data/lib/pubnub/version.rb
CHANGED
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 = '
|
13
|
-
spec.
|
12
|
+
spec.homepage = 'https://github.com/pubnub/ruby'
|
13
|
+
spec.licenses = ['LicenseRef-LICENSE.txt']
|
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.
|
4
|
+
version: 5.3.1
|
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
|
11
|
+
date: 2023-11-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -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,9 @@ files:
|
|
281
289
|
- lib/pubnub/validators/where_now.rb
|
282
290
|
- lib/pubnub/version.rb
|
283
291
|
- pubnub.gemspec
|
284
|
-
homepage:
|
292
|
+
homepage: https://github.com/pubnub/ruby
|
285
293
|
licenses:
|
286
|
-
-
|
294
|
+
- LicenseRef-LICENSE.txt
|
287
295
|
metadata: {}
|
288
296
|
post_install_message:
|
289
297
|
rdoc_options: []
|
@@ -306,6 +314,8 @@ specification_version: 4
|
|
306
314
|
summary: PubNub Official Ruby gem.
|
307
315
|
test_files:
|
308
316
|
- features/step_definitions/access_steps.rb
|
317
|
+
- features/step_definitions/crypto_steps.rb
|
318
|
+
- features/support/cryptor.rb
|
309
319
|
- features/support/env.rb
|
310
320
|
- features/support/helper.rb
|
311
321
|
- features/support/hooks.rb
|
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
|