Skip to content

Commit 07e8524

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

File tree

1 file changed

+23
-14
lines changed

1 file changed

+23
-14
lines changed

lib/akami/wsse/verify_signature.rb

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,17 @@ class VerifySignature
1111

1212
class InvalidDigest < RuntimeError; end
1313
class InvalidSignedValue < RuntimeError; end
14+
class MissingDecryptedAttachment < RuntimeError; end
1415

1516
attr_reader :document
1617

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

2127
# Returns XML namespaces that are used internally for document querying.
@@ -51,14 +57,14 @@ def certificate
5157
# Validates document signature, returns +true+ on success, +false+ otherwise.
5258
def valid?
5359
verify
54-
rescue InvalidDigest, InvalidSignedValue
60+
rescue InvalidDigest, InvalidSignedValue, MissingDecryptedAttachment
5561
return false
5662
end
5763

5864
# Validates document signature and digests and raises if anything mismatches.
5965
def verify!
6066
verify
61-
rescue InvalidDigest, InvalidSignedValue => e
67+
rescue InvalidDigest, InvalidSignedValue, MissingDecryptedAttachment => e
6268
raise InvalidSignature, e.message
6369
end
6470

@@ -78,15 +84,23 @@ def digesters
7884

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

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

87-
element_id = ref.attributes['URI'].value[1..-1] # strip leading '#'
88-
element = document.at_xpath(%(//*[@wsu:Id="#{element_id}"]), namespaces)
89-
unless supplied_digest(element) == generate_digest(element, digest_algorithm, transform_inclusive_ns)
91+
ref_uri = ref.attributes['URI'].value
92+
if ref_uri.start_with?("#")
93+
element_id = ref_uri.sub(/^#/, '')
94+
element = document.at_xpath(%(//*[@wsu:Id="#{element_id}"]), namespaces)
95+
generated_digest = generate_digest(element, digest_algorithm, transform_inclusive_ns)
96+
else
97+
element_id = ref_uri.sub(/^cid:/, '')
98+
element = @decrypted_attachments[element_id]
99+
raise MissingDecryptedAttachment, "Missing decrypted attachment for #{element_id}" if element.nil?
100+
generated_digest = digest(element, digest_algorithm).strip
101+
end
102+
103+
unless supplied_digest(ref) == generated_digest
90104
raise InvalidDigest, "Invalid Digest for #{element_id}"
91105
end
92106
end
@@ -118,19 +132,14 @@ def generate_digest(element, algorithm, inclusive_namespaces = nil)
118132
end
119133

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

125138
def signature_value
126139
element = document.at_xpath('//wse:Security/ds:Signature/ds:SignatureValue', namespaces)
127140
element ? element.text : ""
128141
end
129142

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-
134143
# Calculate digest for string with given algorithm URL and Base64 encodes it.
135144
def digest(string, algorithm)
136145
Base64.encode64 digester(algorithm).digest(string)

0 commit comments

Comments
 (0)