summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXabier de Zuazo <xabier@onddo.com>2014-07-06 21:07:42 +0200
committerXabier de Zuazo <xabier@onddo.com>2014-07-06 21:07:42 +0200
commit67fe30df3a5700927788b363fbcbf9423ceeb3d2 (patch)
treee8acd191a921205eb33aa2a87452ea2bd0c778cd
parent1864ec8293923210738cca0c11aef5b96786cb86 (diff)
downloadchef-67fe30df3a5700927788b363fbcbf9423ceeb3d2.tar.gz
[CHEF-5356-gcm] If the requirements to use Encryted Data Bags 3 are not met, give a meaningful error message
-rw-r--r--lib/chef/encrypted_data_bag_item/assertions.rb54
-rw-r--r--lib/chef/encrypted_data_bag_item/decryptor.rb28
-rw-r--r--lib/chef/encrypted_data_bag_item/encrypted_data_bag_item_assertions.rb37
-rw-r--r--lib/chef/encrypted_data_bag_item/encryptor.rb5
-rw-r--r--spec/unit/encrypted_data_bag_item_spec.rb141
5 files changed, 206 insertions, 59 deletions
diff --git a/lib/chef/encrypted_data_bag_item/assertions.rb b/lib/chef/encrypted_data_bag_item/assertions.rb
new file mode 100644
index 0000000000..6e90008523
--- /dev/null
+++ b/lib/chef/encrypted_data_bag_item/assertions.rb
@@ -0,0 +1,54 @@
+#
+# 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 EncryptedDataBagRequirementsFailure < StandardError
+ end
+
+ module Assertions
+
+ def assert_format_version_acceptable!(format_version)
+ unless format_version.kind_of?(Integer) and format_version >= Chef::Config[:data_bag_decrypt_minimum_version]
+ raise UnacceptableEncryptedDataBagItemFormat,
+ "The encrypted data bag item has format version `#{format_version}', " +
+ "but the config setting 'data_bag_decrypt_minimum_version' requires version `#{Chef::Config[:data_bag_decrypt_minimum_version]}'"
+ end
+ end
+
+ def assert_valid_cipher!(requested_cipher, algorithm)
+ # In the future, chef may support configurable ciphers. For now, only
+ # aes-256-cbc and aes-256-gcm are supported.
+ unless requested_cipher == algorithm
+ raise UnsupportedCipher,
+ "Cipher '#{requested_cipher}' is not supported by this version of Chef. Available ciphers: ['#{ALGORITHM}', '#{AEAD_ALGORITHM}']"
+ end
+ end
+
+ def assert_aead_requirements_met!(algorithm)
+ unless OpenSSL::Cipher::Cipher.method_defined?(:auth_data=)
+ raise EncryptedDataBagRequirementsFailure, "The used Encrypted Data Bags version requires Ruby >= 1.9"
+ end
+ unless OpenSSL::Cipher::Cipher.ciphers.include?(algorithm)
+ raise EncryptedDataBagRequirementsFailure, "The used Encrypted Data Bags version requires an OpenSSL version with \"#{algorithm}\" algorithm support"
+ end
+ end
+
+ end
+
+end
diff --git a/lib/chef/encrypted_data_bag_item/decryptor.rb b/lib/chef/encrypted_data_bag_item/decryptor.rb
index 82d04c0d67..b269868c53 100644
--- a/lib/chef/encrypted_data_bag_item/decryptor.rb
+++ b/lib/chef/encrypted_data_bag_item/decryptor.rb
@@ -26,6 +26,7 @@ require 'chef/encrypted_data_bag_item/unsupported_encrypted_data_bag_item_format
require 'chef/encrypted_data_bag_item/unacceptable_encrypted_data_bag_item_format'
require 'chef/encrypted_data_bag_item/decryption_failure'
require 'chef/encrypted_data_bag_item/unsupported_cipher'
+require 'chef/encrypted_data_bag_item/assertions'
class Chef::EncryptedDataBagItem
@@ -37,6 +38,7 @@ class Chef::EncryptedDataBagItem
# to create an instance of the appropriate strategy for the given encrypted
# data bag value.
module Decryptor
+ extend Chef::EncryptedDataBagItem::Assertions
# Detects the encrypted data bag item format version and instantiates a
# decryptor object for that version. Call #for_decrypted_item on the
@@ -67,15 +69,8 @@ class Chef::EncryptedDataBagItem
end
end
- def self.assert_format_version_acceptable!(format_version)
- unless format_version.kind_of?(Integer) and format_version >= Chef::Config[:data_bag_decrypt_minimum_version]
- raise UnacceptableEncryptedDataBagItemFormat,
- "The encrypted data bag item has format version `#{format_version}', " +
- "but the config setting 'data_bag_decrypt_minimum_version' requires version `#{Chef::Config[:data_bag_decrypt_minimum_version]}'"
- end
- end
-
class Version0Decryptor
+ include Chef::EncryptedDataBagItem::Assertions
attr_reader :encrypted_data
attr_reader :key
@@ -155,7 +150,7 @@ class Chef::EncryptedDataBagItem
def openssl_decryptor
@openssl_decryptor ||= begin
- assert_valid_cipher!
+ assert_valid_cipher!(@encrypted_data["cipher"], algorithm)
d = OpenSSL::Cipher::Cipher.new(algorithm)
d.decrypt
# We must set key before iv: https://bugs.ruby-lang.org/issues/8221
@@ -165,15 +160,6 @@ class Chef::EncryptedDataBagItem
end
end
- def assert_valid_cipher!
- # In the future, chef may support configurable ciphers. For now, only
- # aes-256-cbc and aes-256-gcm are supported.
- requested_cipher = @encrypted_data["cipher"]
- unless requested_cipher == algorithm
- raise UnsupportedCipher,
- "Cipher '#{requested_cipher}' is not supported by this version of Chef. Available ciphers: ['#{ALGORITHM}', '#{AEAD_ALGORITHM}']"
- end
- end
end
class Version2Decryptor < Version1Decryptor
@@ -208,6 +194,12 @@ class Chef::EncryptedDataBagItem
class Version3Decryptor < Version1Decryptor
+ def initialize(encrypted_data, key)
+ super
+ assert_aead_requirements_met!(algorithm)
+ end
+
+
# Returns the used decryption algorithm
def algorithm
AEAD_ALGORITHM
diff --git a/lib/chef/encrypted_data_bag_item/encrypted_data_bag_item_assertions.rb b/lib/chef/encrypted_data_bag_item/encrypted_data_bag_item_assertions.rb
new file mode 100644
index 0000000000..4d116715f6
--- /dev/null
+++ b/lib/chef/encrypted_data_bag_item/encrypted_data_bag_item_assertions.rb
@@ -0,0 +1,37 @@
+#
+# 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 EncryptedDataBagRequirementsFailure < StandardError
+ end
+
+ module Assertions
+
+ def assert_requirements_met!
+ unless OpenSSL::Cipher::Cipher.method_defined?(:auth_data=)
+ raise EncryptedDataBagRequirementsFailure, "The used Encrypted Data Bags version requires Ruby >= 1.9"
+ end
+ unless OpenSSL::Cipher::Cipher.ciphers.include?(algorithm)
+ raise EncryptedDataBagRequirementsFailure, "The used Encrypted Data Bags version requires an OpenSSL version with \"#{algorithm}\" algorithm support"
+ end
+ end
+
+ end
+
+end
diff --git a/lib/chef/encrypted_data_bag_item/encryptor.rb b/lib/chef/encrypted_data_bag_item/encryptor.rb
index a3f3011c21..5b4eaa7c98 100644
--- a/lib/chef/encrypted_data_bag_item/encryptor.rb
+++ b/lib/chef/encrypted_data_bag_item/encryptor.rb
@@ -23,6 +23,7 @@ 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'
+require 'chef/encrypted_data_bag_item/assertions'
class Chef::EncryptedDataBagItem
@@ -53,6 +54,8 @@ class Chef::EncryptedDataBagItem
attr_reader :key
attr_reader :plaintext_data
+ include Chef::EncryptedDataBagItem::Assertions
+
# Create a new Encryptor for +data+, which will be encrypted with the given
# +key+.
#
@@ -149,9 +152,11 @@ class Chef::EncryptedDataBagItem
end
class Version3Encryptor < Version1Encryptor
+ include Chef::EncryptedDataBagItem::Assertions
def initialize(plaintext_data, key, iv=nil)
super
+ assert_aead_requirements_met!(algorithm)
@auth_tag = nil
end
diff --git a/spec/unit/encrypted_data_bag_item_spec.rb b/spec/unit/encrypted_data_bag_item_spec.rb
index 68379e2f17..53fe8cd778 100644
--- a/spec/unit/encrypted_data_bag_item_spec.rb
+++ b/spec/unit/encrypted_data_bag_item_spec.rb
@@ -92,34 +92,59 @@ describe Chef::EncryptedDataBagItem::Encryptor do
end
end
- describe "when using version 3 format",
- :if => (RUBY_VERSION >= "2" and OpenSSL::OPENSSL_VERSION_NUMBER >= 10001000) do
-
+ describe "when using version 3 format" do
before do
Chef::Config[:data_bag_encrypt_version] = 3
end
- it "creates a version 3 encryptor" do
- encryptor.should be_a_instance_of(Chef::EncryptedDataBagItem::Encryptor::Version3Encryptor)
- end
+ context "on supported platforms",
+ :if => (RUBY_VERSION >= "2" and OpenSSL::OPENSSL_VERSION_NUMBER >= 10001000) do
- it "generates different authentication tags" do
- encryptor3 = Chef::EncryptedDataBagItem::Encryptor.new(plaintext_data, key)
- encryptor.for_encrypted_item # required to generate the auth_tag
- encryptor3.for_encrypted_item
- encryptor.auth_tag.should_not eq(encryptor3.auth_tag)
- end
+ it "creates a version 3 encryptor" do
+ encryptor.should be_a_instance_of(Chef::EncryptedDataBagItem::Encryptor::Version3Encryptor)
+ end
- it "includes the auth_tag in the envelope" do
- final_data = encryptor.for_encrypted_item
- final_data["auth_tag"].should eq(Base64::encode64(encryptor.auth_tag))
- end
+ it "generates different authentication tags" do
+ encryptor3 = Chef::EncryptedDataBagItem::Encryptor.new(plaintext_data, key)
+ encryptor.for_encrypted_item # required to generate the auth_tag
+ encryptor3.for_encrypted_item
+ encryptor.auth_tag.should_not eq(encryptor3.auth_tag)
+ end
- it "throws an error if auth tag is read before encrypting the data" do
- lambda { encryptor.auth_tag }.should raise_error(Chef::EncryptedDataBagItem::EncryptionFailure)
- end
+ it "includes the auth_tag in the envelope" do
+ final_data = encryptor.for_encrypted_item
+ final_data["auth_tag"].should eq(Base64::encode64(encryptor.auth_tag))
+ end
- end
+ it "throws an error if auth tag is read before encrypting the data" do
+ lambda { encryptor.auth_tag }.should raise_error(Chef::EncryptedDataBagItem::EncryptionFailure)
+ end
+
+ end # context on supported platforms
+
+ context "on unsupported platforms" do
+
+ context "on platforms with old Ruby",
+ :if => RUBY_VERSION < "2" do
+
+ it "throws an error warning about the Ruby version" do
+ lambda { encryptor }.should raise_error(Chef::EncryptedDataBagItem::EncryptedDataBagRequirementsFailure, /requires Ruby/)
+ end
+
+ end # context on platforms with old Ruby
+
+ context "on platforms with old OpenSSL",
+ :if => OpenSSL::OPENSSL_VERSION_NUMBER < 10001000 do
+
+ it "throws an error warning about the OpenSSL version" do
+ lambda { encryptor }.should raise_error(Chef::EncryptedDataBagItem::EncryptedDataBagRequirementsFailure, /requires an OpenSSL/)
+ end
+
+ end # context on platforms with old OpenSSL
+
+ end # context on unsupported platforms
+
+ end # when using version 3 format
end
@@ -130,34 +155,68 @@ describe Chef::EncryptedDataBagItem::Decryptor do
let(:encryption_key) { "passwd" }
let(:decryption_key) { encryption_key }
- context "when decrypting a version 3 (JSON+aes-256-gcm+random iv+auth tag) encrypted value",
- :if => (RUBY_VERSION >= "2" and OpenSSL::OPENSSL_VERSION_NUMBER >= 10001000) do
+ context "when decrypting a version 3 (JSON+aes-256-gcm+random iv+auth tag) encrypted value" do
- let(:encrypted_value) do
- Chef::EncryptedDataBagItem::Encryptor::Version3Encryptor.new(plaintext_data, encryption_key).for_encrypted_item
- end
+ context "on supported platforms",
+ :if => (RUBY_VERSION >= "2" and OpenSSL::OPENSSL_VERSION_NUMBER >= 10001000) do
- let(:bogus_auth_tag) { "bogus_auth_tag" }
+ let(:encrypted_value) do
+ Chef::EncryptedDataBagItem::Encryptor::Version3Encryptor.new(plaintext_data, encryption_key).for_encrypted_item
+ end
- it "decrypts the encrypted value" do
- decryptor.decrypted_data.should eq({"json_wrapper" => plaintext_data}.to_json)
- end
+ let(:bogus_auth_tag) { "bogus_auth_tag" }
- it "unwraps the encrypted data and returns it" do
- decryptor.for_decrypted_item.should eq plaintext_data
- end
+ it "decrypts the encrypted value" do
+ decryptor.decrypted_data.should eq({"json_wrapper" => plaintext_data}.to_json)
+ end
- it "rejects the data if the authentication tag is wrong" do
- encrypted_value["auth_tag"] = bogus_auth_tag
- lambda { decryptor.for_decrypted_item }.should raise_error(Chef::EncryptedDataBagItem::DecryptionFailure)
- end
+ it "unwraps the encrypted data and returns it" do
+ decryptor.for_decrypted_item.should eq plaintext_data
+ end
- it "rejects the data if the authentication tag is missing" do
- encrypted_value.delete("auth_tag")
- lambda { decryptor.for_decrypted_item }.should raise_error(Chef::EncryptedDataBagItem::DecryptionFailure)
- end
+ it "rejects the data if the authentication tag is wrong" do
+ encrypted_value["auth_tag"] = bogus_auth_tag
+ lambda { decryptor.for_decrypted_item }.should raise_error(Chef::EncryptedDataBagItem::DecryptionFailure)
+ end
- end
+ it "rejects the data if the authentication tag is missing" do
+ encrypted_value.delete("auth_tag")
+ lambda { decryptor.for_decrypted_item }.should raise_error(Chef::EncryptedDataBagItem::DecryptionFailure)
+ end
+
+ end # context on supported platforms
+
+ context "on unsupported platforms" do
+ let(:encrypted_value) do
+ {
+ "encrypted_data" => "",
+ "iv" => "",
+ "version" => 3,
+ "cipher" => "aes-256-cbc",
+ }
+ end
+
+ context "on platforms with old Ruby",
+ :if => RUBY_VERSION < "2" do
+
+ it "throws an error warning about the Ruby version" do
+ lambda { decryptor }.should raise_error(Chef::EncryptedDataBagItem::EncryptedDataBagRequirementsFailure, /requires Ruby/)
+ end
+
+ end # context on platforms with old Ruby
+
+ context "on platforms with old OpenSSL",
+ :if => OpenSSL::OPENSSL_VERSION_NUMBER < 10001000 do
+
+ it "throws an error warning about the OpenSSL version" do
+ lambda { decryptor }.should raise_error(Chef::EncryptedDataBagItem::EncryptedDataBagRequirementsFailure, /requires an OpenSSL/)
+ end
+
+ end # context on unsupported platforms
+
+ end # context on platforms with old OpenSSL
+
+ end # context when decrypting a version 3
context "when decrypting a version 2 (JSON+aes-256-cbc+hmac-sha256+random iv) encrypted value" do
let(:encrypted_value) do