main
  1# frozen_string_literal: true
  2
  3module Xml
  4  module Kit
  5    module Templatable
  6      # Can be used to disable embeding a signature.
  7      # By default a signature will be embedded if a signing
  8      # certificate is available.
  9      attr_accessor :embed_signature
 10
 11      # Used to enable/disable encrypting the document.
 12      attr_accessor :encrypt
 13
 14      # The [Xml::Kit::KeyPair] to use for generating a signature.
 15      attr_accessor :signing_key_pair
 16
 17      # The [Xml::Kit::Certificate] that contains the public key to use for encrypting the document.
 18      attr_accessor :encryption_certificate
 19
 20      # Allows you to specify the digest method algorithm. (Default: SHA256)
 21      # A list of digest methods can be found in [Xml::Kit::Signature].
 22      attr_accessor :digest_method
 23
 24      # Allows you to specify the signature method algorithm. (Default: SHA256)
 25      # A list of signature methods can be found in [Xml::Kit::Signature].
 26      attr_accessor :signature_method
 27
 28      # Returns the generated XML document with an XML Digital Signature and XML Encryption.
 29      def to_xml(xml: ::Builder::XmlMarkup.new, pretty: false)
 30        result = signatures.complete(render(self, xml: xml))
 31        pretty ? Nokogiri::XML(result).to_xml(indent: 2) : result
 32      end
 33
 34      # Generates an {#Xml::Kit::EncryptedKey} section. https://www.w3.org/TR/xmlenc-core1/#sec-EncryptedKey
 35      #
 36      # @since 0.3.0
 37      # @param xml [Builder::XmlMarkup] the xml builder instance
 38      # @param id [String] the id of EncryptedKey element
 39      def encrypt_key_for(xml:, id:, key_info: nil)
 40        ::Xml::Kit::EncryptedKey.new(
 41          id: id,
 42          asymmetric_cipher: asymmetric_cipher,
 43          symmetric_cipher: symmetric_cipher,
 44          key_info: key_info
 45        ).to_xml(xml: xml)
 46      end
 47
 48      # @deprecated Use {#encrypt_data_for} instead of this
 49      def encryption_for(*args, &block)
 50        ::Xml::Kit.deprecate(
 51          'encryption_for is deprecated. Use encrypt_data_for instead.'
 52        )
 53        encrypt_data_for(*args, &block)
 54      end
 55
 56      # Generates an {#Xml::Kit::EncryptedData} section. https://www.w3.org/TR/xmlenc-core1/#sec-EncryptedData
 57      #
 58      # @since 0.3.0
 59      # @param xml [Builder::XmlMarkup] the xml builder instance
 60      # @param key_info [Xml::Kit::KeyInfo] the key info to render in the EncryptedData
 61      def encrypt_data_for(xml:, key_info: nil)
 62        return yield xml unless encrypt?
 63
 64        temp = ::Builder::XmlMarkup.new
 65        yield temp
 66        ::Xml::Kit::EncryptedData.new(
 67          signatures.complete(temp.target!),
 68          symmetric_cipher: symmetric_cipher,
 69          asymmetric_cipher: asymmetric_cipher,
 70          key_info: key_info
 71        ).to_xml(xml: xml)
 72      end
 73
 74      # Provides a default RSA asymmetric cipher. Can be overridden to provide custom ciphers.
 75      #
 76      # @abstract
 77      # @since 0.3.0
 78      def asymmetric_cipher(algorithm: Crypto::RsaCipher::ALGORITHM)
 79        raise Xml::Kit::Error, 'encryption_certificate is not specified.' unless encryption_certificate
 80
 81        @asymmetric_cipher ||= Crypto.cipher_for(
 82          algorithm,
 83          encryption_certificate.public_key
 84        )
 85      end
 86
 87      # Provides a default aes256-cbc symmetric cipher. Can be overridden to provide custom ciphers.
 88      #
 89      # @abstract
 90      # @since 0.3.0
 91      def symmetric_cipher
 92        @symmetric_cipher ||= Crypto::SymmetricCipher.new
 93      end
 94
 95      def render(model, options)
 96        ::Xml::Kit::Template.new(model).to_xml(options)
 97      end
 98
 99      def signature_for(reference_id:, xml:)
100        return unless sign?
101
102        signatures.build(reference_id).to_xml(xml: xml)
103      end
104
105      # Allows you to specify which key pair to use for generating an XML digital signature.
106      #
107      # @param key_pair [Xml::Kit::KeyPair] the key pair to use for signing.
108      def sign_with(key_pair, signature_method: :SHA256, digest_method: :SHA256)
109        self.signing_key_pair = key_pair
110        self.embed_signature = true
111        self.signature_method = signature_method
112        self.digest_method = digest_method
113        signatures.sign_with(key_pair)
114      end
115
116      # Allows you to specify which public key to use for generating an XML encrypted element.
117      #
118      # @param certificate [Xml::Kit::Certificate] the certificate containing the public key to use for encryption.
119      def encrypt_with(certificate)
120        self.encrypt = true
121        self.encryption_certificate = certificate
122      end
123
124      private
125
126      def sign?
127        embed_signature
128      end
129
130      # @!visibility private
131      def signatures
132        @signatures ||= ::Xml::Kit::Signatures.new(
133          key_pair: signing_key_pair,
134          digest_method: digest_method || :SHA256,
135          signature_method: signature_method || :SHA256
136        )
137      end
138
139      # @!visibility private
140      def encrypt?
141        encrypt && encryption_certificate
142      end
143    end
144  end
145end