diff --git a/Gemfile b/Gemfile index 817f62a8..851fabc2 100644 --- a/Gemfile +++ b/Gemfile @@ -1,2 +1,2 @@ -source 'http://rubygems.org' +source 'https://rubygems.org' gemspec diff --git a/Gemfile.lock b/Gemfile.lock index c18e1a4f..db9cf51a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - saml_idp (0.23.3.pre.18f) + saml_idp (0.23.4.pre.18f) activesupport builder faraday @@ -9,7 +9,7 @@ PATH pkcs11 GEM - remote: http://rubygems.org/ + remote: https://rubygems.org/ specs: actioncable (7.1.3.4) actionpack (= 7.1.3.4) @@ -317,7 +317,7 @@ DEPENDENCIES simplecov (~> 0.22.0) sqlite3 timecop - xmlenc (>= 0.7.1) + xmlenc (>= 0.8.0) BUNDLED WITH 2.5.7 diff --git a/lib/saml_idp/encryptor.rb b/lib/saml_idp/encryptor.rb index c1e3d594..aa729d6b 100644 --- a/lib/saml_idp/encryptor.rb +++ b/lib/saml_idp/encryptor.rb @@ -3,6 +3,16 @@ module SamlIdp class Encryptor attr_accessor :encryption_key, :block_encryption, :key_transport, :cert + # Encryption algorithms enumerated in: + # https://github.com/digidentity/xmlenc/blob/937ca2f/lib/xmlenc/encrypted_data.rb#L3-L10 + ENCRYPTION_ALGORITHMS_NS = { + 'aes128-cbc' => 'http://www.w3.org/2001/04/xmlenc#aes128-cbc', + 'aes256-cbc' => 'http://www.w3.org/2001/04/xmlenc#aes256-cbc', + 'aes128-gcm' => 'http://www.w3.org/2009/xmlenc11#aes128-gcm', + 'aes192-gcm' => 'http://www.w3.org/2009/xmlenc11#aes192-gcm', + 'aes256-gcm' => 'http://www.w3.org/2009/xmlenc11#aes256-gcm', + } + def initialize(opts) self.block_encryption = opts[:block_encryption] self.key_transport = opts[:key_transport] @@ -35,7 +45,7 @@ def openssl_cert private :openssl_cert def block_encryption_ns - "http://www.w3.org/2001/04/xmlenc##{block_encryption}" + ENCRYPTION_ALGORITHMS_NS[block_encryption] end private :block_encryption_ns diff --git a/lib/saml_idp/version.rb b/lib/saml_idp/version.rb index c7cd3504..e7592673 100644 --- a/lib/saml_idp/version.rb +++ b/lib/saml_idp/version.rb @@ -1,3 +1,3 @@ module SamlIdp - VERSION = '0.23.3-18f'.freeze + VERSION = '0.23.4-18f'.freeze end diff --git a/saml_idp.gemspec b/saml_idp.gemspec index 555e8c0c..f01b13ad 100644 --- a/saml_idp.gemspec +++ b/saml_idp.gemspec @@ -42,6 +42,6 @@ Gem::Specification.new do |s| s.add_development_dependency 'simplecov', '~> 0.22.0' s.add_development_dependency 'sqlite3' s.add_development_dependency('timecop') - s.add_development_dependency('xmlenc', '>= 0.7.1') + s.add_development_dependency('xmlenc', '>= 0.8.0') s.metadata['rubygems_mfa_required'] = 'true' end diff --git a/spec/lib/saml_idp/controller_spec.rb b/spec/lib/saml_idp/controller_spec.rb index 59b4cba8..6ef85552 100644 --- a/spec/lib/saml_idp/controller_spec.rb +++ b/spec/lib/saml_idp/controller_spec.rb @@ -1,4 +1,5 @@ require 'spec_helper' +require 'saml_idp/encryptor' describe SamlIdp::Controller do include SamlIdp::Controller @@ -56,13 +57,6 @@ def head(status, options = {}); end end let(:principal) { double email_address: 'foo@example.com' } - let(:encryption_opts) do - { - cert: SamlIdp::Default::X509_CERTIFICATE, - block_encryption: 'aes256-cbc', - key_transport: 'rsa-oaep-mgf1p', - } - end it 'creates a SAML Response' do saml_response = encode_response(principal) @@ -116,17 +110,25 @@ def head(status, options = {}); end expect(response.is_valid?).to be_truthy end - it 'encrypts SAML Response assertion' do - self.algorithm = algorithm_name - saml_response = encode_response(principal, encryption: encryption_opts) - resp_settings = saml_settings - resp_settings.private_key = SamlIdp::Default::SECRET_KEY - response = OneLogin::RubySaml::Response.new(saml_response, settings: resp_settings) - expect(response.document.to_s).not_to match('foo@example.com') - expect(response.decrypted_document.to_s).to match('foo@example.com') - expect(response.name_id).to eq('foo@example.com') - expect(response.issuers.first).to eq('http://example.com') - expect(response.is_valid?).to be_truthy + SamlIdp::Encryptor::ENCRYPTION_ALGORITHMS_NS.keys.each do |encryption_algorithm| + it "encrypts SAML Response assertion using #{encryption_algorithm}" do + self.algorithm = algorithm_name + encryption_opts = { + cert: SamlIdp::Default::X509_CERTIFICATE, + block_encryption: encryption_algorithm, + key_transport: 'rsa-oaep-mgf1p', + } + + saml_response = encode_response(principal, encryption: encryption_opts) + resp_settings = saml_settings + resp_settings.private_key = SamlIdp::Default::SECRET_KEY + response = OneLogin::RubySaml::Response.new(saml_response, settings: resp_settings) + expect(response.document.to_s).not_to match('foo@example.com') + expect(response.decrypted_document.to_s).to match('foo@example.com') + expect(response.name_id).to eq('foo@example.com') + expect(response.issuers.first).to eq('http://example.com') + expect(response.is_valid?).to be_truthy + end end end end diff --git a/spec/lib/saml_idp/encryptor_spec.rb b/spec/lib/saml_idp/encryptor_spec.rb index d8d7ab74..95f789ad 100644 --- a/spec/lib/saml_idp/encryptor_spec.rb +++ b/spec/lib/saml_idp/encryptor_spec.rb @@ -14,15 +14,18 @@ module SamlIdp subject { described_class.new encryption_opts } - it 'encrypts XML' do - raw_xml = 'bar' - encrypted_xml = subject.encrypt(raw_xml) - expect(encrypted_xml).not_to match 'bar' - encrypted_doc = Nokogiri::XML::Document.parse(encrypted_xml) - encrypted_data = Xmlenc::EncryptedData.new(encrypted_doc.at_xpath('//xenc:EncryptedData', - Xmlenc::NAMESPACES)) - decrypted_xml = encrypted_data.decrypt(subject.encryption_key) - expect(decrypted_xml).to eq(raw_xml) + SamlIdp::Encryptor::ENCRYPTION_ALGORITHMS_NS.keys.each do |encryption_algorithm| + it "encrypts XML with #{encryption_algorithm}" do + encryption_opts[:block_encryption] = encryption_algorithm + raw_xml = 'bar' + encrypted_xml = subject.encrypt(raw_xml) + expect(encrypted_xml).not_to match 'bar' + encrypted_doc = Nokogiri::XML::Document.parse(encrypted_xml) + encrypted_data = Xmlenc::EncryptedData.new(encrypted_doc.at_xpath('//xenc:EncryptedData', + Xmlenc::NAMESPACES)) + decrypted_xml = encrypted_data.decrypt(subject.encryption_key) + expect(decrypted_xml).to eq(raw_xml) + end end it 'does not have a KeyName element' do @@ -32,5 +35,15 @@ module SamlIdp expect(encrypted_doc.remove_namespaces!.xpath('//KeyName')).to be_empty end + + context 'invalid block_encryption' do + it 'raises an exception' do + encryption_opts[:block_encryption] = 'abc123' + + expect do + subject.encrypt('bar') + end.to raise_error(Xmlenc::UnsupportedError) + end + end end end