rsaml 0.1.2

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 (97) hide show
  1. data/LICENSE +0 -0
  2. data/README +13 -0
  3. data/Rakefile +136 -0
  4. data/lib/rsaml.rb +57 -0
  5. data/lib/rsaml/action.rb +57 -0
  6. data/lib/rsaml/action_namespace.rb +63 -0
  7. data/lib/rsaml/advice.rb +34 -0
  8. data/lib/rsaml/assertion.rb +192 -0
  9. data/lib/rsaml/attribute.rb +76 -0
  10. data/lib/rsaml/audience.rb +19 -0
  11. data/lib/rsaml/authentication_context.rb +34 -0
  12. data/lib/rsaml/authn_context/README +1 -0
  13. data/lib/rsaml/authn_context/authentication_context_declaration.rb +42 -0
  14. data/lib/rsaml/authn_context/identification.rb +10 -0
  15. data/lib/rsaml/authn_context/physical_verification.rb +24 -0
  16. data/lib/rsaml/condition.rb +13 -0
  17. data/lib/rsaml/conditions.rb +107 -0
  18. data/lib/rsaml/encrypted.rb +12 -0
  19. data/lib/rsaml/errors.rb +16 -0
  20. data/lib/rsaml/evidence.rb +21 -0
  21. data/lib/rsaml/ext/string.rb +5 -0
  22. data/lib/rsaml/identifier.rb +9 -0
  23. data/lib/rsaml/identifier/base.rb +23 -0
  24. data/lib/rsaml/identifier/issuer.rb +28 -0
  25. data/lib/rsaml/identifier/name.rb +55 -0
  26. data/lib/rsaml/parser.rb +23 -0
  27. data/lib/rsaml/protocol.rb +21 -0
  28. data/lib/rsaml/protocol/artifact_resolve.rb +14 -0
  29. data/lib/rsaml/protocol/assertion_id_request.rb +18 -0
  30. data/lib/rsaml/protocol/authn_request.rb +91 -0
  31. data/lib/rsaml/protocol/idp_entry.rb +18 -0
  32. data/lib/rsaml/protocol/idp_list.rb +28 -0
  33. data/lib/rsaml/protocol/message.rb +65 -0
  34. data/lib/rsaml/protocol/name_id_policy.rb +31 -0
  35. data/lib/rsaml/protocol/query.rb +12 -0
  36. data/lib/rsaml/protocol/query/attribute_query.rb +56 -0
  37. data/lib/rsaml/protocol/query/authn_query.rb +30 -0
  38. data/lib/rsaml/protocol/query/authz_decision_query.rb +40 -0
  39. data/lib/rsaml/protocol/query/subject_query.rb +22 -0
  40. data/lib/rsaml/protocol/request.rb +27 -0
  41. data/lib/rsaml/protocol/requested_authn_context.rb +34 -0
  42. data/lib/rsaml/protocol/response.rb +56 -0
  43. data/lib/rsaml/protocol/scoping.rb +33 -0
  44. data/lib/rsaml/protocol/status.rb +38 -0
  45. data/lib/rsaml/protocol/status_code.rb +84 -0
  46. data/lib/rsaml/proxy_restriction.rb +30 -0
  47. data/lib/rsaml/statement.rb +10 -0
  48. data/lib/rsaml/statement/attribute_statement.rb +27 -0
  49. data/lib/rsaml/statement/authentication_statement.rb +57 -0
  50. data/lib/rsaml/statement/authorization_decision_statement.rb +53 -0
  51. data/lib/rsaml/statement/base.rb +9 -0
  52. data/lib/rsaml/subject.rb +37 -0
  53. data/lib/rsaml/subject_confirmation.rb +35 -0
  54. data/lib/rsaml/subject_confirmation_data.rb +55 -0
  55. data/lib/rsaml/subject_locality.rb +27 -0
  56. data/lib/rsaml/validatable.rb +21 -0
  57. data/lib/rsaml/version.rb +9 -0
  58. data/lib/xml_enc.rb +3 -0
  59. data/lib/xml_sig.rb +11 -0
  60. data/lib/xml_sig/canonicalization_method.rb +43 -0
  61. data/lib/xml_sig/key_info.rb +55 -0
  62. data/lib/xml_sig/reference.rb +57 -0
  63. data/lib/xml_sig/signature.rb +29 -0
  64. data/lib/xml_sig/signature_method.rb +20 -0
  65. data/lib/xml_sig/signed_info.rb +27 -0
  66. data/lib/xml_sig/transform.rb +37 -0
  67. data/test/action_namespace_test.rb +93 -0
  68. data/test/action_test.rb +51 -0
  69. data/test/advice_test.rb +25 -0
  70. data/test/assertion_test.rb +192 -0
  71. data/test/attribute_test.rb +60 -0
  72. data/test/authentication_context_test.rb +26 -0
  73. data/test/conditions_test.rb +84 -0
  74. data/test/evidence_test.rb +33 -0
  75. data/test/identifier_test.rb +22 -0
  76. data/test/issuer_test.rb +33 -0
  77. data/test/name_test.rb +33 -0
  78. data/test/parser_test.rb +32 -0
  79. data/test/protocol/assertion_id_request_test.rb +19 -0
  80. data/test/protocol/attribute_query_test.rb +30 -0
  81. data/test/protocol/authn_query_test.rb +20 -0
  82. data/test/protocol/authn_request_test.rb +56 -0
  83. data/test/protocol/authz_decision_query_test.rb +31 -0
  84. data/test/protocol/idp_list_test.rb +15 -0
  85. data/test/protocol/request_test.rb +66 -0
  86. data/test/protocol/response_test.rb +68 -0
  87. data/test/protocol/scoping_test.rb +20 -0
  88. data/test/protocol/status_code_test.rb +34 -0
  89. data/test/protocol/status_test.rb +16 -0
  90. data/test/proxy_restriction_test.rb +20 -0
  91. data/test/rsaml_test.rb +12 -0
  92. data/test/statement_test.rb +101 -0
  93. data/test/subject_locality_test.rb +27 -0
  94. data/test/subject_test.rb +44 -0
  95. data/test/test_helper.rb +16 -0
  96. data/test/xml_sig/canonicalization_test.rb +19 -0
  97. metadata +187 -0
@@ -0,0 +1,57 @@
1
+ module RSAML #:nodoc:
2
+ module Statement #:nodoc:
3
+ # The assertion subject was authenticated by a particular means at a particular time.
4
+ class AuthenticationStatement < Base
5
+ # Specifies the time at which the authentication took place. The time value is encoded in UTC
6
+ attr_accessor :authn_instant
7
+
8
+ # Specifies the index of a particular session between the principal identified by the subject and the
9
+ # authenticating authority. In general, any string value MAY be used as a SessionIndex value.
10
+ # However, when privacy is a consideration, care must be taken to ensure that the SessionIndex
11
+ # value does not invalidate other privacy mechanisms. Accordingly, the value SHOULD NOT be usable
12
+ # to correlate activity by a principal across different session participants.
13
+ attr_accessor :session_index
14
+
15
+ # Specifies a time instant at which the session between the principal identified by the subject and the
16
+ # SAML authority issuing this statement MUST be considered ended. The time value is encoded in
17
+ # UTCSpecifies
18
+ attr_accessor :session_not_on_or_after
19
+
20
+ # Specifies the DNS domain name and IP address for the system from which the assertion subject was
21
+ # apparently authenticated.
22
+ attr_accessor :subject_locality
23
+
24
+ # The authentication context.
25
+ attr_accessor :authn_context
26
+
27
+ # Initialize the statement
28
+ def initialize(authn_context)
29
+ @authn_context = authn_context
30
+ @authn_instant = Time.now.utc
31
+ end
32
+
33
+ # Validate the structure of the authentication statement. Raise a ValidationError if the
34
+ # statement is invalid.
35
+ def validate
36
+ if session_not_on_or_after && !session_not_on_or_after.utc?
37
+ raise ValidationError, "Session not on or after must be UTC"
38
+ end
39
+ raise ValidationError, "Authn context required" unless authn_context
40
+ raise ValidationError, "Authn instant required" unless authn_instant
41
+ raise ValidationError, "Authn instant must be UTC" unless authn_instant.utc?
42
+ end
43
+
44
+ # Construct an XML fragment representing the authentication statement
45
+ def to_xml(xml=Builder::XmlMarkup.new)
46
+ validate
47
+ attributes = {'AuthnInstant' => authn_instant.xmlschema}
48
+ attributes['SessionIndex'] = session_index unless session_index.nil?
49
+ attributes['SessionNotOnOrAfter'] = session_not_on_or_after.xmlschema unless session_not_on_or_after.nil?
50
+ xml.tag!('saml:AuthnStatement', attributes) {
51
+ xml << authn_context.to_xml
52
+ xml << subject_locality.to_xml unless subject_locality.nil?
53
+ }
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,53 @@
1
+ module RSAML #:nodoc:
2
+ module Statement #:nodoc:
3
+ # A request to allow the assertion subject to access the specified resource
4
+ # has been granted or denied.
5
+ class AuthorizationDecisionStatement < Base
6
+ # defines the possible values to be reported as the status of an authorization decision statement.
7
+ #
8
+ # Possible values are:
9
+ # * <tt>Permit</tt>: The specified action is permitted.
10
+ # * <tt>Deny</tt>: The specified action is denied.
11
+ # * <tt>Indeterminate</tt> The SAML authority cannot determine whether the specified action
12
+ # is permitted or denied.
13
+ def self.decision_types
14
+ %w(Permit Deny Indeterminate)
15
+ end
16
+
17
+ # A URI reference identifying the resource to which access authorization is sought.
18
+ # This attribute MAY have the value of the empty URI reference (""), and the meaning
19
+ # is defined to be "the start of the current document"
20
+ attr_accessor :resource
21
+
22
+ # The decision rendered by the SAML authority with respect to the specified resource.
23
+ attr_accessor :decision
24
+
25
+ # The set of actions authorized to be performed on the specified resource.
26
+ def actions
27
+ @actions ||= []
28
+ end
29
+
30
+ # A set of assertions that the SAML authority relied on in making the decision.
31
+ def evidence
32
+ @evidence ||= []
33
+ end
34
+
35
+ # Validate the structure
36
+ def validate
37
+ raise ValidationError, "Resource is required" if resource.nil?
38
+ raise ValidationError, "Decision is required" if decision.nil?
39
+ raise ValidationError, "One or more actions must be specified" if actions.empty?
40
+ actions.each { |action| action.validate }
41
+ end
42
+
43
+ # Construct an XML fragment representing the authorization decision statement
44
+ def to_xml(xml=Builder::XmlMarkup.new)
45
+ attributes = {'Resource' => resource, 'Decision' => decision}
46
+ xml.tag!('saml:AuthzStatement', attributes) {
47
+ actions.each { |action| xml << action.to_xml }
48
+ evidence.each { |e| xml << e.to_xml }
49
+ }
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,9 @@
1
+ module RSAML #:nodoc:
2
+ module Statement #:nodoc:
3
+ # Base class for statements.
4
+ class Base
5
+ # An xsi:type attribute used to indicate the actual statement type.
6
+ attr_accessor :type
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,37 @@
1
+ module RSAML #:nodoc:
2
+ # Specifies the principal that is the subject of all of the (zero or more)
3
+ # statements in an assertion.
4
+ class Subject
5
+
6
+ # The subject identifier
7
+ attr_accessor :identifier
8
+
9
+ # Initialize the subject with the given identifier
10
+ def initialize(identifier=nil)
11
+ @identifier = identifier
12
+ end
13
+
14
+ # Information that allows the subject to be confirmed. If more than one subject confirmation is provided,
15
+ # then satisfying any one of them is sufficient to confirm the subject for the purpose of applying the
16
+ # assertion.
17
+ def subject_confirmations
18
+ @subject_confirmations ||= []
19
+ end
20
+
21
+ # Construct an XML fragment representing the subject
22
+ def to_xml(xml=Builder::XmlMarkup.new)
23
+ xml.tag!('saml:Subject') {
24
+ xml << identifier.to_xml unless identifier.nil?
25
+ xml << subject_confirmations.map { |sc| sc.to_xml }.join
26
+ }
27
+ end
28
+
29
+ # Construct a Subject from an XML Element.
30
+ def self.from_xml(element)
31
+ element = REXML::Document.new(element).root if element.is_a?(String)
32
+ element.get_elements('saml:NameID').each do |identifier|
33
+ return Subject.new(Identifier::Name.from_xml(identifier))
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,35 @@
1
+ module RSAML #:nodoc:
2
+ # Provides the means for a relying party to verify the correspondence of the subject of the
3
+ # assertion with the party with whom the relying party is communicating.
4
+ class SubjectConfirmation
5
+
6
+ # Hash of available SAML methods for subject confirmation
7
+ def self.methods
8
+ {
9
+ :holder_of_key => 'urn:oasis:names:tc:SAML:2.0:cm:holder-of-key',
10
+ :sender_vouches => 'urn:oasis:names:tc:SAML:2.0:cm:sender-vouches',
11
+ :bearer => 'urn:oasis:names:tc:SAML:2.0:cm:bearer'
12
+ }
13
+ end
14
+
15
+ # A URI reference that identifies a protocol or mechanism to be used to confirm the subject.
16
+ attr_accessor :method
17
+
18
+ # Identifies the entity expected to satisfy the enclosing subject confirmation requirements.
19
+ attr_accessor :identifier
20
+
21
+ # Additional confirmation information to be used by a specific confirmation method.
22
+ attr_accessor :subject_confirmation_data
23
+
24
+ def initialize(method)
25
+ @method = method
26
+ end
27
+
28
+ # Construct an XML fragment representing the subject confirmation
29
+ def to_xml(xml=Builder::XmlMarkup.new)
30
+ attributes = {'Method' => method}
31
+ subject_confirmation_data_block = Proc.new { xml << subject_confirmation_data.to_xml} if subject_confirmation_data
32
+ xml.tag!('saml:SubjectConfirmation', attributes, &subject_confirmation_data_block)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,55 @@
1
+ module RSAML #:nodoc:
2
+ # specifies additional data that allows the subject to be confirmed or constrains the circumstances under
3
+ # which the act of subject confirmation can take place. Subject confirmation takes place when a relying
4
+ # party seeks to verify the relationship between an entity presenting the assertion (that is, the attesting
5
+ # entity) and the subject of the assertion's claims.
6
+ class SubjectConfirmationData
7
+ # A time instant before which the subject cannot be confirmed. The time value is encoded in UTC.
8
+ attr_accessor :not_before
9
+
10
+ # A time instant at which the subject can no longer be confirmed. The time value is encoded in UTC.
11
+ attr_accessor :not_on_or_after
12
+
13
+ # A URI specifying the entity or location to which an attesting entity can present the assertion. For
14
+ # example, this attribute might indicate that the assertion must be delivered to a particular network
15
+ # endpoint in order to prevent an intermediary from redirecting it someplace else.
16
+ attr_accessor :recipient
17
+
18
+ # The ID of a SAML protocol message in response to which an attesting entity can present the
19
+ # assertion. For example, this attribute might be used to correlate the assertion to a SAML request that
20
+ # resulted in its presentation.
21
+ attr_accessor :in_response_to
22
+
23
+ # The network address/location from which an attesting entity can present the assertion. For example,
24
+ # this attribute might be used to bind the assertion to particular client addresses to prevent an attacker
25
+ # from easily stealing and presenting the assertion from another location.
26
+ attr_accessor :address
27
+
28
+ # Point for extension attributes
29
+ def attributes
30
+ @attributes = []
31
+ end
32
+
33
+ # Point for extension elements
34
+ def elements
35
+ @elements = []
36
+ end
37
+
38
+ # Confirm the subject confirmation data
39
+ def confirm
40
+ raise ConfirmationError, "Subject confirmation failed: not before" if not_before && Time.now < not_before
41
+ raise ConfirmationError, "Subject confirmation failed: not on or after" if not_on_or_after && Time.now >= not_on_or_after
42
+ # TODO implement tests for remaining elements such as recipient, in_response_to and address
43
+ end
44
+
45
+ def to_xml(xml=Builder::XmlMarkup.new)
46
+ attributes = {}
47
+ attributes['Recipient'] = recipient unless recipient.nil?
48
+ attributes['NotOnOrAfter'] = not_on_or_after.xmlschema unless not_on_or_after.nil?
49
+ attributes['NotBefore'] = not_before.xmlschema unless not_before.nil?
50
+ attributes['InResponseTo'] = in_response_to unless in_response_to.nil?
51
+ attributes[ 'Address'] = address unless address.nil?
52
+ xml.tag!('saml:SubjectConfirmationData', attributes)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,27 @@
1
+ module RSAML #:nodoc:
2
+ # This element is entirely advisory, since both of these fields are quite easily "spoofed,"
3
+ # but may be useful information in some applications.
4
+ class SubjectLocality
5
+ # The network address of the system from which the principal identified by the subject was
6
+ # authenticated.
7
+ attr_accessor :address
8
+
9
+ # The DNS name of the system from which the principal identified by the subject was authenticated.
10
+ attr_accessor :dns_name
11
+
12
+ # Raise an error if the subject locality is structurally invalid.
13
+ def validate
14
+ unless address =~ /^(\d{1,3}\.){3}\d{1,3}$/
15
+ raise ValidationError, "Invalid address"
16
+ end
17
+ end
18
+
19
+ # Construct an XML fragment representing the subject locality
20
+ def to_xml(xml=Builder::XmlMarkup.new)
21
+ attributes = {}
22
+ attributes['Address'] = address unless address.nil?
23
+ attributes['DNSName'] = dns_name unless dns_name.nil?
24
+ xml.tag!('saml:SubjectLocality', attributes)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ module RSAML
2
+ # Module that can be mixed in to any class to provide a :valid? method. This method
3
+ # will look for a :validate method and invoke it, catching any ValidationError exceptions
4
+ # and return either true if the SAML object is structurally valid or false if it isn't.
5
+ module Validatable
6
+ attr_accessor :verbose
7
+ # Return true if the object is valid. Only objects with a validate method will
8
+ # be checked for validity.
9
+ def valid?
10
+ if respond_to?(:validate)
11
+ begin
12
+ validate
13
+ rescue ValidationError => e
14
+ puts "Validation failed: #{e.message}" if verbose
15
+ return false
16
+ end
17
+ end
18
+ return true
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ module RSAML#:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 2
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ # Ruby XML Encryption implementation
2
+ module XmlEnc
3
+ end
@@ -0,0 +1,11 @@
1
+ # Ruby XML Signature implementation
2
+ module XmlSig
3
+ end
4
+
5
+ require 'xml_sig/signature'
6
+ require 'xml_sig/signature_method'
7
+ require 'xml_sig/canonicalization_method'
8
+ require 'xml_sig/reference'
9
+ require 'xml_sig/signed_info'
10
+ require 'xml_sig/key_info'
11
+ require 'xml_sig/transform'
@@ -0,0 +1,43 @@
1
+ require 'iconv'
2
+
3
+ module XmlSig #:nodoc:
4
+ class CanonicalizationMethod
5
+ attr_accessor :algorithm
6
+
7
+ def validate
8
+ raise ValidationError, "Algorithm is required" if algorithm.nil?
9
+ end
10
+
11
+ def to_xml(xml=Builder::XmlMarkup.new)
12
+ attributes = {'Algorightm' => algorithm}
13
+ xml.tag!('ds:CanonicalizationMethod', attributes)
14
+ end
15
+ end
16
+
17
+ class XMLC14NBase
18
+ # Convert the content from the given charset to UTF-8.
19
+ def convert_to_utf8(content, from)
20
+ Iconv.iconv('UTF-8', from, content).join
21
+ end
22
+ def convert_linebreaks(content)
23
+ content.gsub(/\r\n/, "\n").gsub(/\r/, "\n")
24
+ end
25
+ def normalize_attribute_values(content)
26
+
27
+ end
28
+ end
29
+
30
+ # Canonicalization algorithm for XML removing comments
31
+ class XMLC14NWithComments < XMLC14NBase
32
+ def process(content, charset='UTF-8')
33
+ content = convert_to_utf8(content) unless charset == 'UTF-8'
34
+ content
35
+ end
36
+ end
37
+ class XMLC14NWithoutComments < XMLC14NBase
38
+ def process(content, charset='UTF-8')
39
+ content = convert_to_utf8(content) unless charset == 'UTF-8'
40
+ doc = REXML::Document.new(content)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,55 @@
1
+ module XmlSig #:nodoc:
2
+ class KeyInfo
3
+ attr_accessor :id
4
+ attr_accessor :key_name
5
+ attr_accessor :key_value
6
+ attr_accessor :retrieval_method
7
+ attr_accessor :key_data
8
+
9
+ def to_xml(xml=Builder::XmlMarkup.new)
10
+ xml.tag!('ds:KeyInfo') {
11
+ xml.tag!('ds:KeyName', key_name)
12
+ xml.tag!('ds:KeyValue') {
13
+ xml << key_value.to_xml
14
+ }
15
+ }
16
+ end
17
+ end
18
+
19
+ class RetrievalMethod
20
+ attr_accessor :uri
21
+ attr_accessor :type
22
+
23
+ def transforms
24
+ @transforms ||= []
25
+ end
26
+
27
+ def validate
28
+ raise ValidationError, "URI is required" if uri.nil?
29
+ end
30
+
31
+ def to_xml(xml=Builder::XmlMarkup.new)
32
+ attributes = {'URI' => uri}
33
+ attributes['Type'] = type unless type.nil?
34
+ xml.tag!('ds:RetrievalMethod', attributes) {
35
+ transforms.each { |transform| xml << transform.to_xml }
36
+ }
37
+ end
38
+ end
39
+
40
+ # REQUIRED
41
+ class DSAKeyValue
42
+ end
43
+ # OPTIONAL
44
+ class RSAKeyValue
45
+ end
46
+
47
+ class X509Data
48
+ end
49
+ class PGPData
50
+ end
51
+ class SPKIData
52
+ end
53
+ class MgmtData
54
+ end
55
+ end
@@ -0,0 +1,57 @@
1
+ module XmlSig #:nooc:
2
+ class Reference
3
+ attr_accessor :id
4
+ attr_accessor :uri
5
+ attr_accessor :type
6
+
7
+ attr_accessor :digest_method
8
+
9
+ # DigestValue is an element that contains the encoded value of the digest. The
10
+ # digest is always encoded using base64
11
+ attr_accessor :digest_value
12
+
13
+ def transforms
14
+ @transforms ||= []
15
+ end
16
+
17
+ def validate
18
+ raise ValidationError, "Digest method is required" if digest_method.nil?
19
+ raise ValidationError, "Digest value is required" if digest_value.nil?
20
+ end
21
+
22
+ def to_xml(xml=Builder::XmlMarkup.new)
23
+ attributes = {}
24
+ attributes['Id'] = id unless id.nil?
25
+ attributes['URI'] = uri unless uri.nil?
26
+ attributes['Type'] = type unless type.nil?
27
+ xml.tag!('ds:Reference', attributes) {
28
+ xml.tag!('ds:Transforms') {
29
+ transforms.each { |transform| xml << transform.to_xml }
30
+ }
31
+ xml << digest_method.to_xml unless digest_method.nil?
32
+ xml.tag!('ds:DigestValue', digest_value)
33
+ }
34
+ end
35
+ end
36
+
37
+ # DigestMethod is a required element that identifies the digest algorithm to be applied
38
+ # to the signed object.
39
+ class DigestMethod
40
+ def self.identifiers
41
+ @identifiers ||= {
42
+ 'SHA-1', 'http://www.w3.org/2000/09/xmldsig#sha1'
43
+ }
44
+ end
45
+
46
+ attr_accessor :algorithm
47
+
48
+ def validate
49
+ raise ValidationError, "Algorithm is required" if algorithm.nil?
50
+ end
51
+
52
+ def to_xml(xml=Builder::XmlMarkup.new)
53
+ attributes = {'Algorightm' => algorithm}
54
+ xml.tag!('ds:DigestMethod', attributes)
55
+ end
56
+ end
57
+ end