hanko_xml 0.1.0-arm64-darwin

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +44 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +222 -0
  5. data/THIRD_PARTY_LICENSES.md +392 -0
  6. data/dependencies.yml +87 -0
  7. data/lib/hanko_xml/3.1/hanko_xml.bundle +0 -0
  8. data/lib/hanko_xml/3.2/hanko_xml.bundle +0 -0
  9. data/lib/hanko_xml/3.3/hanko_xml.bundle +0 -0
  10. data/lib/hanko_xml/3.4/hanko_xml.bundle +0 -0
  11. data/lib/hanko_xml/algorithms.rb +228 -0
  12. data/lib/hanko_xml/dom.rb +137 -0
  13. data/lib/hanko_xml/errors.rb +132 -0
  14. data/lib/hanko_xml/exc-c14n.xsd +26 -0
  15. data/lib/hanko_xml/input.rb +50 -0
  16. data/lib/hanko_xml/key.rb +213 -0
  17. data/lib/hanko_xml/key_info.rb +67 -0
  18. data/lib/hanko_xml/native_version.rb +75 -0
  19. data/lib/hanko_xml/nokogiri_ext.rb +34 -0
  20. data/lib/hanko_xml/placement.rb +127 -0
  21. data/lib/hanko_xml/policy.rb +502 -0
  22. data/lib/hanko_xml/reference.rb +49 -0
  23. data/lib/hanko_xml/signature_info.rb +134 -0
  24. data/lib/hanko_xml/signed_document.rb +155 -0
  25. data/lib/hanko_xml/signer.rb +572 -0
  26. data/lib/hanko_xml/template.rb +150 -0
  27. data/lib/hanko_xml/transform.rb +87 -0
  28. data/lib/hanko_xml/verification.rb +377 -0
  29. data/lib/hanko_xml/verifier.rb +694 -0
  30. data/lib/hanko_xml/version.rb +5 -0
  31. data/lib/hanko_xml/xades/verifier.rb +319 -0
  32. data/lib/hanko_xml/xades.rb +287 -0
  33. data/lib/hanko_xml/xml.rb +93 -0
  34. data/lib/hanko_xml/xmldsig-core-schema.xsd +332 -0
  35. data/lib/hanko_xml.rb +122 -0
  36. data/sig/hanko_xml/key.rbs +48 -0
  37. data/sig/hanko_xml/native.rbs +12 -0
  38. data/sig/hanko_xml/native_version.rbs +13 -0
  39. data/sig/hanko_xml/nokogiri_ext.rbs +23 -0
  40. data/sig/hanko_xml/placement.rbs +27 -0
  41. data/sig/hanko_xml/policy.rbs +83 -0
  42. data/sig/hanko_xml/reference.rbs +36 -0
  43. data/sig/hanko_xml/signed_document.rbs +66 -0
  44. data/sig/hanko_xml/signer.rbs +50 -0
  45. data/sig/hanko_xml/template.rbs +41 -0
  46. data/sig/hanko_xml/verification.rbs +100 -0
  47. data/sig/hanko_xml/verifier.rbs +45 -0
  48. data/sig/hanko_xml/xades.rbs +70 -0
  49. data/sig/hanko_xml.rbs +62 -0
  50. metadata +104 -0
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HankoXml
4
+ # A safe-serialization wrapper around a signed Nokogiri::XML::Document.
5
+ # See API_CONTRACT.md §6.7.
6
+ #
7
+ # A bare Nokogiri document's #to_xml defaults to formatting/reindenting
8
+ # the tree, which invalidates the signature that was just computed. This
9
+ # wrapper intercepts serialization to ensure it is always byte-faithful.
10
+ #
11
+ # Read-only Nokogiri methods are delegated; mutators raise
12
+ # FrozenSignatureError (S-17). Nodes returned by the readers are still the
13
+ # live tree, so serialization also checks that the document is unchanged
14
+ # since signing and raises FrozenSignatureError if it is not.
15
+ #
16
+ # Signed without Nokogiri (#102), it holds only the signed bytes: the
17
+ # serializers, #valid? and #inspect work, #document is nil, and the
18
+ # Nokogiri readers and #signatures raise NokogiriRequired.
19
+ class SignedDocument
20
+ READ_METHODS = %i[at at_css at_xpath css search xpath root encoding errors namespaces
21
+ children version url].freeze
22
+
23
+ MUTATORS = %i[<< add_child add_next_sibling add_previous_sibling after before children= content=
24
+ inner_html= native_content= parent= remove remove_attribute replace root= set_attribute
25
+ []= delete encoding= unlink add_namespace add_namespace_definition default_namespace=
26
+ do_xinclude canonicalize! remove_namespaces! swap wrap].freeze
27
+
28
+ # @return [Nokogiri::XML::Document] The underlying Nokogiri document.
29
+ # Escape hatch: mutating it invalidates the signature, and this
30
+ # wrapper's serializers then raise FrozenSignatureError. Serialize an
31
+ # intentionally modified document with HankoXml.serialize(document).
32
+ attr_reader :document
33
+
34
+ # The signed bytes, without a DOM (#102). Signer#sign returns one when
35
+ # Nokogiri is not loaded.
36
+ # @param bytes [String] The signed document, as Native.sign returned it.
37
+ def self.from_bytes(bytes)
38
+ allocate.tap { |signed| signed.send(:initialize_from_bytes, bytes) }
39
+ end
40
+
41
+ # Initializes the wrapper with a signed document.
42
+ # @param document [Nokogiri::XML::Document] The signed document.
43
+ def initialize(document)
44
+ @document = document
45
+ @signed_xml = serialize(declaration: true).freeze
46
+ freeze
47
+ end
48
+
49
+ READ_METHODS.each do |name|
50
+ define_method(name) do |*args, **kwargs, &block|
51
+ require_document!("SignedDocument##{name}")
52
+ @document.public_send(name, *args, **kwargs, &block)
53
+ end
54
+ end
55
+
56
+ MUTATORS.each do |name|
57
+ define_method(name) do |*|
58
+ raise HankoXml::FrozenSignatureError,
59
+ "SignedDocument##{name} would invalidate the signature; use #document if you really mean to " \
60
+ "modify it, and HankoXml.serialize to write the result"
61
+ end
62
+ end
63
+
64
+ # Safely serializes the document to an XML String without re-formatting.
65
+ #
66
+ # @param declaration [Boolean] Whether to include the `<?xml ...?>` declaration.
67
+ # @return [String] The serialized XML.
68
+ # @raise [HankoXml::FrozenSignatureError] if the document changed since signing.
69
+ def to_xml(declaration: true)
70
+ return bytes_xml(declaration: declaration) unless @document
71
+
72
+ current = serialize(declaration: true)
73
+ unless current == @signed_xml
74
+ raise HankoXml::FrozenSignatureError,
75
+ "the signed document was modified after signing, so this serialization would not verify; " \
76
+ "use HankoXml.serialize(signed.document) if the change is intentional"
77
+ end
78
+
79
+ declaration ? current : serialize(declaration: false)
80
+ end
81
+
82
+ # Alias for {#to_xml}.
83
+ # @return [String]
84
+ def to_s
85
+ to_xml
86
+ end
87
+
88
+ # Serializes the document to a byte string.
89
+ # @return [String] The serialized XML in binary encoding.
90
+ def bytes
91
+ @document ? to_xml.b : @signed_xml
92
+ end
93
+
94
+ # Writes the safely serialized XML to an IO object.
95
+ #
96
+ # @param io [IO] The stream to write to.
97
+ # @param declaration [Boolean] Whether to include the `<?xml ...?>` declaration.
98
+ def write_to(io, declaration: true)
99
+ io.write(to_xml(declaration: declaration))
100
+ end
101
+
102
+ # Returns all `<ds:Signature>` nodes in the document.
103
+ # @return [Nokogiri::XML::NodeSet]
104
+ def signatures
105
+ require_document!("SignedDocument#signatures")
106
+ @document.xpath(".//ds:Signature", ds: NS["ds"])
107
+ end
108
+
109
+ # Verifies the signatures against a given trust source.
110
+ #
111
+ # @param kwargs [Hash] Configuration options passed to {Verifier#initialize}
112
+ # (certificate:, certificates:, store:, key:, keys:, policy:, schema:,
113
+ # signature:, etc).
114
+ # @return [Boolean] True if the signature is valid.
115
+ def valid?(**kwargs)
116
+ HankoXml.valid?(@document || @signed_xml, **kwargs)
117
+ end
118
+
119
+ def inspect
120
+ count = @document ? signatures.size : Native.scan(@signed_xml, [], false)[:signature_count]
121
+ "#<HankoXml::SignedDocument signatures=#{count}>"
122
+ end
123
+
124
+ private
125
+
126
+ def initialize_from_bytes(bytes)
127
+ @document = nil
128
+ @signed_xml = bytes.b.freeze
129
+ freeze
130
+ end
131
+
132
+ # The signed bytes, tagged with the encoding their declaration names.
133
+ # Leaving the declaration out drops it and the whitespace after it, which
134
+ # is outside the root element and so outside every signature.
135
+ def bytes_xml(declaration:)
136
+ xml = declaration ? @signed_xml : @signed_xml.sub(/\A<\?xml[^>]*\?>\s*/n, "")
137
+ xml.dup.force_encoding(declared_encoding)
138
+ end
139
+
140
+ def declared_encoding
141
+ name = @signed_xml[/\A<\?xml[^>]*\bencoding=["']([A-Za-z0-9._-]+)["']/n, 1]
142
+ name ? Encoding.find(name) : Encoding::UTF_8
143
+ rescue ArgumentError
144
+ Encoding::UTF_8
145
+ end
146
+
147
+ def require_document!(capability)
148
+ Dom.require!(capability) unless @document
149
+ end
150
+
151
+ def serialize(declaration:)
152
+ HankoXml::Dom.to_xml(@document, declaration: declaration)
153
+ end
154
+ end
155
+ end