lib/ably/util/crypto.rb in ably-0.1.6 vs lib/ably/util/crypto.rb in ably-0.2.0

- old
+ new

@@ -5,70 +5,98 @@ class Crypto DEFAULTS = { algorithm: 'AES', mode: 'CBC', key_length: 128, - block_length: 16 } + BLOCK_LENGTH = 16 + + # Configured options for this Crypto object, see {#initialize} for a list of configured options + # + # @return [Hash] attr_reader :options - def initialize(options = {}) - raise ArgumentError, ":secret is required" unless options.has_key?(:secret) + # 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} + # + # @return [Ably::Util::Crypto] + # + # @example + # crypto = Ably::Util::Crypto.new(key: 'mysecret') + # 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 end - def encrypt(payload) + # 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 + # + def encrypt(payload, encrypt_options = {}) cipher = openssl_cipher cipher.encrypt - cipher.key = secret - iv = cipher.random_iv + cipher.key = key + iv = encrypt_options[:iv] || options[:iv] || cipher.random_iv cipher.iv = iv - iv << cipher.update(pack(payload)) << cipher.final + iv << cipher.update(payload) << cipher.final end + # Decrypt payload using configured Cipher + # + # @param [String] encrypted_payload_with_iv the encrypted payload to be decrypted + # + # @return [String] + # def decrypt(encrypted_payload_with_iv) - raise Ably::Exceptions::EncryptionError, "iv is missing" unless encrypted_payload_with_iv.length >= block_length*2 + raise Ably::Exceptions::EncryptionError, 'iv is missing or not long enough' unless encrypted_payload_with_iv.length >= BLOCK_LENGTH*2 - iv = encrypted_payload_with_iv.slice(0..15) - encrypted_payload = encrypted_payload_with_iv.slice(16..-1) + iv = encrypted_payload_with_iv.slice(0...BLOCK_LENGTH) + encrypted_payload = encrypted_payload_with_iv.slice(BLOCK_LENGTH..-1) decipher = openssl_cipher decipher.decrypt - decipher.key = secret + decipher.key = key decipher.iv = iv - unpack(decipher.update(encrypted_payload) << decipher.final) + 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 - private - def pack(object) - object.to_msgpack + # The Cipher algorithm string such as AES-128-CBC + # @return [String] + def cipher_type + (options[:combined] || "#{options[:algorithm]}-#{options[:key_length]}-#{options[:mode]}").to_s.upcase end - def unpack(msgpack_binary) - MessagePack.unpack(msgpack_binary) - end - - def secret - options[:secret] - end - - def block_length - options[:block_length] - end - - def cipher_type - "#{options[:algorithm]}-#{options[:key_length]}-#{options[:mode]}" + private + def key + options[:key] end def openssl_cipher OpenSSL::Cipher.new(cipher_type) end