File: encryption_utils.rb

package info (click to toggle)
ruby-aws-sdk 1.66.0-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 6,808 kB
  • ctags: 4,854
  • sloc: ruby: 28,354; makefile: 7
file content (146 lines) | stat: -rw-r--r-- 5,041 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# Copyright 2011-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
#     http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.

require 'openssl'

module AWS
  class S3
    # @api private
    module EncryptionUtils

      protected

      UNSAFE_MSG = "Unsafe encryption, data is longer than key length"

      # @param [OpenSSL::PKey::RSA, String] key Key used to encrypt.
      #
      # @param [String] data Data to be encrypted.
      #
      # @note Use check_encryption_materials before this method to check
      #   formatting of keys.
      # @note This should not be used for data longer than the key length as
      #   it will not be cryptographically safe.
      #
      # @return [String] Returns the data encrypted with the key given.
      def encrypt data, key
        rsa = OpenSSL::PKey::RSA
        data_cipher_size = get_cipher_size(data.length)

        # Encrypting data key
        case key
        when rsa # Asymmetric encryption
          warn UNSAFE_MSG if key.public_key.n.num_bits < data_cipher_size
          key.public_encrypt(data)
        when String             # Symmetric encryption
          warn UNSAFE_MSG if get_cipher_size(key.length) < data_cipher_size
          cipher = get_aes_cipher(:encrypt, :ECB, key)
          cipher.update(data) + cipher.final
        end
      end

      # @param [OpenSSL::PKey::RSA, String] key Key used to encrypt.
      #
      # @param [String] data Data to be encrypted.
      #
      # @note Use check_encryption_materials before this method to check
      #   formatting of keys
      #
      # @return [String] Returns the data decrypted with the key given.
      def decrypt data, key
        rsa = OpenSSL::PKey::RSA
        begin
          case key
          when rsa # Asymmetric Decryption
              key.private_decrypt(data)
          when String             # Symmetric Decryption
              cipher = get_aes_cipher(:decrypt, :ECB, key)
              cipher.update(data) + cipher.final
          end
        rescue OpenSSL::Cipher::CipherError
          raise RuntimeError, "decryption failed, incorrect key?"
        end
      end

      # Checks for any formatting problems for keys and initialization vectors
      #   supported with EncryptionUtils.
      def check_encryption_materials mode, key
        rsa = OpenSSL::PKey::RSA
        case key
        when rsa
          unless key.private? or mode == :encrypt
            msg = "invalid key, #{rsa} requires a private key"
            raise ArgumentError, msg
          end
        when String # no problem
        else
          msg = "invalid key, must be an #{rsa} or a cipher key string"
          raise ArgumentError, msg
        end
      end

      # @param [OpenSSL::Cipher] cipher The cipher with configured key and iv.
      #
      # @yield [String, String] key_iv_pair A randomly generated key, iv pair
      #   for use with the given cipher.  Sets the key and iv on the cipher.
      def generate_aes_key cipher, &block
        key_iv_pair = [cipher.random_key, cipher.random_iv]
        yield(key_iv_pair) if block_given?
      end

      # @param [Symbol] mode The encryption/decryption mode.  Valid inputs are
      #   :encrypt or :decrypt
      #
      # @param [String] key Key for the cipher.
      #
      # @param [String] iv IV for the cipher.
      #
      # @return [OpenSSL::Cipher] Will return a configured `OpenSSL::Cipher`.
      def get_aes_cipher mode, block_mode, key = nil, iv = nil

        # If no key given, default to 256 bit
        cipher_size = (key) ? get_cipher_size(key.length) : 256

        cipher = OpenSSL::Cipher.new("AES-#{cipher_size}-#{block_mode}")

        (mode == :encrypt) ? cipher.encrypt : cipher.decrypt
        cipher.key = key if key
        cipher.iv  = iv  if iv
        cipher
      end

      # @param  [Integer] size Size of data given.
      # @return [Integer] Returns the AES encrypted size based on a given size.
      def get_encrypted_size size
        # The next multiple of 16
        ((size / 16) + 1) * 16
      end
      module_function :get_encrypted_size

      private

      # @param  [Fixnum] key_length Length of the key given.
      # @return [Fixnum] Returns the cipher size based on the key length.
      def get_cipher_size(key_length)
        case key_length
        when 32 then 256
        when 24 then 192
        when 16 then 128
        else
          msg = "invalid key, symmetric key required to be 16, 24, or 32 bytes "
          msg << "in length, saw length #{key_length}"
          raise ArgumentError, msg
        end
      end
    end
  end
end