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
|