-
Notifications
You must be signed in to change notification settings - Fork 89
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(crypto): decouple crypto into module
Decouple code which works with data encryption and decryption into separate module.
- Loading branch information
Showing
11 changed files
with
531 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,4 +51,7 @@ class RequestError < Error | |
|
||
class ResponseError < Error | ||
end | ||
|
||
class UnknownCryptorError < Error | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
# frozen_string_literal: true | ||
|
||
module Pubnub | ||
# Crypto module for data processing. | ||
# | ||
# The PubNub client uses a module to encrypt and decrypt sent data in a way | ||
# that's compatible with previous versions (if additional cryptors have been | ||
# registered). | ||
class CryptoModule < Crypto::CryptoProvider | ||
# Legacy AES-CBC cryptor based module. | ||
# | ||
# Data encryption and decryption will be done by default using the | ||
# <i>LegacyCrypto</i>. In addition to the <i>LegacyCrypto</i> for data | ||
# decryption, the <i>AesCbcCryptor</i> will be registered for | ||
# future-compatibility (which will help with gradual application updates). | ||
# | ||
# @param cipher_key [String] Key for data encryption and decryption. | ||
# @param use_random_iv [Boolean] Whether random IV should be used for data decryption. | ||
def self.new_aes_cbc(cipher_key, use_random_iv) | ||
# TODO: Create AES-CBC based crypto module | ||
end | ||
|
||
# Legacy AES-CBC cryptor based module. | ||
# | ||
# Data encryption and decryption will be done by default using the | ||
# <i>LegacyCrypto</i>. In addition to the <i>LegacyCrypto</i> for data | ||
# decryption, the <i>AesCbcCryptor</i> will be registered for | ||
# future-compatibility (which will help with gradual application updates). | ||
# | ||
# @param cipher_key [String] Key for data encryption and decryption. | ||
# @param use_random_iv [Boolean] Whether random IV should be used for data decryption. | ||
def self.new_legacy(cipher_key, use_random_iv) | ||
# TODO: Create legacy AES-CBC based crypto module | ||
end | ||
|
||
# Create crypto module. | ||
# @param default [Cryptor] Default cryptor used to encrypt and decrypt data. | ||
# @param cryptors [Array<Cryptor>, nil] Additional cryptors which will be | ||
# used to decrypt data encrypted by previously used cryptors. | ||
def initialize(default, cryptors) | ||
if default.nil? | ||
raise ArgumentError, { | ||
message: '\'default\' cryptor required for data encryption.' | ||
} | ||
end | ||
|
||
@default = default | ||
@cryptors = cryptors&.each_with_object({}) do |value, hash| | ||
hash[value.identifier] = value | ||
end || {} | ||
end | ||
|
||
def encrypt(data) | ||
# Encrypting provided data. | ||
encrypted_data = default_cryptor.encrypt(data) | ||
return nil if encrypted_data.nil? | ||
|
||
payload = Crypto::CryptorHeader.new(default_cryptor.identifier, encrypted_data.metadata).to_s | ||
payload << encrypted_data.metadata unless encrypted_data.metadata.nil? | ||
payload << encrypted_data.data | ||
end | ||
|
||
def decrypt(data) | ||
header = Crypto::CryptorHeader.parse(data) | ||
cryptor = cryptor(header&.identifier || '\x00\x00\x00\x00') | ||
|
||
# Check whether there is a cryptor to decrypt data or not. | ||
if cryptor.nil? | ||
identifier = header&.identifier || 'UNKN' | ||
raise UnknownCryptorError, { | ||
message: "Decrypting data created by unknown cryptor. Please make sure to register | ||
#{identifier} or update SDK." | ||
} | ||
end | ||
|
||
metadata_size = header&.data_size || 0 | ||
metadata = metadata_size.positive? ? data[0..metadata_size] : nil | ||
data = data[metadata_size..-1] | ||
|
||
return nil if data.nil? | ||
|
||
cryptor.decrypt(EncryptedData.new(data, metadata)) | ||
end | ||
|
||
private | ||
|
||
# Cryptor used by the module by default to encrypt data. | ||
# | ||
# @return [Cryptor] Default cryptor used to encrypt and decrypt data. | ||
def default_cryptor | ||
@default | ||
end | ||
|
||
# Additional cryptors that can be used to decrypt data if the | ||
# default_cryptor can't. | ||
# | ||
# @return [Hash<String, Cryptor>] Map of Cryptor to their identifiers. | ||
def additional_cryptors | ||
@cryptors | ||
end | ||
|
||
# Find cryptor with a specified identifier. | ||
# | ||
# Data decryption can only be done with registered cryptors. An identifier | ||
# in the cryptor data header is used to identify a suitable cryptor. | ||
# | ||
# @param identifier [String] A unicode cryptor identifier. | ||
# @return [Cryptor, nil] Target cryptor or `nil` in case there is none with | ||
# the specified identifier. | ||
def cryptor(identifier) | ||
return default_cryptor if default_cryptor.identifier == identifier | ||
|
||
additional_cryptors.fetch(identifier, nil) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# frozen_string_literal: true | ||
|
||
module Pubnub | ||
module Crypto | ||
# Base class which is used to implement a module that can be used to | ||
# configure <b>PubNub</b> client or for manual data encryption and | ||
# decryption. | ||
class CryptoProvider | ||
# Encrypt provided data. | ||
# | ||
# @param data [String] Source data for encryption. | ||
# @return [String, nil] Encrypted data or <i>nil</i> in case of encryption | ||
# error. | ||
def encrypt(data) | ||
raise NotImplementedError, 'Subclass should provide "encrypt" method implementation.' | ||
end | ||
|
||
# Decrypt provided data. | ||
# | ||
# @param data [String] Encrypted data for decryption. | ||
# @return [String, nil] Decrypted data or <i>nil</i> in case of decryption | ||
# error. | ||
# | ||
# @raise [UnknownCryptorError] If the <i>cryptor</i> for data processing is | ||
# not registered. | ||
def decrypt(data) | ||
raise NotImplementedError, 'Subclass should provide "decrypt" method implementation.' | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# frozen_string_literal: true | ||
|
||
module Pubnub | ||
module Crypto | ||
# Encrypted data representation object. | ||
# | ||
# Objects contain both encrypted data and additional data created by cryptor | ||
# that will be required to decrypt the data. | ||
class EncryptedData | ||
# Cryptor may provide here any information which will be useful when data | ||
# should be decrypted. | ||
# | ||
# For example <i>metadata</i> may contain: | ||
# * initialization vector | ||
# * cipher key identifier | ||
# * encrypted <i>data</i> length | ||
# | ||
# @return [String, nil] Cryptor-defined information. | ||
attr_reader :metadata | ||
|
||
# Encrypted data. | ||
# | ||
# @return [String] Encrypted data. | ||
attr_reader :data | ||
|
||
# Create encrypted data object. | ||
# | ||
# An object used to keep track of the results of data encryption and the | ||
# additional data the <i>cryptor</i> needs to handle it later. | ||
# | ||
# @param data [String] Outcome of successful cryptor <i>encrypt</i> method | ||
# call. | ||
# @param metadata [String, nil] Additional information is provided by | ||
# <i>cryptor</i> so that encrypted data can be handled later. | ||
def initialize(data, metadata = nil) | ||
@data = data | ||
@metadata = metadata | ||
end | ||
end | ||
|
||
# Base class which is used to implement cryptor that should be used with | ||
# <i>CryptorProvider</i> implementation for data encryption and decryption. | ||
class Cryptor | ||
# Identifier will be encoded into cryptor data header and passed along | ||
# with encrypted <i>data</i> and <i>metadata</i>. | ||
# | ||
# The identifier <b>must</b> be 4 bytes long. | ||
# @return [String] Unique cryptor identifier. | ||
def identifier | ||
raise NotImplementedError, 'Subclass should provide "identifier" method implementation.' | ||
end | ||
|
||
# Encrypt provided data. | ||
# | ||
# @param data [String] Source data for encryption. | ||
# @return [EncryptedData, nil] Encrypted data or <i>nil</i> in case of | ||
# encryption error. | ||
def encrypt(data) | ||
raise NotImplementedError, 'Subclass should provide "encrypt" method implementation.' | ||
end | ||
|
||
# Decrypt provided data. | ||
# | ||
# @param data [EncryptedData] Encrypted data for decryption. | ||
# @return [String, nil] Decrypted data or <i>nil</i> in case of decryption | ||
# error. | ||
def decrypt(data) | ||
raise NotImplementedError, 'Subclass should provide "decrypt" method implementation.' | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.