class Samlr::Signature

A SAML specific implementation en.wikipedia.org/wiki/XML_Signature

Attributes

document[R]
fingerprint[R]
options[R]
original[R]
prefix[R]
signature[R]

Public Class Methods

new(original, prefix, options) click to toggle source

Is initialized with the source document and a path to the element embedding the signature

# File lib/samlr/signature.rb, line 12
def initialize(original, prefix, options)
  # Signature validations require document alterations
  @original = original
  @document = original.dup
  @prefix   = prefix
  @options  = options

  if @signature = document.at("#{prefix}/ds:Signature", NS_MAP)
    @signature.remove # enveloped signatures only
  end

  @fingerprint = if options[:fingerprint]
    Fingerprint.from_string(options[:fingerprint])
  elsif options[:certificate]
    Certificate.new(options[:certificate]).fingerprint
  end
end

Public Instance Methods

missing?() click to toggle source
# File lib/samlr/signature.rb, line 34
def missing?
  signature.nil? || certificate.nil?
end
present?() click to toggle source
# File lib/samlr/signature.rb, line 30
def present?
  !missing?
end
references() click to toggle source
# File lib/samlr/signature.rb, line 48
def references
  @references ||= [].tap do |refs|
    original.xpath("#{prefix}/ds:Signature/ds:SignedInfo/ds:Reference[@URI]", NS_MAP).each do |ref|
      refs << Samlr::Reference.new(ref)
    end
  end
end
verify!() click to toggle source
# File lib/samlr/signature.rb, line 38
def verify!
  raise SignatureError.new("No signature at #{prefix}/ds:Signature") unless present?

  verify_fingerprint! unless options[:skip_fingerprint]
  verify_digests!
  verify_signature!

  true
end

Private Instance Methods

certificate() click to toggle source
# File lib/samlr/signature.rb, line 113
def certificate
  @certificate ||= begin
    if node = certificate_node
      Certificate.new(Base64.decode64(node.text))
    elsif cert = options[:certificate]
      Certificate.new(cert)
    else
      nil
    end
  end
end
certificate!() click to toggle source
# File lib/samlr/signature.rb, line 125
def certificate!
  certificate || raise(SignatureError.new("No X509Certificate element in response signature. Cannot validate signature."))
end
certificate_node() click to toggle source
# File lib/samlr/signature.rb, line 129
def certificate_node
  signature.at("./ds:KeyInfo/ds:X509Data/ds:X509Certificate", NS_MAP)
end
decoded_signature_value() click to toggle source
# File lib/samlr/signature.rb, line 109
def decoded_signature_value
  @decoded_signature_value = Base64.decode64(signature_value)
end
referenced_node(id) click to toggle source

Looks up node by id, checks that there's only a single node with a given id

# File lib/samlr/signature.rb, line 91
def referenced_node(id)
  nodes = document.xpath("//*[@ID='#{id}']")

  if nodes.size != 1
    raise SignatureError.new("Reference validation error: Invalid element references", "Expected 1 element with id #{id}, found #{nodes.size}")
  end

  nodes.first
end
signature_method() click to toggle source
# File lib/samlr/signature.rb, line 101
def signature_method
  @signature_method ||= Samlr::Tools.algorithm(signature.at("./ds:SignedInfo/ds:SignatureMethod/@Algorithm", NS_MAP).try(:value))
end
signature_value() click to toggle source
# File lib/samlr/signature.rb, line 105
def signature_value
  @signature_value ||= signature.at("./ds:SignatureValue", NS_MAP).text
end
verify_digests!() click to toggle source

Tests that the document content has not been edited

# File lib/samlr/signature.rb, line 68
def verify_digests!
  references.each do |reference|
    node    = referenced_node(reference.uri)
    canoned = node.canonicalize(C14N, reference.namespaces)
    digest  = reference.digest_method.digest(canoned)

    if digest != reference.decoded_digest_value
      raise SignatureError.new("Reference validation error: Digest mismatch for #{reference.uri}")
    end
  end
end
verify_fingerprint!() click to toggle source

Establishes trust that the remote party is who you think

# File lib/samlr/signature.rb, line 63
def verify_fingerprint!
  fingerprint.verify!(certificate!)
end
verify_signature!() click to toggle source

Tests correctness of the signature (and hence digests)

# File lib/samlr/signature.rb, line 81
def verify_signature!
  node      = original.at("#{prefix}/ds:Signature/ds:SignedInfo", NS_MAP)
  canoned   = node.canonicalize(C14N)

  unless x509.public_key.verify(signature_method.new, decoded_signature_value, canoned)
    raise SignatureError.new("Signature validation error: Possible canonicalization mismatch", "This canonicalizer returns #{canoned}")
  end
end
x509() click to toggle source
# File lib/samlr/signature.rb, line 58
def x509
  @x509 ||= certificate!.x509
end