summaryrefslogtreecommitdiff
path: root/lib/chef/encrypted_data_bag_item
diff options
context:
space:
mode:
authorXabier de Zuazo <xabier@onddo.com>2014-06-18 10:28:51 +0200
committerXabier de Zuazo <xabier@onddo.com>2014-07-01 10:59:26 +0200
commit7ffcfd2743a1ddedbdbcdb68e65fbe4731ab0b2e (patch)
treeaf75e1256a65c1488f9e44c4ee2d127e5dc90d20 /lib/chef/encrypted_data_bag_item
parent855b72b5803d525cf953b51ad1cd499c09a2bd37 (diff)
downloadchef-7ffcfd2743a1ddedbdbcdb68e65fbe4731ab0b2e.tar.gz
[CHEF-5356-gcm] Chef::EncryptedDataBagItem Version3 implementation using GCM
Diffstat (limited to 'lib/chef/encrypted_data_bag_item')
-rw-r--r--lib/chef/encrypted_data_bag_item/decryptor.rb47
-rw-r--r--lib/chef/encrypted_data_bag_item/encryption_failure.rb22
-rw-r--r--lib/chef/encrypted_data_bag_item/encryptor.rb76
3 files changed, 134 insertions, 11 deletions
diff --git a/lib/chef/encrypted_data_bag_item/decryptor.rb b/lib/chef/encrypted_data_bag_item/decryptor.rb
index 69b8d62e3b..82d04c0d67 100644
--- a/lib/chef/encrypted_data_bag_item/decryptor.rb
+++ b/lib/chef/encrypted_data_bag_item/decryptor.rb
@@ -45,6 +45,8 @@ class Chef::EncryptedDataBagItem
format_version = format_version_of(encrypted_value)
assert_format_version_acceptable!(format_version)
case format_version
+ when 3
+ Version3Decryptor.new(encrypted_value, key)
when 2
Version2Decryptor.new(encrypted_value, key)
when 1
@@ -83,6 +85,11 @@ class Chef::EncryptedDataBagItem
@key = key
end
+ # Returns the used decryption algorithm
+ def algorithm
+ ALGORITHM
+ end
+
def for_decrypted_item
YAML.load(decrypted_data)
end
@@ -102,7 +109,7 @@ class Chef::EncryptedDataBagItem
def openssl_decryptor
@openssl_decryptor ||= begin
- d = OpenSSL::Cipher::Cipher.new(ALGORITHM)
+ d = OpenSSL::Cipher::Cipher.new(algorithm)
d.decrypt
d.pkcs5_keyivgen(key)
d
@@ -110,7 +117,7 @@ class Chef::EncryptedDataBagItem
end
end
- class Version1Decryptor
+ class Version1Decryptor < Version0Decryptor
attr_reader :encrypted_data
attr_reader :key
@@ -149,8 +156,9 @@ class Chef::EncryptedDataBagItem
def openssl_decryptor
@openssl_decryptor ||= begin
assert_valid_cipher!
- d = OpenSSL::Cipher::Cipher.new(ALGORITHM)
+ d = OpenSSL::Cipher::Cipher.new(algorithm)
d.decrypt
+ # We must set key before iv: https://bugs.ruby-lang.org/issues/8221
d.key = Digest::SHA256.digest(key)
d.iv = iv
d
@@ -159,11 +167,11 @@ class Chef::EncryptedDataBagItem
def assert_valid_cipher!
# In the future, chef may support configurable ciphers. For now, only
- # aes-256-cbc is supported.
+ # aes-256-cbc and aes-256-gcm are supported.
requested_cipher = @encrypted_data["cipher"]
- unless requested_cipher == ALGORITHM
+ unless requested_cipher == algorithm
raise UnsupportedCipher,
- "Cipher '#{requested_cipher}' is not supported by this version of Chef. Available ciphers: ['#{ALGORITHM}']"
+ "Cipher '#{requested_cipher}' is not supported by this version of Chef. Available ciphers: ['#{ALGORITHM}', '#{AEAD_ALGORITHM}']"
end
end
end
@@ -197,5 +205,32 @@ class Chef::EncryptedDataBagItem
valid == 0
end
end
+
+ class Version3Decryptor < Version1Decryptor
+
+ # Returns the used decryption algorithm
+ def algorithm
+ AEAD_ALGORITHM
+ end
+
+ def auth_tag
+ auth_tag_b64 = @encrypted_data["auth_tag"]
+ if auth_tag_b64.nil?
+ raise DecryptionFailure, "Error decrypting data bag value: invalid authentication tag. Most likely the data is corrupted"
+ end
+ Base64.decode64(auth_tag_b64)
+ end
+
+ def openssl_decryptor
+ @openssl_decryptor ||= begin
+ d = super
+ d.auth_tag = auth_tag
+ d.auth_data = ''
+ d
+ end
+ end
+
+ end
+
end
end
diff --git a/lib/chef/encrypted_data_bag_item/encryption_failure.rb b/lib/chef/encrypted_data_bag_item/encryption_failure.rb
new file mode 100644
index 0000000000..380e9bc1f7
--- /dev/null
+++ b/lib/chef/encrypted_data_bag_item/encryption_failure.rb
@@ -0,0 +1,22 @@
+#
+# Author:: Xabier de Zuazo (<xabier@onddo.com>)
+# Copyright:: Copyright (c) 2014 Onddo Labs, SL.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License 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.
+#
+
+class Chef::EncryptedDataBagItem
+ class EncryptionFailure < StandardError
+ end
+end
diff --git a/lib/chef/encrypted_data_bag_item/encryptor.rb b/lib/chef/encrypted_data_bag_item/encryptor.rb
index 9686e84b34..a3f3011c21 100644
--- a/lib/chef/encrypted_data_bag_item/encryptor.rb
+++ b/lib/chef/encrypted_data_bag_item/encryptor.rb
@@ -22,6 +22,7 @@ require 'openssl'
require 'ffi_yajl'
require 'chef/encrypted_data_bag_item'
require 'chef/encrypted_data_bag_item/unsupported_encrypted_data_bag_item_format'
+require 'chef/encrypted_data_bag_item/encryption_failure'
class Chef::EncryptedDataBagItem
@@ -40,9 +41,11 @@ class Chef::EncryptedDataBagItem
Version1Encryptor.new(value, secret, iv)
when 2
Version2Encryptor.new(value, secret, iv)
+ when 3
+ Version3Encryptor.new(value, secret, iv)
else
raise UnsupportedEncryptedDataBagItemFormat,
- "Invalid encrypted data bag format version `#{format_version}'. Supported versions are '1', '2'"
+ "Invalid encrypted data bag format version `#{format_version}'. Supported versions are '1', '2', '3'"
end
end
@@ -65,6 +68,11 @@ class Chef::EncryptedDataBagItem
@iv = iv && Base64.decode64(iv)
end
+ # Returns the used encryption algorithm
+ def algorithm
+ ALGORITHM
+ end
+
# Returns a wrapped and encrypted version of +plaintext_data+ suitable for
# using as the value in an encrypted data bag item.
def for_encrypted_item
@@ -72,7 +80,7 @@ class Chef::EncryptedDataBagItem
"encrypted_data" => encrypted_data,
"iv" => Base64.encode64(iv),
"version" => 1,
- "cipher" => ALGORITHM
+ "cipher" => algorithm
}
end
@@ -88,11 +96,12 @@ class Chef::EncryptedDataBagItem
# it for the specified iv and encryption key.
def openssl_encryptor
@openssl_encryptor ||= begin
- encryptor = OpenSSL::Cipher::Cipher.new(ALGORITHM)
+ encryptor = OpenSSL::Cipher::Cipher.new(algorithm)
encryptor.encrypt
+ # We must set key before iv: https://bugs.ruby-lang.org/issues/8221
+ encryptor.key = Digest::SHA256.digest(key)
@iv ||= encryptor.random_iv
encryptor.iv = @iv
- encryptor.key = Digest::SHA256.digest(key)
encryptor
end
end
@@ -125,7 +134,7 @@ class Chef::EncryptedDataBagItem
"hmac" => hmac,
"iv" => Base64.encode64(iv),
"version" => 2,
- "cipher" => ALGORITHM
+ "cipher" => algorithm
}
end
@@ -138,5 +147,62 @@ class Chef::EncryptedDataBagItem
end
end
end
+
+ class Version3Encryptor < Version1Encryptor
+
+ def initialize(plaintext_data, key, iv=nil)
+ super
+ @auth_tag = nil
+ end
+
+ # Returns a wrapped and encrypted version of +plaintext_data+ suitable for
+ # using as the value in an encrypted data bag item.
+ def for_encrypted_item
+ {
+ "encrypted_data" => encrypted_data,
+ "iv" => Base64.encode64(iv),
+ "auth_tag" => Base64.encode64(auth_tag),
+ "version" => 3,
+ "cipher" => algorithm
+ }
+ end
+
+ # Returns the used encryption algorithm
+ def algorithm
+ AEAD_ALGORITHM
+ end
+
+ # Returns a wrapped and encrypted version of +plaintext_data+ suitable for
+ # Returns the auth_tag.
+ def auth_tag
+ # Generated auth_tag comes from OpenSSL::Cipher::Cipher#auth_tag
+ # This must be generated after the data is encrypted
+ if @auth_tag.nil?
+ raise EncryptionFailure, "Internal Error: GCM authentication tag read before encryption"
+ end
+ @auth_tag
+ end
+
+ # Generates (and memoizes) an OpenSSL::Cipher::Cipher object and configures
+ # it for the specified iv and encryption key using AEAD
+ def openssl_encryptor
+ @openssl_encryptor ||= begin
+ encryptor = super
+ encryptor.auth_data = ''
+ encryptor
+ end
+ end
+
+ # Encrypts, Base64 encodes +serialized_data+ and gets the authentication tag
+ def encrypted_data
+ @encrypted_data ||= begin
+ enc_data_b64 = super
+ @auth_tag = openssl_encryptor.auth_tag
+ enc_data_b64
+ end
+ end
+
+ end
+
end
end