Skip to content

Commit c6577e7

Browse files
committed
Add signature verification of decrypted attachments
1 parent 0d0a03e commit c6577e7

File tree

1 file changed

+16
-11
lines changed

1 file changed

+16
-11
lines changed

lib/akami/wsse/verify_signature.rb

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,13 @@ class InvalidSignedValue < RuntimeError; end
1414

1515
attr_reader :document
1616

17-
def initialize(xml)
17+
# @param xml [String] The XML document to verify
18+
# @param decrypted_attachments [Hash] A hash of decrypted attachments: { 'id' => 'decrypted_string' }
19+
# For example: the decrypted_attachments of a gzipped xml is the gzipped base64 string, the result of the decryption
20+
# { 'phase4-att-1f34-4d68a..' => 'kZ\xB4\xCD}\xCB..' }
21+
def initialize(xml, decrypted_attachments: {})
1822
@document = Nokogiri::XML(xml.to_s, &:noblanks)
23+
@decrypted_attachments = decrypted_attachments
1924
end
2025

2126
# Returns XML namespaces that are used internally for document querying.
@@ -78,14 +83,19 @@ def digesters
7883

7984
def verify
8085
document.xpath('//wse:Security/ds:Signature/ds:SignedInfo/ds:Reference', namespaces).each do |ref|
81-
next unless ref.attributes['URI'].value.start_with?('#')
82-
8386
digest_algorithm = ref.at_xpath('//ds:DigestMethod', namespaces)['Algorithm']
8487

8588
transform_inclusive_ns = inclusive_namespaces(ref, './/ds:Transforms/ds:Transform/ec:InclusiveNamespaces')
8689

87-
element_id = ref.attributes['URI'].value[1..-1] # strip leading '#'
88-
element = document.at_xpath(%(//*[@wsu:Id="#{element_id}"]), namespaces)
90+
ref_uri = ref.attributes['URI'].value
91+
if ref_uri.start_with?("#")
92+
element_id = ref_uri.sub(/^#/, '')
93+
element = document.at_xpath(%(//*[@wsu:Id="#{element_id}"]), namespaces)
94+
else
95+
element_id = ref_uri.sub(/^cid:/, '')
96+
element = @decrypted_attachments[element_id]
97+
end
98+
8999
unless supplied_digest(element) == generate_digest(element, digest_algorithm, transform_inclusive_ns)
90100
raise InvalidDigest, "Invalid Digest for #{element_id}"
91101
end
@@ -118,19 +128,14 @@ def generate_digest(element, algorithm, inclusive_namespaces = nil)
118128
end
119129

120130
def supplied_digest(element)
121-
element = document.at_xpath(element, namespaces) if element.is_a? String
122-
find_digest_value element.attributes['Id'].value
131+
element.at_xpath('.//ds:DigestValue', namespaces).text
123132
end
124133

125134
def signature_value
126135
element = document.at_xpath('//wse:Security/ds:Signature/ds:SignatureValue', namespaces)
127136
element ? element.text : ""
128137
end
129138

130-
def find_digest_value(id)
131-
document.at_xpath(%(//wse:Security/ds:Signature/ds:SignedInfo/ds:Reference[@URI="##{id}"]/ds:DigestValue), namespaces).text
132-
end
133-
134139
# Calculate digest for string with given algorithm URL and Base64 encodes it.
135140
def digest(string, algorithm)
136141
Base64.encode64 digester(algorithm).digest(string)

0 commit comments

Comments
 (0)