lib/ably/util/crypto.rb in ably-0.8.8 vs lib/ably/util/crypto.rb in ably-0.8.9

- old
+ new

@@ -2,71 +2,84 @@ require 'openssl' module Ably::Util class Crypto DEFAULTS = { - algorithm: 'AES', - mode: 'CBC', - key_length: 128, + algorithm: 'aes', + mode: 'cbc', + key_length: 256, } BLOCK_LENGTH = 16 - # Configured options for this Crypto object, see {#initialize} for a list of configured options + # Configured {Ably::Models::CipherParams} for this Crypto object, see {#initialize} for a list of configureable options # - # @return [Hash] - attr_reader :options + # @return [Ably::Models::CipherParams] + attr_reader :cipher_params # Creates a {Ably::Util::Crypto} object # - # @param [Hash] options an options Hash used to configure the Crypto library - # @option options [String] :key Required secret key used for encrypting and decrypting - # @option options [String] :algorithm optional (default AES), specify the encryption algorithm supported by {http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html OpenSSL::Cipher} - # @option options [String] :mode optional (default CBC), specify the cipher mode supported by {http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html OpenSSL::Cipher} - # @option options [Integer] :key_length optional (default 128), specify the key length of the cipher supported by {http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html OpenSSL::Cipher} - # @option options [String] :combined optional (default AES-128-CBC), specify in one option the algorithm, key length and cipher of the cipher supported by {http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html OpenSSL::Cipher} + # @param [Hash] params a Hash used to configure the Crypto library's {Ably::Models::CipherParams} + # @option params (see Ably::Models::CipherParams#initialize) # # @return [Ably::Util::Crypto] # # @example - # crypto = Ably::Util::Crypto.new(key: 'mysecret') + # key = Ably::Util::Crypto.generate_random_key + # crypto = Ably::Util::Crypto.new(key: key) # encrypted = crypto.encrypt('secret text') # crypto.decrypt(decrypted) # => 'secret text' # - def initialize(options) - raise ArgumentError, ':key is required' unless options.has_key?(:key) - @options = DEFAULTS.merge(options).freeze + def initialize(params) + @fixed_iv = params.delete(:fixed_iv) if params.kind_of?(Hash) + @cipher_params = Ably::Models::CipherParams(params) end - # Obtain a default CipherParams. This uses default algorithm, mode and - # padding and key length. A key and IV are generated using the default - # system SecureRandom; the key may be obtained from the returned CipherParams + # Obtain a default {Ably::Models::CipherParams}. This uses default algorithm, mode and + # padding and key length. An IV is generated using the default + # system SecureRandom; the key may be obtained from the returned {Ably::Models::CipherParams} # for out-of-band distribution to other clients. + + # @param [Hash] params a Hash used to configure the Crypto library's {Ably::Models::CipherParams} + # @option params (see Ably::Models::CipherParams#initialize) # - # @return [Hash] CipherParam options Hash with attributes :key, :algorithn, :mode, :key_length + # @return [Ably::Models::CipherParams] Configured cipher params with :key, :algorithm, :mode, :key_length attributes # - def self.get_default_params(key = nil) - params = DEFAULTS.merge(key: key) - params[:key_length] = key.unpack('b*').first.length if params[:key] - cipher_type = "#{params[:algorithm]}-#{params[:key_length]}-#{params[:mode]}" - params[:key] = OpenSSL::Cipher.new(cipher_type.upcase).random_key unless params[:key] - params + def self.get_default_params(params = {}) + Ably::Models::CipherParams(params) end + # Generate a random encryption key from the supplied keylength (or the + # default key_length of 256 if none supplied) + # + # @param [Integer] key_length Optional (default 256) key length for the generated random key. 128 and 256 bit key lengths are supported + # @return Binary String (byte array) with ASCII_8BIT encoding + # + def self.generate_random_key(key_length = DEFAULTS.fetch(:key_length)) + params = DEFAULTS.merge(key_length: key_length) + OpenSSL::Cipher.new(cipher_type(params)).random_key + end + + # The Cipher algorithm string such as AES-128-CBC + # @api private + def self.cipher_type(options) + Ably::Models::CipherParams.cipher_type(options) + end + # Encrypt payload using configured Cipher # # @param [String] payload the payload to be encrypted # @param [Hash] encrypt_options an options Hash to configure the encrypt action # @option encrypt_options [String] :iv optionally use the provided Initialization Vector instead of a randomly generated IV # - # @return [String] binary string with {Encoding::ASCII_8BIT} encoding + # @return [String] binary string with +Encoding::ASCII_8BIT+ encoding # def encrypt(payload, encrypt_options = {}) cipher = openssl_cipher cipher.encrypt cipher.key = key - iv = encrypt_options[:iv] || options[:iv] || cipher.random_iv + iv = encrypt_options[:iv] || fixed_iv || cipher.random_iv cipher.iv = iv iv << cipher.update(payload) << cipher.final end @@ -88,33 +101,30 @@ decipher.iv = iv decipher.update(encrypted_payload) << decipher.final end - # Generate a random key - # @return [String] - def random_key - openssl_cipher.random_key - end - # Generate a random IV # @return [String] def random_iv openssl_cipher.random_iv end - # The Cipher algorithm string such as AES-128-CBC + private + # Used solely for tests to fix the IV instead of randomly generate one + attr_reader :fixed_iv + + # Generate a random key # @return [String] - def cipher_type - (options[:combined] || "#{options[:algorithm]}-#{options[:key_length]}-#{options[:mode]}").to_s.upcase + def random_key + openssl_cipher.random_key end - private def key - options[:key] + cipher_params.key end def openssl_cipher - OpenSSL::Cipher.new(cipher_type) + @openssl_cipher ||= OpenSSL::Cipher.new(cipher_params.cipher_type) end end end