Commit 47601b4

mokha <mo@mokhan.ca>
2019-01-25 02:00:26
allow target class to provide symmetric and assymetric ciphers
1 parent 5ffb1f7
lib/xml/kit/crypto/oaep_cipher.rb
@@ -8,7 +8,10 @@ module Xml
         ALGORITHMS = {
           ALGORITHM => true
         }.freeze
-        def initialize(_algorithm, key)
+        attr_reader :algorithm, :key
+
+        def initialize(algorithm, key)
+          @algorithm = algorithm
           @key = key
         end
 
lib/xml/kit/crypto/rsa_cipher.rb
@@ -5,8 +5,10 @@ module Xml
     module Crypto
       class RsaCipher
         ALGORITHM = "#{::Xml::Kit::Namespaces::XMLENC}rsa-1_5".freeze
+        attr_reader :algorithm, :key
 
-        def initialize(_algorithm, key)
+        def initialize(algorithm, key)
+          @algorithm = algorithm
           @key = key
         end
 
lib/xml/kit/crypto/unknown_cipher.rb
@@ -4,7 +4,12 @@ module Xml
   module Kit
     module Crypto
       class UnknownCipher
-        def initialize(algorithm, key); end
+        attr_reader :algorithm, :key
+
+        def initialize(algorithm, key)
+          @algorithm = algorithm
+          @key = key
+        end
 
         def self.matches?(_algorithm)
           true
lib/xml/kit/templates/encrypted_key.builder
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 xml.EncryptedKey Id: id, xmlns: ::Xml::Kit::Namespaces::XMLENC do
-  xml.EncryptionMethod Algorithm: algorithm
+  xml.EncryptionMethod Algorithm: asymmetric_cipher.algorithm
   render(key_info, xml: xml) if key_info
   xml.CipherData do
     xml.CipherValue cipher_value
lib/xml/kit/encrypted_key.rb
@@ -6,31 +6,19 @@ module Xml
   module Kit
     class EncryptedKey
       include ::Xml::Kit::Templatable
-      DEFAULT_ALGORITHM = ::Xml::Kit::Crypto::RsaCipher::ALGORITHM
-
-      attr_reader :id, :algorithm
-      attr_reader :public_key, :key
+      attr_reader :id
+      attr_reader :asymmetric_cipher, :symmetric_cipher
       attr_accessor :key_info
 
-      def initialize(id: Id.generate, public_key:, key:, key_info: nil, algorithm: DEFAULT_ALGORITHM)
+      def initialize(id: Id.generate, asymmetric_cipher:, symmetric_cipher:, key_info: nil)
         @id = id
-        @algorithm = algorithm
-        @public_key = public_key
-        @key = key
+        @asymmetric_cipher = asymmetric_cipher
+        @symmetric_cipher = symmetric_cipher
         @key_info = key_info
       end
 
       def cipher_value
-        asymmetric_cipher = asymmetric(algorithm, public_key)
-        Base64.strict_encode64(asymmetric_cipher.encrypt(key))
-      end
-
-      private
-
-      def asymmetric(algorithm, public_key)
-        return algorithm unless algorithm.is_a?(String)
-
-        ::Xml::Kit::Crypto.cipher_for(algorithm, public_key)
+        Base64.strict_encode64(asymmetric_cipher.encrypt(symmetric_cipher.key))
       end
     end
   end
lib/xml/kit/encryption.rb
@@ -41,11 +41,18 @@ module Xml
       end
 
       def create_key_info_for(public_key, symmetric_cipher, asymmetric_algorithm)
+        asymmetric_cipher = asymmetric(asymmetric_algorithm, public_key)
         KeyInfo.new do |x|
-          x.encrypted_key = EncryptedKey.new(public_key: public_key, key: symmetric_cipher.key, algorithm: asymmetric_algorithm)
+          x.encrypted_key = EncryptedKey.new(asymmetric_cipher: asymmetric_cipher, symmetric_cipher: symmetric_cipher)
           @asymmetric_cipher_value = x.encrypted_key.cipher_value
         end
       end
+
+      def asymmetric(algorithm, public_key)
+        return algorithm unless algorithm.is_a?(String)
+
+        ::Xml::Kit::Crypto.cipher_for(algorithm, public_key)
+      end
     end
   end
 end
lib/xml/kit/templatable.rb
@@ -24,7 +24,11 @@ module Xml
       end
 
       def encrypt_key_for(xml:, id:, public_key:, key:)
-        ::Xml::Kit::EncryptedKey.new(id: id, public_key: public_key, key: key).to_xml(xml: xml)
+        ::Xml::Kit::EncryptedKey.new(
+          id: id,
+          asymmetric_cipher: asymmetric_cipher,
+          symmetric_cipher: symmetric_cipher
+        ).to_xml(xml: xml)
       end
 
       def encryption_for(*args, &block)
@@ -32,7 +36,7 @@ module Xml
         encrypt_data_for(*args, &block)
       end
 
-      def encrypt_data_for(xml:, key_info: nil, symmetric_cipher: Crypto::SymmetricCipher.new)
+      def encrypt_data_for(xml:, key_info: nil)
         return yield xml unless encrypt?
 
         temp = ::Builder::XmlMarkup.new
@@ -41,10 +45,19 @@ module Xml
           signatures.complete(temp.target!),
           encryption_certificate.public_key,
           symmetric_algorithm: symmetric_cipher,
+          asymmetric_algorithm: asymmetric_cipher,
           key_info: key_info
         ).to_xml(xml: xml)
       end
 
+      def asymmetric_cipher(algorithm: Crypto::RsaCipher::ALGORITHM)
+        @asymmetric_cipher ||= Crypto.cipher_for(algorithm, encryption_certificate.public_key)
+      end
+
+      def symmetric_cipher
+        @symmetric_cipher ||= Crypto::SymmetricCipher.new
+      end
+
       def render(model, options)
         ::Xml::Kit::Template.new(model).to_xml(options)
       end
spec/fixtures/soap.builder
@@ -8,7 +8,7 @@ xml.Envelope xmlns: "http://schemas.xmlsoap.org/soap/envelope/" do
     end
   end
   xml.Body xmlns: 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd', Id: id  do
-    encrypt_data_for xml: xml, key_info: body_key_info, symmetric_cipher: symmetric_cipher do |xml|
+    encrypt_data_for xml: xml, key_info: body_key_info do |xml|
       xml.EncryptMe do
         xml.Secret "secret"
       end
spec/support/soap.rb
@@ -19,10 +19,6 @@ class Soap
     symmetric_cipher.key
   end
 
-  def symmetric_cipher
-    @symmetric_cipher ||= ::Xml::Kit::Crypto::SymmetricCipher.new
-  end
-
   def key_id
     'EK-E2C32E59F27A1320A215468956686717'
   end
spec/xml/kit/encrypted_key_spec.rb
@@ -2,14 +2,14 @@
 
 RSpec.describe ::Xml::Kit::EncryptedKey do
   describe '#to_xml' do
-    subject { described_class.new(id: id, algorithm: algorithm, public_key: public_key, key: symmetric_key, key_info: key_info) }
+    subject { described_class.new(id: id, asymmetric_cipher: asymmetric_cipher, symmetric_cipher: symmetric_cipher, key_info: key_info) }
 
+    let(:symmetric_cipher) { ::Xml::Kit::Crypto::SymmetricCipher.new }
+    let(:asymmetric_cipher) { ::Xml::Kit::Crypto.cipher_for(algorithm, private_key.public_key) }
     let(:algorithm) { ::Xml::Kit::Crypto::RsaCipher::ALGORITHM }
     let(:key_info) { ::Xml::Kit::KeyInfo.new }
     let(:id) { ::Xml::Kit::Id.generate }
     let(:private_key) { OpenSSL::PKey::RSA.new(2048) }
-    let(:public_key) { private_key.public_key }
-    let(:symmetric_key) { SecureRandom.hex(32) }
     let(:result) { Hash.from_xml(subject.to_xml) }
 
     before do
@@ -19,9 +19,9 @@ RSpec.describe ::Xml::Kit::EncryptedKey do
     specify { expect(result.key?('EncryptedKey')).to be_present }
     specify { expect(result['EncryptedKey']['Id']).to eql(id) }
     specify { expect(result['EncryptedKey']['xmlns']).to eql(::Xml::Kit::Namespaces::XMLENC) }
-    specify { expect(result['EncryptedKey']['EncryptionMethod']['Algorithm']).to be_present }
+    specify { expect(result['EncryptedKey']['EncryptionMethod']['Algorithm']).to eql(algorithm) }
     specify { expect(result['EncryptedKey']['CipherData']['CipherValue']).to be_present }
-    specify { expect(private_key.private_decrypt(Base64.decode64(result['EncryptedKey']['CipherData']['CipherValue']))).to eql(symmetric_key) }
+    specify { expect(private_key.private_decrypt(Base64.decode64(result['EncryptedKey']['CipherData']['CipherValue']))).to eql(symmetric_cipher.key) }
     specify { expect(subject.to_xml).to match_xsd('xenc-schema') }
     specify { expect(result['EncryptedKey'].key?('KeyInfo')).to be(true) }
   end
spec/xml/kit/encryption_spec.rb
@@ -37,7 +37,7 @@ RSpec.describe Xml::Kit::Encryption do
       subject { described_class.new(xml, public_key, symmetric_algorithm: symmetric_cipher, asymmetric_algorithm: asymmetric_cipher) }
 
       let(:symmetric_cipher) { instance_double(Xml::Kit::Crypto::SymmetricCipher, key: 'symmetric_key', encrypt: 'CIPHERTEXT', to_s: 'symmetric_cipher') }
-      let(:asymmetric_cipher) { instance_double(Xml::Kit::Crypto::RsaCipher, encrypt: 'asymmetric CIPHERTEXT', to_s: 'asymmetric_cipher') }
+      let(:asymmetric_cipher) { instance_double(Xml::Kit::Crypto::RsaCipher, encrypt: 'asymmetric CIPHERTEXT', to_s: 'asymmetric_cipher', algorithm: 'asymmetric_cipher') }
       let(:key_pair) { Xml::Kit::KeyPair.generate(use: :encryption) }
       let(:public_key) { key_pair.public_key }
       let(:xml) do
spec/xml/kit/key_info_spec.rb
@@ -5,12 +5,12 @@ RSpec.describe Xml::Kit::KeyInfo do
 
   describe '#to_xml' do
     context 'with encrypted key' do
-      let(:encrypted_key) { ::Xml::Kit::EncryptedKey.new(id: id, algorithm: algorithm, public_key: public_key, key: symmetric_key) }
+      let(:encrypted_key) { ::Xml::Kit::EncryptedKey.new(id: id, asymmetric_cipher: asymmetric_cipher, symmetric_cipher: symmetric_cipher) }
+      let(:symmetric_cipher) { ::Xml::Kit::Crypto::SymmetricCipher.new }
+      let(:asymmetric_cipher) { ::Xml::Kit::Crypto.cipher_for(algorithm, private_key.public_key) }
       let(:algorithm) { ::Xml::Kit::Crypto::RsaCipher::ALGORITHM }
       let(:id) { ::Xml::Kit::Id.generate }
       let(:private_key) { OpenSSL::PKey::RSA.new(2048) }
-      let(:public_key) { private_key.public_key }
-      let(:symmetric_key) { SecureRandom.hex(32) }
       let(:result) { Hash.from_xml(subject.to_xml) }
 
       before do
@@ -18,7 +18,7 @@ RSpec.describe Xml::Kit::KeyInfo do
       end
 
       specify { expect(result['KeyInfo']['EncryptedKey']['EncryptionMethod']['Algorithm']).to eql(algorithm) }
-      specify { expect(private_key.private_decrypt(Base64.decode64(result['KeyInfo']['EncryptedKey']['CipherData']['CipherValue']))).to eql(symmetric_key) }
+      specify { expect(private_key.private_decrypt(Base64.decode64(result['KeyInfo']['EncryptedKey']['CipherData']['CipherValue']))).to eql(symmetric_cipher.key) }
     end
 
     context 'with key name' do