HankoXml

Module: HankoXml

Defined in:
lib/hanko_xml.rb,
lib/hanko_xml/dom.rb,
lib/hanko_xml/key.rb,
lib/hanko_xml/xml.rb,
lib/hanko_xml/input.rb,
lib/hanko_xml/xades.rb,
lib/hanko_xml/errors.rb,
lib/hanko_xml/policy.rb,
lib/hanko_xml/signer.rb,
lib/hanko_xml/version.rb,
lib/hanko_xml/key_info.rb,
lib/hanko_xml/template.rb,
lib/hanko_xml/verifier.rb,
lib/hanko_xml/placement.rb,
lib/hanko_xml/reference.rb,
lib/hanko_xml/transform.rb,
lib/hanko_xml/algorithms.rb,
lib/hanko_xml/nokogiri_ext.rb,
lib/hanko_xml/verification.rb,
lib/hanko_xml/xades/locator.rb,
lib/hanko_xml/native_version.rb,
lib/hanko_xml/signature_info.rb,
lib/hanko_xml/xades/verifier.rb,
lib/hanko_xml/signed_document.rb,
lib/hanko_xml/xades/augmenter.rb,
lib/hanko_xml/xades/long_term.rb,
lib/hanko_xml/xades/timestamp.rb,
lib/hanko_xml/xades/timestamp.rb,
lib/hanko_xml/xades/validation_data.rb

Overview

Core extensions for Nokogiri::XML::Document and Nokogiri::XML::Node to support direct #sign, #sign!, #signature_valid?, #signatures, and #signed? See API_CONTRACT.md §6.6.

Defined Under Namespace

Modules: Algorithms, Dom, KeyInfo, NativeVersion, NokogiriExt, Xades Classes: CertificateExpired, ConfigurationError, DigestMismatch, Error, FrozenSignatureError, Input, Key, KeyLoadError, MultipleSignatures, NativeError, NativeVersionMismatch, NokogiriRequired, NotSignedError, ParseError, Placement, PlacementError, Policy, PolicyError, Reference, ReferenceError, ResolutionMismatch, SchemaError, SignatureInfo, SignatureInvalid, SignatureMissing, SignedDocument, Signer, SigningError, Template, Transform, UntrustedCertificate, Verification, VerificationError, Verifier, XadesError

Constant Summary collapse

NS =

XML-DSig namespace prefixes used in every XPath this library runs internally -- never //Signature, never local-name(). See API_CONTRACT.md §6.8.

{
  "ds" => "http://www.w3.org/2000/09/xmldsig#",
  "ds11" => "http://www.w3.org/2009/xmldsig11#",
  "ec" => "http://www.w3.org/2001/10/xml-exc-c14n#"
}.freeze
VERSION =
"0.2.0"

Class Method Summary collapse

Class Method Details

.capabilities ⇒ Object

Which capabilities are available given whether Nokogiri is loaded. See §6.1.



49
50
51
# File 'lib/hanko_xml.rb', line 49

def capabilities
  Dom.capabilities
end

.coerce_document(input, policy: nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Coerces any of the accepted document input shapes (§6.2) into a Nokogiri::XML::Document: an existing Document as-is, a Node via its #document, a String of markup, a path-like object, or anything responding to #read.

policy, when given, applies its max_document_size to unparsed input before it is read or parsed (S-12): see #read_source. An existing Document/Node is not checked here; Signer/Verifier check its serialized form before any native call.



57
58
59
60
61
62
# File 'lib/hanko_xml/xml.rb', line 57

def coerce_document(input, policy: nil)
  return input if Dom.document?(input)
  return input.document if Dom.node?(input)

  parse(read_source(input, policy: policy))
end

.nokogiri? ⇒ Boolean

True when Nokogiri is loaded. See §6.1.

Returns:

  • (Boolean)


43
44
45
# File 'lib/hanko_xml.rb', line 43

def nokogiri?
  Dom.available?
end

.parse(source) ⇒ Object

Parses source (a String, IO, or path-like object) the one way that does not break XML-DSig: strict (no RECOVER), nonet, never NOBLANKS, never NOENT, never HUGE. See API_CONTRACT.md §6.3.

Raises HankoXml::NokogiriRequired without Nokogiri, HankoXml::ParseError on malformed input.



21
22
23
24
25
26
27
28
# File 'lib/hanko_xml/xml.rb', line 21

def parse(source)
  Dom.require!("HankoXml.parse")

  doc, errors = Dom.parse(source)
  raise parse_error(errors) if errors.any?

  doc
end

.parse_error(errors) ⇒ Object

libxml2's diagnostics can quote the document (tag names, text), so they go in #xmlsec_errors, never in #message (#96, API_CONTRACT.md §5.10).



32
33
34
# File 'lib/hanko_xml/xml.rb', line 32

def parse_error(errors)
  HankoXml::ParseError.new("the document is not well-formed XML", xmlsec_errors: errors.map(&:message))
end

.read_source(input, policy: nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Reads unparsed input (§6.2) without parsing it: a String as-is, a path-like object's file, or anything responding to #read. +policy+'s max_document_size applies before the input is read (S-12): a String by #bytesize, a path by File.size, an IO by reading at most one byte past the limit.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/hanko_xml/xml.rb', line 71

def read_source(input, policy: nil)
  limit = policy&.max_document_size
  limit = nil unless limit&.finite?

  case input
  when String
    policy&.enforce_document_size!(input.bytesize)
    input
  when ->(obj) { obj.respond_to?(:to_path) }
    policy&.enforce_document_size!(::File.size(input.to_path))
    ::File.binread(input.to_path)
  when ->(obj) { obj.respond_to?(:read) }
    source = limit ? input.read(limit + 1) : input.read
    policy&.enforce_document_size!(source.to_s.bytesize)
    source.to_s
  else
    raise ArgumentError,
          "expected a Nokogiri::XML::Document/Node, an XML String, a path-like object " \
          "(Pathname/File), or an IO, got #{input.class}"
  end
end

.serialize(doc) ⇒ Object

Byte-faithful serialization: AS_XML only, never FORMAT (which reindents the tree and invalidates any signature over it). See API_CONTRACT.md §6.3.



40
41
42
43
44
# File 'lib/hanko_xml/xml.rb', line 40

def serialize(doc)
  Dom.require!("HankoXml.serialize")

  Dom.to_xml(doc)
end

.sign(doc_or_xml, **kwargs) ⇒ HankoXml::SignedDocument

Signs an XML document, returning a safely-serializable SignedDocument wrapper. This is a convenience shortcut that creates a Signer and calls #sign. The original document is not modified (a deep clone is created).

Parameters:

  • doc_or_xml (String, Nokogiri::XML::Node, Nokogiri::XML::Document, IO) —

    The XML to sign.

  • kwargs (Hash) —

    Configuration options passed to HankoXml::Signer#initialize.

Returns:



60
61
62
# File 'lib/hanko_xml.rb', line 60

def sign(doc_or_xml, **kwargs)
  HankoXml::Signer.new(**kwargs).sign(doc_or_xml)
end

.sign!(doc, **kwargs) ⇒ Nokogiri::XML::Node, Nokogiri::XML::Document

Signs a Nokogiri XML document in place. This is a convenience shortcut that creates a Signer and calls #sign!.

Parameters:

  • doc (Nokogiri::XML::Node, Nokogiri::XML::Document) —

    The document to mutate.

  • kwargs (Hash) —

    Configuration options passed to HankoXml::Signer#initialize.

Returns:

  • (Nokogiri::XML::Node, Nokogiri::XML::Document) —

    The mutated original document.



70
71
72
# File 'lib/hanko_xml.rb', line 70

def sign!(doc, **kwargs)
  HankoXml::Signer.new(**kwargs).sign!(doc)
end

.signature_present?(xml) ⇒ Boolean

Checks whether the document contains at least one populated ds:Signature. Does NOT verify the signature's cryptographic validity.

Parameters:

  • xml (String, Nokogiri::XML::Node, Nokogiri::XML::Document, IO) —

    The XML to check.

Returns:



108
109
110
111
# File 'lib/hanko_xml.rb', line 108

def signature_present?(xml)
  # Structural, from the native parse; needs no DOM (#102).
  HankoXml::Input.read(xml).scan([])[:signature_values].any?
end

.valid?(xml, **kwargs) ⇒ Boolean

Verifies an XML signature and answers with a plain boolean, so it is safe directly in a conditional. This is a convenience shortcut that creates a Verifier and calls #valid?.

There is deliberately no HankoXml.verify: a Verification is always truthy, so if HankoXml.verify(doc, ...) would be a security bug that reads like correct code (API_CONTRACT §5.1). For the rich object use verify! or HankoXml::Verifier#verify.

Parameters:

  • xml (String, Nokogiri::XML::Node, Nokogiri::XML::Document, IO) —

    The signed XML.

  • kwargs (Hash) —

    Configuration options passed to HankoXml::Verifier#initialize.

Returns:

  • (Boolean) —

    True only when the signature verified under the policy.

Raises:

  • (HankoXml::Error) —

    If parsing fails or trust sources are misconfigured.



87
88
89
# File 'lib/hanko_xml.rb', line 87

def valid?(xml, **kwargs)
  HankoXml::Verifier.new(**kwargs).valid?(xml)
end

.verify!(xml, **kwargs) ⇒ HankoXml::Verification

Verifies an XML signature, raising an exception if invalid. This is a convenience shortcut that creates a Verifier and calls #verify!.

Parameters:

  • xml (String, Nokogiri::XML::Node, Nokogiri::XML::Document, IO) —

    The signed XML.

  • kwargs (Hash) —

    Configuration options passed to HankoXml::Verifier#initialize.

Returns:

Raises:



99
100
101
# File 'lib/hanko_xml.rb', line 99

def verify!(xml, **kwargs)
  HankoXml::Verifier.new(**kwargs).verify!(xml)
end

.zero_copy_available? ⇒ Boolean

Returns whether zero-copy DOM verification is available on this platform.

Returns:

  • (Boolean) —

    Always false (libxmlsec1 requires serialization).



116
117
118
# File 'lib/hanko_xml.rb', line 116

def zero_copy_available?
  false
end