Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add crypto module #151

Merged
merged 21 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b1742bf
fix: ¯\_(ツ)_/¯
MikeDobrzan Sep 28, 2023
1064993
fix: ¯\_(ツ)_/¯
MikeDobrzan Sep 28, 2023
80398f6
refactor(crypto): decouple crypto into module
parfeon Sep 29, 2023
0d642c5
fix(crypto): revert crypto module name
parfeon Sep 29, 2023
486c2e8
fix: use proper module when initialize CryptoModule
parfeon Sep 29, 2023
5b7011e
Merge remote-tracking branch 'origin/cryptors' into CLEN-1588/decoupl…
parfeon Sep 29, 2023
ec5759c
fix(crypto): fix issues discovered with unit tests
parfeon Sep 29, 2023
3ce6863
chore: update direct 'addressable' dependency
parfeon Sep 29, 2023
603f24f
chore: update Gemfile.lock
parfeon Sep 29, 2023
5a7f6c9
refactor(crypto): move old crypto to specs
parfeon Oct 1, 2023
ac463fd
fix: fix platforms list in lock file
parfeon Oct 1, 2023
0b3efcc
chore: revert Gemfile lock to master
parfeon Oct 1, 2023
706f4c3
chore(codeowners): update codeowners list
parfeon Oct 1, 2023
7c38f0a
test: update steps definition for new feature steps
parfeon Oct 2, 2023
0119565
fix: fix errors discovered with contract tests
parfeon Oct 3, 2023
1158348
refactor: apply changes after review
parfeon Oct 3, 2023
ecd6870
Merge branch 'master' into CLEN-1588/decouple-crypto-module
parfeon Oct 3, 2023
220f87a
docs(license): update license
parfeon Oct 16, 2023
824f414
Merge branch 'CLEN-1588/decouple-crypto-module' of github.com:pubnub/…
parfeon Oct 16, 2023
c8c377b
docs(license): update license file
parfeon Oct 16, 2023
1a083f6
PubNub SDK v5.3.0 release.
pubnub-release-bot Oct 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ruby jruby-9.3.8.0
ruby 3.2.2
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ end

group :development, :test do
gem 'awesome_print'
gem 'pry'
gem 'pry', '>= 0.14.2'
gem 'pry-rescue'
gem 'pry-stack_explorer'
end
10 changes: 5 additions & 5 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ GEM
dry-equalizer (~> 0.2)
dry-initializer (~> 3.0)
dry-schema (~> 1.5)
ffi (1.13.1)
ffi (1.13.1-java)
ffi (1.16.2)
ffi (1.16.2-java)
hashdiff (1.0.1)
httpclient (2.8.3)
interception (0.5)
Expand All @@ -115,10 +115,10 @@ GEM
parallel (1.19.2)
parser (2.7.1.4)
ast (~> 2.4.1)
pry (0.13.1)
pry (0.14.2)
coderay (~> 1.1)
method_source (~> 1.0)
pry (0.13.1-java)
pry (0.14.2-java)
coderay (~> 1.1)
method_source (~> 1.0)
spoon (~> 0.0)
Expand Down Expand Up @@ -187,7 +187,7 @@ DEPENDENCIES
awesome_print
codacy-coverage
cucumber
pry
pry (>= 0.14.2)
pry-rescue
pry-stack_explorer
pubnub!
Expand Down
2 changes: 0 additions & 2 deletions features/step_definitions/access_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -254,5 +254,3 @@
Then('the error detail message is not empty') do
expect(parse_error_body(@global_state[:last_call_res])["error"]["message"].empty?).to eq false
end


99 changes: 99 additions & 0 deletions features/step_definitions/crypto_steps.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# frozen_string_literal: true
#
require 'pubnub'

Given(/^Crypto module with '([^']*)' cryptor$/) do |cryptor_id|
@cryptor_ids = [cryptor_id]
end

Given(/^Crypto module with default '([^']*)' and additional '([^']*)' cryptors$/) do |cryptor_id1, cryptor_id2|
@cryptor_ids = [cryptor_id1, cryptor_id2]
end

Given(/^Legacy code with '([^']*)' cipher key and '(random|constant|-)' vector$/) do |cipher_key, use_random_iv|
use_random_iv = use_random_iv != 'constant'
@legacy_cryptor = Cryptor.new cipher_key, use_random_iv
end

Then(/^with '([^']*)' cipher key$/) do |cipher_key|
@cipher_key = cipher_key
end

Then(/^with '(random|constant|-)' vector$/) do |use_random_iv|
@use_random_iv = use_random_iv != 'constant'
end

When(/^I encrypt '([^']*)' file as '([^']*)'$/) do |file_name, _|
@source_file_name = file_name
@source_file_content = File.binread "sdk-specifications/features/encryption/assets/#{file_name}"
@encrypted_content = crypto_module.encrypt @source_file_content
if file_name.include? 'empty'
@encrypt_status = 'encryption error' if @encrypted_content.nil? && @encrypt_status.nil?
@encrypt_status = 'success' if !@encrypted_content.nil? && @encrypt_status.nil?
else
expect(@encrypted_content).not_to eq nil
end
end

When(/^I decrypt '([^']*)' file$/) do |file_name|
file_content = File.binread "sdk-specifications/features/encryption/assets/#{file_name}"

begin
@decrypted_content = crypto_module.decrypt file_content
rescue Pubnub::UnknownCryptorError
@decrypt_status = 'unknown cryptor error'
end
@decrypt_status = 'decryption error' if @decrypted_content.nil? && @decrypt_status.nil?
@decrypt_status = 'success' if !@decrypted_content.nil? && @decrypt_status.nil?
end

When(/^I decrypt '([^']*)' file as '([^']*)'$/) do |file_name, _|
file_content = File.binread "sdk-specifications/features/encryption/assets/#{file_name}"

begin
@decrypted_content = crypto_module.decrypt file_content
rescue Pubnub::UnknownCryptorError
@decrypt_status = 'unknown cryptor error'
end
@decrypt_status = 'decryption error' if @decrypted_content.nil? && @decrypt_status.nil?
@decrypt_status = 'success' if !@decrypted_content.nil? && @decrypt_status.nil?
end

Then(/^Decrypted file content equal to the '([^']*)' file content$/) do |file_name|
file_content = File.binread "sdk-specifications/features/encryption/assets/#{file_name}"
expect(@decrypted_content).not_to eq nil
expect(@decrypted_content).to eq file_content
end

Then('Successfully decrypt an encrypted file with legacy code') do
expect(@legacy_cryptor).not_to eq nil
base64_encoded = Base64.strict_encode64(@encrypted_content)
decrypted_content = @legacy_cryptor.decrypt(base64_encoded)
expect(decrypted_content).not_to eq nil
expect(decrypted_content).to eq @source_file_content
end

Then(/^I receive '([^']*)'$/) do |outcome|
expect(@encrypt_status || @decrypt_status).not_to eq nil
expect(@encrypt_status || @decrypt_status).to eq outcome
end

# Crypto module
#
# @return [Pubnub::Crypto::CryptoModule] Crypto module instance.
def crypto_module
cryptors = []
@cryptor_ids.each do |cryptor_id|
cryptor = if cryptor_id == 'acrh'
Pubnub::Crypto::AesCbcCryptor.new @cipher_key
elsif cryptor_id == 'legacy'
Pubnub::Crypto::LegacyCryptor.new @cipher_key, @use_random_iv
end
cryptors.push(cryptor) unless cryptor.nil?
end

raise ArgumentError, "No crypto identifiers specified: #{@cryptor_ids}" if cryptors.empty?

default_cryptor = cryptors.shift
Pubnub::Crypto::CryptoModule.new default_cryptor, cryptors unless default_cryptor.nil?
end
58 changes: 58 additions & 0 deletions features/support/cryptor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Internal Crypto class used for message encryption and decryption
class Cryptor
def initialize(cipher_key, use_random_iv)
@alg = 'AES-256-CBC'
sha256_key = Digest::SHA256.hexdigest(cipher_key.to_s)
@key = sha256_key.slice(0, 32)
@using_random_iv = use_random_iv
@iv = @using_random_iv == true ? random_iv : '0123456789012345'
end

def encrypt(message)
aes = OpenSSL::Cipher.new(@alg)
aes.encrypt
aes.key = @key
aes.iv = @iv

json_message = message.to_json
cipher = @using_random_iv == true ? @iv : ''
cipher << aes.update(json_message)
cipher << aes.final

Base64.strict_encode64(cipher)
end

def decrypt(cipher_text)
undecoded_text = Base64.decode64(cipher_text)
iv = @iv

if cipher_text.length > 16 && @using_random_iv == true
iv = undecoded_text.slice!(0..15)
end

decode_cipher = OpenSSL::Cipher.new(@alg).decrypt
decode_cipher.key = @key
decode_cipher.iv = iv

plain_text = decryption(undecoded_text, decode_cipher)

plain_text
end

private

def decryption(cipher_text, decode_cipher)
plain_text = decode_cipher.update(cipher_text)
plain_text << decode_cipher.final
rescue StandardError => e
puts "Pubnub :: DECRYPTION ERROR: #{e}"
'"DECRYPTION ERROR"'
end

private

def random_iv
random_bytes = Random.new.bytes(16).unpack('NnnnnN')
format('%08x%04x%04x', *random_bytes)
end
end
1 change: 0 additions & 1 deletion features/support/hooks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
@pn_configuration = {}

when_mock_server_used {
puts "Using mock"
expect(ENV['SERVER_HOST']).not_to be_nil
expect(ENV['SERVER_PORT']).not_to be_nil
@pn_configuration = {
Expand Down
31 changes: 30 additions & 1 deletion lib/pubnub/client.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
# frozen_string_literal: true

require 'base64'

require 'pubnub/error'
require 'pubnub/uuid'
require 'pubnub/formatter'
require 'pubnub/crypto'
require 'pubnub/constants'
require 'pubnub/configuration'
require 'pubnub/subscribe_callback'

require 'pubnub/modules/crypto/module'

require 'pubnub/schemas/envelope_schema'

require 'pubnub/event'
Expand Down Expand Up @@ -183,6 +186,7 @@ def initialize(options)
clean_env
prepare_env
validate! @env
setup_crypto_module
@telemetry = Telemetry.new
Pubnub.logger.debug('Pubnub::Client') do
"Created new Pubnub::Client instance. Version: #{Pubnub::VERSION}"
Expand Down Expand Up @@ -339,6 +343,14 @@ def set_token(token)
@env[:token] = token
end

# Data processing crypto module.
#
# @return [Pubnub::Crypto::CryptoProvider, nil] Crypto module for data encryption and
# decryption.
def crypto_module
@env[:crypto_module]
end

private

def create_state_pools(event)
Expand Down Expand Up @@ -386,9 +398,26 @@ def setup_app(options)
Concurrent.global_logger = Pubnub.logger
@subscriber = Subscriber.new(self)
options[:user_id] = options[:uuid] if options[:user_id].nil?

if options[:cipher_key] && options[:crypto_module]
puts 'It is expected that only cipherKey or cryptoModule will be configured ' \
'at once. PubNub client will use the configured cryptoModule.'
end

@env = options
end

# Complete crypto module configuration
# Create crypto module if it is required by user (specified
# <i>cipher_key</i> and not <i>crypto_module</i>).
def setup_crypto_module
random_iv = @env[:random_iv]
key = @env[:cipher_key]

# Create crypto module if it is not specified
@env[:crypto_module] = Crypto::CryptoModule.new_legacy(key, random_iv) if key && @env[:crypto_module].nil?
end

def prepare_env
assign_defaults
setup_pools
Expand Down
70 changes: 0 additions & 70 deletions lib/pubnub/crypto.rb

This file was deleted.

3 changes: 3 additions & 0 deletions lib/pubnub/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,7 @@ class RequestError < Error

class ResponseError < Error
end

class UnknownCryptorError < Error
end
end
Loading