main
1# frozen_string_literal: true
2
3module Xml
4 module Kit
5 # {include:file:spec/xml/kit/decryption_spec.rb}
6 class Decryption
7 # The list of private keys to use to attempt to decrypt the document.
8 attr_reader :cipher_registry, :private_keys
9
10 def initialize(private_keys:, cipher_registry: ::Xml::Kit::Crypto)
11 @private_keys = private_keys
12 @cipher_registry = cipher_registry
13 end
14
15 # Decrypts an EncryptedData section of an XML document.
16 #
17 # @param data [Hash] the XML document converted to a [Hash] using Hash.from_xml.
18 # @deprecated Use {#decrypt_hash} instead of this
19 def decrypt(data)
20 ::Xml::Kit.deprecate(
21 'decrypt is deprecated. Use decrypt_xml or decrypt_hash instead.'
22 )
23 decrypt_hash(data)
24 end
25
26 # Decrypts an EncryptedData section of an XML document.
27 #
28 # @param raw_xml [String] the XML document as a string.
29 def decrypt_xml(raw_xml)
30 decrypt_hash(Hash.from_xml(raw_xml))
31 end
32
33 # Decrypts an EncryptedData section of an XML document.
34 #
35 # @param hash [Hash] the XML document converted to a [Hash] using Hash.from_xml.
36 def decrypt_hash(hash)
37 data = hash['EncryptedData']
38 to_plaintext(
39 Base64.decode64(data['CipherData']['CipherValue']),
40 symmetric_key_from(data['KeyInfo']['EncryptedKey']),
41 data['EncryptionMethod']['Algorithm']
42 )
43 end
44
45 # Decrypts an EncryptedData Nokogiri::XML::Element.
46 #
47 # @param node [Nokogiri::XML::Element.] the XML node to decrypt.
48 def decrypt_node(node)
49 return node unless !node.nil? && node.name == 'EncryptedData'
50
51 node.parent.replace(decrypt_xml(node.to_s))[0]
52 end
53
54 private
55
56 def symmetric_key_from(encrypted_key, attempts = private_keys.count)
57 cipher, algorithm = cipher_and_algorithm_from(encrypted_key)
58 private_keys.each do |private_key|
59 attempts -= 1
60 return to_plaintext(cipher, private_key, algorithm)
61 rescue OpenSSL::PKey::RSAError
62 raise if attempts.zero?
63 end
64 raise DecryptionError, private_keys
65 end
66
67 def to_plaintext(cipher_text, private_key, algorithm)
68 cipher_registry.cipher_for(algorithm, private_key).decrypt(cipher_text)
69 end
70
71 def cipher_and_algorithm_from(encrypted_key)
72 [
73 Base64.decode64(encrypted_key['CipherData']['CipherValue']),
74 encrypted_key['EncryptionMethod']['Algorithm']
75 ]
76 end
77 end
78 end
79end