Commit 2546e4d

mokha <mo@mokhan.ca>
2018-11-25 22:08:07
improve specs.
1 parent 614e689
spec/xml/kit/certificate_spec.rb
@@ -6,95 +6,84 @@ RSpec.describe Xml::Kit::Certificate do
   let(:certificate) { generate_key_pair('password')[0] }
 
   describe '#fingerprint' do
-    it 'returns a fingerprint' do
-      expect(subject.fingerprint).to be_instance_of(Xml::Kit::Fingerprint)
-    end
+    specify { expect(subject.fingerprint).to be_instance_of(Xml::Kit::Fingerprint) }
   end
 
   describe '#for?' do
-    it 'returns true, when it is for signing' do
-      subject = described_class.new(certificate, use: :signing)
-      expect(subject).to be_for(:signing)
-      expect(subject).to be_signing
-      expect(subject).not_to be_for(:encryption)
-      expect(subject).not_to be_encryption
+    context 'when it is for signing' do
+      subject { described_class.new(certificate, use: :signing) }
+
+      specify { expect(subject).to be_for(:signing) }
+      specify { expect(subject).to be_signing }
+      specify { expect(subject).not_to be_for(:encryption) }
+      specify { expect(subject).not_to be_encryption }
     end
 
-    it 'returns true, when it is for encryption' do
-      subject = described_class.new(certificate, use: :encryption)
-      expect(subject).to be_for(:encryption)
-      expect(subject).not_to be_for(:signing)
+    context 'when it is for encryption' do
+      subject { described_class.new(certificate, use: :encryption) }
 
-      expect(subject).to be_encryption
-      expect(subject).not_to be_signing
+      specify { expect(subject).to be_for(:encryption) }
+      specify { expect(subject).not_to be_for(:signing) }
+      specify { expect(subject).to be_encryption }
+      specify { expect(subject).not_to be_signing }
     end
 
-    it 'returns true when it is for both' do
-      subject = described_class.new(certificate)
-      expect(subject).to be_for(:encryption)
-      expect(subject).to be_for(:signing)
+    context 'when it is for both signing and encryption' do
+      subject { described_class.new(certificate) }
 
-      expect(subject).to be_encryption
-      expect(subject).to be_signing
+      specify { expect(subject).to be_for(:encryption) }
+      specify { expect(subject).to be_for(:signing) }
+      specify { expect(subject).to be_encryption }
+      specify { expect(subject).to be_signing }
     end
   end
 
   describe 'equality' do
-    it 'is equal by reference equality' do
-      expect(subject).to eql(subject)
-    end
-
-    it 'is equal by value equality' do
-      expect(
-        described_class.new(certificate, use: :signing)
-      ).to eql(
-        described_class.new(certificate, use: :signing)
-      )
-    end
+    specify { expect(subject).to eql(subject) }
+    specify { expect(described_class.new(certificate, use: :signing)).to eql(described_class.new(certificate, use: :signing)) }
   end
 
   describe '#to_h' do
-    it 'returns a hash' do
-      expect(subject.to_h).to eql(
-        use: :signing,
-        fingerprint: subject.fingerprint.to_s
-      )
-    end
+    specify { expect(subject.to_h).to eql(use: :signing, fingerprint: subject.fingerprint.to_s) }
   end
 
   describe '#stripped' do
-    it 'removes the BEGIN and END lines' do
-      expected = certificate.to_s.gsub(/-----BEGIN CERTIFICATE-----/, '').gsub(/-----END CERTIFICATE-----/, '').delete("\n")
-      expect(subject.stripped).to eql(expected)
-    end
+    let(:expected) { certificate.to_s.gsub(/-----BEGIN CERTIFICATE-----/, '').gsub(/-----END CERTIFICATE-----/, '').delete("\n") }
+
+    specify { expect(subject.stripped).to eql(expected) }
   end
 
   describe '#x509' do
-    it 'returns an x509 certificate' do
-      expected = OpenSSL::X509::Certificate.new(certificate.to_s)
-      actual = subject.x509
-      expect(actual).to be_instance_of(OpenSSL::X509::Certificate)
-      expect(actual.to_s).to eql(expected.to_s)
-    end
+    let(:expected) { OpenSSL::X509::Certificate.new(certificate.to_s) }
+    let(:actual) { subject.x509 }
+
+    specify { expect(actual).to be_instance_of(OpenSSL::X509::Certificate) }
+    specify { expect(actual.to_s).to eql(expected.to_s) }
   end
 
   describe '#expired?' do
     let(:certificate) { OpenSSL::X509::Certificate.new }
 
-    it 'returns false, when the certificate has not expired yet' do
-      certificate.not_before = 1.minute.ago
-      certificate.not_after = 10.minutes.from_now
+    context 'when the certificate has not expired yet' do
+      subject { described_class.new(certificate, use: :signing) }
 
-      subject = described_class.new(certificate, use: :signing)
-      expect(subject).not_to be_expired(Time.now)
+      before do
+        certificate.not_before = 1.minute.ago
+        certificate.not_after = 10.minutes.from_now
+      end
+
+      specify { expect(subject).not_to be_expired(Time.now) }
     end
 
-    it 'returns true, when the current time is after the time of expiration' do
-      certificate.not_before = 10.minutes.ago
-      certificate.not_after = 1.minute.ago
+    context 'when the current time is after the time of the expiration' do
+      subject { described_class.new(certificate, use: :signing) }
+
+      before do
+        certificate.not_before = 10.minutes.ago
+        certificate.not_after = 1.minute.ago
+      end
 
-      subject = described_class.new(certificate, use: :signing)
-      expect(subject).to be_expired(Time.now)
+      specify { expect(subject).to be_expired(Time.now) }
     end
   end
 
@@ -166,29 +155,27 @@ RSpec.describe Xml::Kit::Certificate do
       certificate.not_after = 10.minutes.from_now
     end
 
-    it 'delegates not_after to the x509 certificate' do
-      expect(subject.not_after).to eql(certificate.not_after)
-    end
-
-    it 'delegates not_before to the x509 certificate' do
-      expect(subject.not_before).to eql(certificate.not_before)
-    end
+    specify { expect(subject.not_after).to eql(certificate.not_after) }
+    specify { expect(subject.not_before).to eql(certificate.not_before) }
   end
 
   describe '#to_xml' do
-    it 'generates the correct xml' do
-      result = Hash.from_xml(subject.to_xml)
-      expect(result['KeyDescriptor']).to be_present
-      expect(result['KeyDescriptor']['use']).to eql('signing')
-      expect(result['KeyDescriptor']['KeyInfo']['xmlns']).to eql(Xml::Kit::Namespaces::XMLDSIG)
-      expect(result['KeyDescriptor']['KeyInfo']['X509Data']['X509Certificate']).to eql(subject.stripped)
+    context 'when generated' do
+      let(:result) { Hash.from_xml(subject.to_xml) }
+
+      specify { expect(result['KeyDescriptor']).to be_present }
+      specify { expect(result['KeyDescriptor']['use']).to eql('signing') }
+      specify { expect(result['KeyDescriptor']['KeyInfo']['xmlns']).to eql(Xml::Kit::Namespaces::XMLDSIG) }
+      specify { expect(result['KeyDescriptor']['KeyInfo']['X509Data']['X509Certificate']).to eql(subject.stripped) }
     end
 
-    it 'omits the `use` when the cert can be used for both signing and encryption' do
-      subject = described_class.new(certificate, use: nil)
-      result = Hash.from_xml(subject.to_xml)
-      expect(result['KeyDescriptor']).to be_present
-      expect(result['KeyDescriptor']['use']).to be_nil
+    context 'when the certificate can be used for both signing and encryption' do
+      subject { described_class.new(certificate, use: nil) }
+
+      let(:result) { Hash.from_xml(subject.to_xml) }
+
+      specify { expect(result['KeyDescriptor']).to be_present }
+      specify { expect(result['KeyDescriptor']['use']).to be_nil }
     end
   end
 end
spec/xml/kit/decryption_spec.rb
@@ -5,161 +5,163 @@ RSpec.describe Xml::Kit::Decryption do
     let(:secret) { FFaker::Movie.title }
     let(:password) { FFaker::Movie.title }
 
-    it 'decrypts AES-128-CBC data' do
-      certificate_pem, private_key_pem = generate_key_pair(password)
-
-      public_key = OpenSSL::X509::Certificate.new(certificate_pem).public_key
-      private_key = OpenSSL::PKey::RSA.new(private_key_pem, password)
-
-      cipher = OpenSSL::Cipher.new('AES-128-CBC')
-      cipher.encrypt
-      key = cipher.random_key
-      iv = cipher.random_iv
-      encrypted = cipher.update(secret) + cipher.final
-
-      data = {
-        'EncryptedData' => {
-          'xmlns:xenc' => 'http://www.w3.org/2001/04/xmlenc#',
-          'xmlns:dsig' => 'http://www.w3.org/2000/09/xmldsig#',
-          'Type' => 'http://www.w3.org/2001/04/xmlenc#Element',
-          'EncryptionMethod' => {
-            'Algorithm' => 'http://www.w3.org/2001/04/xmlenc#aes128-cbc'
-          },
-          'KeyInfo' => {
+    context 'when decrypting AES-128-CBC data' do
+      subject { described_class.new(private_keys: [private_key]) }
+
+      let(:key_pair) { generate_key_pair(password) }
+      let(:certificate_pem) { key_pair[0] }
+      let(:private_key_pem) { key_pair[1] }
+      let(:public_key) { OpenSSL::X509::Certificate.new(certificate_pem).public_key }
+      let(:private_key) { OpenSSL::PKey::RSA.new(private_key_pem, password) }
+      let(:data) do
+        cipher = OpenSSL::Cipher.new('AES-128-CBC')
+        cipher.encrypt
+        key = cipher.random_key
+        iv = cipher.random_iv
+        encrypted = cipher.update(secret) + cipher.final
+        {
+          'EncryptedData' => {
+            'xmlns:xenc' => 'http://www.w3.org/2001/04/xmlenc#',
             'xmlns:dsig' => 'http://www.w3.org/2000/09/xmldsig#',
-            'EncryptedKey' => {
-              'EncryptionMethod' => {
-                'Algorithm' => 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'
-              },
-              'CipherData' => {
-                'CipherValue' => Base64.encode64(public_key.public_encrypt(key))
+            'Type' => 'http://www.w3.org/2001/04/xmlenc#Element',
+            'EncryptionMethod' => {
+              'Algorithm' => 'http://www.w3.org/2001/04/xmlenc#aes128-cbc'
+            },
+            'KeyInfo' => {
+              'xmlns:dsig' => 'http://www.w3.org/2000/09/xmldsig#',
+              'EncryptedKey' => {
+                'EncryptionMethod' => {
+                  'Algorithm' => 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'
+                },
+                'CipherData' => {
+                  'CipherValue' => Base64.encode64(public_key.public_encrypt(key))
+                }
               }
+            },
+            'CipherData' => {
+              'CipherValue' => Base64.encode64(iv + encrypted)
             }
-          },
-          'CipherData' => {
-            'CipherValue' => Base64.encode64(iv + encrypted)
           }
         }
-      }
-      subject = described_class.new(private_keys: [private_key])
-      decrypted = subject.decrypt_hash(data)
-      expect(decrypted.strip).to eql(secret)
+      end
+
+      specify { expect(subject.decrypt_hash(data).strip).to eql(secret) }
     end
 
-    it 'attempts to decrypt with each encryption keypair' do
-      certificate_pem, private_key_pem = generate_key_pair(password)
-      public_key = OpenSSL::X509::Certificate.new(certificate_pem).public_key
-      private_key = OpenSSL::PKey::RSA.new(private_key_pem, password)
-
-      cipher = OpenSSL::Cipher.new('AES-128-CBC')
-      cipher.encrypt
-      key = cipher.random_key
-      iv = cipher.random_iv
-      encrypted = cipher.update(secret) + cipher.final
-
-      data = {
-        'EncryptedData' => {
-          'xmlns:xenc' => 'http://www.w3.org/2001/04/xmlenc#',
-          'xmlns:dsig' => 'http://www.w3.org/2000/09/xmldsig#',
-          'Type' => 'http://www.w3.org/2001/04/xmlenc#Element',
-          'EncryptionMethod' => {
-            'Algorithm' => 'http://www.w3.org/2001/04/xmlenc#aes128-cbc'
-          },
-          'KeyInfo' => {
+    context 'when multiple encryption keys are present' do
+      subject { described_class.new(private_keys: [other_private_key, private_key]) }
+
+      let(:key_pair) { generate_key_pair(password) }
+      let(:other_key_pair) { generate_key_pair(password) }
+      let(:certificate_pem) { key_pair[0] }
+      let(:private_key_pem) { key_pair[1] }
+      let(:public_key) { OpenSSL::X509::Certificate.new(certificate_pem).public_key }
+      let(:private_key) { OpenSSL::PKey::RSA.new(private_key_pem, password) }
+      let(:other_private_key_pem) { other_key_pair[1] }
+      let(:other_private_key) { OpenSSL::PKey::RSA.new(other_private_key_pem, password) }
+
+      let(:data) do
+        cipher = OpenSSL::Cipher.new('AES-128-CBC')
+        cipher.encrypt
+        key = cipher.random_key
+        iv = cipher.random_iv
+        encrypted = cipher.update(secret) + cipher.final
+
+        {
+          'EncryptedData' => {
+            'xmlns:xenc' => 'http://www.w3.org/2001/04/xmlenc#',
             'xmlns:dsig' => 'http://www.w3.org/2000/09/xmldsig#',
-            'EncryptedKey' => {
-              'EncryptionMethod' => {
-                'Algorithm' => 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'
-              },
-              'CipherData' => {
-                'CipherValue' => Base64.encode64(public_key.public_encrypt(key))
+            'Type' => 'http://www.w3.org/2001/04/xmlenc#Element',
+            'EncryptionMethod' => {
+              'Algorithm' => 'http://www.w3.org/2001/04/xmlenc#aes128-cbc'
+            },
+            'KeyInfo' => {
+              'xmlns:dsig' => 'http://www.w3.org/2000/09/xmldsig#',
+              'EncryptedKey' => {
+                'EncryptionMethod' => {
+                  'Algorithm' => 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'
+                },
+                'CipherData' => {
+                  'CipherValue' => Base64.encode64(public_key.public_encrypt(key))
+                }
               }
+            },
+            'CipherData' => {
+              'CipherValue' => Base64.encode64(iv + encrypted)
             }
-          },
-          'CipherData' => {
-            'CipherValue' => Base64.encode64(iv + encrypted)
           }
         }
-      }
-
-      _, other_private_key_pem = generate_key_pair(password)
-      other_private_key = OpenSSL::PKey::RSA.new(other_private_key_pem, password)
+      end
 
-      subject = described_class.new(private_keys: [other_private_key, private_key])
-      decrypted = subject.decrypt_hash(data)
-      expect(decrypted.strip).to eql(secret)
+      specify { expect(subject.decrypt_hash(data).strip).to eql(secret) }
     end
 
-    it 'raise an error when it cannot decrypt the data' do
-      certificate_pem, = generate_key_pair(password)
-      public_key = OpenSSL::X509::Certificate.new(certificate_pem).public_key
-
-      cipher = OpenSSL::Cipher.new('AES-128-CBC')
-      cipher.encrypt
-      key = cipher.random_key
-      iv = cipher.random_iv
-      encrypted = cipher.update(secret) + cipher.final
-
-      data = {
-        'EncryptedData' => {
-          'xmlns:xenc' => 'http://www.w3.org/2001/04/xmlenc#',
-          'xmlns:dsig' => 'http://www.w3.org/2000/09/xmldsig#',
-          'Type' => 'http://www.w3.org/2001/04/xmlenc#Element',
-          'EncryptionMethod' => {
-            'Algorithm' => 'http://www.w3.org/2001/04/xmlenc#aes128-cbc'
-          },
-          'KeyInfo' => {
+    context 'when it cannot decrypt the data' do
+      subject { described_class.new(private_keys: [new_private_key]) }
+
+      let(:key_pair) { generate_key_pair(password) }
+      let(:certificate_pem) { key_pair[0] }
+      let(:public_key) { OpenSSL::X509::Certificate.new(certificate_pem).public_key }
+      let(:new_private_key_pem) { generate_key_pair(password)[1] }
+      let(:new_private_key) { OpenSSL::PKey::RSA.new(new_private_key_pem, password) }
+      let(:data) do
+        cipher = OpenSSL::Cipher.new('AES-128-CBC')
+        cipher.encrypt
+        key = cipher.random_key
+        iv = cipher.random_iv
+        encrypted = cipher.update(secret) + cipher.final
+
+        {
+          'EncryptedData' => {
+            'xmlns:xenc' => 'http://www.w3.org/2001/04/xmlenc#',
             'xmlns:dsig' => 'http://www.w3.org/2000/09/xmldsig#',
-            'EncryptedKey' => {
-              'EncryptionMethod' => {
-                'Algorithm' => 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'
-              },
-              'CipherData' => {
-                'CipherValue' => Base64.encode64(public_key.public_encrypt(key))
+            'Type' => 'http://www.w3.org/2001/04/xmlenc#Element',
+            'EncryptionMethod' => {
+              'Algorithm' => 'http://www.w3.org/2001/04/xmlenc#aes128-cbc'
+            },
+            'KeyInfo' => {
+              'xmlns:dsig' => 'http://www.w3.org/2000/09/xmldsig#',
+              'EncryptedKey' => {
+                'EncryptionMethod' => {
+                  'Algorithm' => 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'
+                },
+                'CipherData' => {
+                  'CipherValue' => Base64.encode64(public_key.public_encrypt(key))
+                }
               }
+            },
+            'CipherData' => {
+              'CipherValue' => Base64.encode64(iv + encrypted)
             }
-          },
-          'CipherData' => {
-            'CipherValue' => Base64.encode64(iv + encrypted)
           }
         }
-      }
-
-      new_private_key_pem = generate_key_pair(password)[1]
-      new_private_key = OpenSSL::PKey::RSA.new(new_private_key_pem, password)
-      subject = described_class.new(private_keys: [new_private_key])
-      expect do
-        subject.decrypt_hash(data)
-      end.to raise_error(OpenSSL::PKey::RSAError)
+      end
+
+      specify { expect { subject.decrypt_hash(data) }.to raise_error(OpenSSL::PKey::RSAError) }
     end
   end
 
   describe '#decrypt_document' do
+    subject { described_class.new(private_keys: [item.encryption_key_pair.private_key]) }
+
     let(:item) { Item.new }
     let(:document) { Nokogiri::XML(item.to_xml) }
-    let(:subject) { described_class.new(private_keys: [item.encryption_key_pair.private_key]) }
     let(:encrypted_node) { document.at_xpath('/Item/Encrypted/xmlenc:EncryptedData', 'xmlenc' => 'http://www.w3.org/2001/04/xmlenc#') }
 
-    it 'decrypts a nokogiri document' do
-      expect(subject.decrypt_node(encrypted_node).name).to eql('EncryptMe')
-    end
+    specify { expect(subject.decrypt_node(encrypted_node).name).to eql('EncryptMe') }
+    specify { expect(subject.decrypt_node(nil)).to be_nil }
 
-    it 'returns the node when it does not contain an EncryptedData' do
-      document = Nokogiri::XML('<hello><world></world></hello>')
-      node = document.at_xpath('//hello/world')
-      expect(subject.decrypt_node(node)).to eql(node)
-    end
+    context 'when it does not contain an EncryptedData' do
+      let(:document) { Nokogiri::XML('<hello><world></world></hello>') }
+      let(:node) { document.at_xpath('//hello/world') }
 
-    it 'returns nil when the node is nil' do
-      expect(subject.decrypt_node(nil)).to be_nil
+      specify { expect(subject.decrypt_node(node)).to eql(node) }
     end
 
-    it 'raises an error when the document cannot be decrypted' do
-      subject = described_class.new(private_keys: [])
+    context 'when the document cannot be decrypted' do
+      subject { described_class.new(private_keys: []) }
 
-      expect do
-        subject.decrypt_node(encrypted_node)
-      end.to raise_error(Xml::Kit::DecryptionError)
+      specify { expect { subject.decrypt_node(encrypted_node) }.to raise_error(Xml::Kit::DecryptionError) }
     end
   end
 end
spec/xml/kit/document_spec.rb
@@ -2,34 +2,43 @@
 
 RSpec.describe Xml::Kit::Document do
   describe '#valid_signature?' do
-    let(:login_url) { "https://#{FFaker::Internet.domain_name}/login" }
-    let(:logout_url) { "https://#{FFaker::Internet.domain_name}/logout" }
     let(:signed_xml) { Item.new.to_xml }
 
-    it 'returns true, when the digest and signature is valid' do
-      expect(described_class.new(signed_xml)).to be_valid
+    context 'when the signature is valid' do
+      subject { described_class.new(signed_xml) }
+
+      specify { expect(subject).to be_valid }
     end
 
-    it 'returns false, when the SHA1 digest is not valid' do
-      subject = described_class.new(signed_xml.gsub('Item', 'uhoh'))
-      expect(subject).not_to be_valid
-      expect(subject.errors[:digest_value]).to be_present
+    context 'when the SHA1 digest is not valid' do
+      subject { described_class.new(signed_xml.gsub('Item', 'uhoh')) }
+
+      before { subject.valid? }
+
+      specify { expect(subject).not_to be_valid }
+      specify { expect(subject.errors[:digest_value]).to be_present }
     end
 
-    it 'is invalid when digest is incorrect' do
-      old_digest = Hash.from_xml(signed_xml)['Item']['Signature']['SignedInfo']['Reference']['DigestValue']
+    context 'when the digest is incorrect' do
+      subject { described_class.new(signed_xml.gsub(old_digest, 'sabotage')) }
+
+      let(:old_digest) { Hash.from_xml(signed_xml)['Item']['Signature']['SignedInfo']['Reference']['DigestValue'] }
 
-      subject = described_class.new(signed_xml.gsub(old_digest, 'sabotage'))
-      expect(subject).not_to be_valid
-      expect(subject.errors[:digest_value]).to be_present
+      before { subject.valid? }
+
+      specify { expect(subject).not_to be_valid }
+      specify { expect(subject.errors[:digest_value]).to be_present }
     end
 
-    it 'returns false, when the signature is invalid' do
-      old_signature = Hash.from_xml(signed_xml)['Item']['Signature']['SignatureValue']
-      signed_xml.gsub!(old_signature, 'sabotage')
-      subject = described_class.new(signed_xml)
-      expect(subject).not_to be_valid
-      expect(subject.errors[:signature]).to be_present
+    context 'when the signature is invalid' do
+      subject { described_class.new(signed_xml.gsub(old_signature, 'sabotage')) }
+
+      let(:old_signature) { Hash.from_xml(signed_xml)['Item']['Signature']['SignatureValue'] }
+
+      before { subject.valid? }
+
+      specify { expect(subject).not_to be_valid }
+      specify { expect(subject.errors[:signature]).to be_present }
     end
 
     context 'when the certificate is expired' do
@@ -48,7 +57,7 @@ RSpec.describe Xml::Kit::Document do
         expired_certificate.sign(private_key, digest_algorithm)
       end
 
-      it 'is invalid' do
+      specify do
         certificate = ::Xml::Kit::Certificate.new(expired_certificate)
         item.sign_with(certificate.to_key_pair(private_key))
         subject = described_class.new(item.to_xml)
spec/xml/kit/fingerprint_spec.rb
@@ -2,19 +2,17 @@
 
 RSpec.describe Xml::Kit::Fingerprint do
   describe '#sha' do
+    let(:key_pair) { generate_key_pair('password') }
+    let(:certificate) { key_pair[0] }
+    let(:x509) { OpenSSL::X509::Certificate.new(certificate) }
+
     it 'returns the SHA256' do
-      certificate, = generate_key_pair('password')
-      x509 = OpenSSL::X509::Certificate.new(certificate)
       sha256 = OpenSSL::Digest::SHA256.new.hexdigest(x509.to_der).upcase.scan(/../).join(':')
-
       expect(described_class.new(certificate).algorithm(OpenSSL::Digest::SHA256)).to eql(sha256)
     end
 
     it 'returns the SHA1' do
-      certificate, = generate_key_pair('password')
-      x509 = OpenSSL::X509::Certificate.new(certificate)
       sha1 = OpenSSL::Digest::SHA1.new.hexdigest(x509.to_der).upcase.scan(/../).join(':')
-
       expect(described_class.new(certificate).algorithm(OpenSSL::Digest::SHA1)).to eql(sha1)
     end
   end