lyrebird 2.1.0 → 2.1.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 763dc798f8c44d5fdf3b9782961fe0ea042ee6a593816a5fb9ac6f6359b284ef
4
- data.tar.gz: fc30ca3c936e76b4b29fb55e0dec4528bd17c156c3bf95c6ba76e3b16b3c0b94
3
+ metadata.gz: 54582bda5673179b49a58b405ee52668486162eb785d728cfd98b6dcaed7f1cf
4
+ data.tar.gz: f6e2048c4adb1a6b3690f2517c781cd431f9cc0271217df6effb8944e808fee0
5
5
  SHA512:
6
- metadata.gz: 97b8ffd5d37a09be4cea80d33e51de9ac12aee670aa281907740c12d65c91af624649522a8511e4dbb3a15e237f422b0016dc22b00c8435abad460ce17279316
7
- data.tar.gz: 44913f9701aaf25446db2ebfd81d2efd7ec887a3be13085bf48444bb7b7eee5724296840e7e3e8e7d67e489b9ef12e6f21882309e5fcd607d0320f3e349ae00c
6
+ metadata.gz: 5998bda9158b2d8d3f64f6260fcfb9b3b06c62469c923e86776bac8ab541f955945621f799b23ab69cc4388e561d403ab15d8adc763b018f5fad9411409668c8
7
+ data.tar.gz: 9aebec46c7f33d74c5274073dea1a93a347fb363b603bbf3cd8a8dc7ba26f0fbe2c9e5f34ba6fc4afcb67bfe850e8c963d485a2adca3539bd3137173d4675bba
@@ -28,112 +28,80 @@ module Lyrebird
28
28
  end
29
29
 
30
30
  def document
31
- @document ||= Nokogiri::XML::Document.new.tap do |doc|
32
- doc.root = root(doc)
33
- end
31
+ @document ||= Nokogiri::XML::Builder.new { |xml| root(xml) }.doc
34
32
  end
35
33
 
36
34
  private
37
35
 
38
- def root(doc)
39
- doc.create_element("Assertion").tap do |a|
40
- ns = a.add_namespace_definition("saml", SAML_ASSERTION_NS)
41
-
42
- a.namespace = ns
43
- a["ID"] = ID.generate
44
- a["Version"] = "2.0"
45
- a["IssueInstant"] = @issue_instant.iso8601
46
-
47
- a.add_child(doc.create_element("Issuer")).tap do |i|
48
- i.namespace = ns
49
- i.content = @issuer
50
- end
51
-
52
- a.add_child(subject(doc, ns))
53
- a.add_child(conditions(doc, ns))
54
- a.add_child(authn_statement(doc, ns))
55
- a.add_child(attribute_statement(doc, ns)) if @attributes.any?
36
+ def root(xml)
37
+ attrs = {
38
+ "xmlns:saml" => SAML_ASSERTION_NS,
39
+ ID: ID.generate,
40
+ Version: "2.0",
41
+ IssueInstant: @issue_instant.iso8601
42
+ }
43
+
44
+ xml["saml"].Assertion(attrs) do
45
+ xml["saml"].Issuer(@issuer.to_s)
46
+ subject(xml)
47
+ conditions(xml)
48
+ authn_statement(xml)
49
+ attribute_statement(xml) if @attributes.any?
56
50
  end
57
51
  end
58
52
 
59
- def subject(doc, ns)
60
- doc.create_element("Subject").tap do |s|
61
- s.namespace = ns
62
-
63
- s.add_child(doc.create_element("NameID")).tap do |nid|
64
- nid.namespace = ns
65
- nid["Format"] = @name_id_format
66
- nid.content = @name_id
67
- end
68
-
69
- s.add_child(subject_confirmation(doc, ns))
53
+ def subject(xml)
54
+ xml["saml"].Subject do
55
+ xml["saml"].NameID(@name_id.to_s, Format: @name_id_format)
56
+ subject_confirmation(xml)
70
57
  end
71
58
  end
72
59
 
73
- def subject_confirmation(doc, ns)
74
- doc.create_element("SubjectConfirmation").tap do |sc|
75
- sc.namespace = ns
76
- sc["Method"] = CM_BEARER
60
+ def subject_confirmation(xml)
61
+ attrs = {
62
+ NotOnOrAfter: @not_on_or_after.iso8601,
63
+ Recipient: @recipient,
64
+ InResponseTo: @in_response_to
65
+ }.compact
77
66
 
78
- sc.add_child(doc.create_element("SubjectConfirmationData")).tap do |d|
79
- d.namespace = ns
80
- d["NotOnOrAfter"] = @not_on_or_after.iso8601
81
- d["Recipient"] = @recipient
82
- d["InResponseTo"] = @in_response_to if @in_response_to
83
- end
67
+ xml["saml"].SubjectConfirmation(Method: CM_BEARER) do
68
+ xml["saml"].SubjectConfirmationData(attrs)
84
69
  end
85
70
  end
86
71
 
87
- def conditions(doc, ns)
88
- doc.create_element("Conditions").tap do |c|
89
- c.namespace = ns
90
- c["NotBefore"] = @not_before.iso8601
91
- c["NotOnOrAfter"] = @not_on_or_after.iso8601
72
+ def conditions(xml)
73
+ attrs = {
74
+ NotBefore: @not_before.iso8601,
75
+ NotOnOrAfter: @not_on_or_after.iso8601
76
+ }
92
77
 
93
- c.add_child(doc.create_element("AudienceRestriction")).tap do |ar|
94
- ar.namespace = ns
95
- ar.add_child(doc.create_element("Audience")).tap do |a|
96
- a.namespace = ns
97
- a.content = @audience
98
- end
78
+ xml["saml"].Conditions(attrs) do
79
+ xml["saml"].AudienceRestriction do
80
+ xml["saml"].Audience(@audience.to_s)
99
81
  end
100
82
  end
101
83
  end
102
84
 
103
- def authn_statement(doc, ns)
104
- doc.create_element("AuthnStatement").tap do |as|
105
- as.namespace = ns
106
- as["AuthnInstant"] = @issue_instant.iso8601
107
- as["SessionIndex"] = ID.generate
85
+ def authn_statement(xml)
86
+ attrs = {
87
+ AuthnInstant: @issue_instant.iso8601,
88
+ SessionIndex: ID.generate
89
+ }
108
90
 
109
- as.add_child(doc.create_element("AuthnContext")).tap do |ac|
110
- ac.namespace = ns
111
- ac.add_child(doc.create_element("AuthnContextClassRef")).tap do |cr|
112
- cr.namespace = ns
113
- cr.content = @authn_context
114
- end
91
+ xml["saml"].AuthnStatement(attrs) do
92
+ xml["saml"].AuthnContext do
93
+ xml["saml"].AuthnContextClassRef(@authn_context.to_s)
115
94
  end
116
95
  end
117
96
  end
118
97
 
119
- def attribute_statement(doc, ns)
120
- doc.create_element("AttributeStatement").tap do |as|
121
- as.namespace = ns
122
-
98
+ def attribute_statement(xml)
99
+ xml["saml"].AttributeStatement do
123
100
  @attributes.each do |name, values|
124
101
  list = values.is_a?(Enumerable) ? values.to_a : [values].compact
125
102
 
126
- as.add_child(doc.create_element("Attribute")).tap do |a|
127
- a.namespace = ns
128
- a["Name"] = name
129
- a["NameFormat"] = ATTR_NAME_FORMAT
130
-
131
- list.each do |value|
132
- a.add_child(doc.create_element("AttributeValue")).tap do |av|
133
- av.namespace = ns
134
- av.content = value
135
- end
136
- end
103
+ xml["saml"].Attribute(Name: name, NameFormat: ATTR_NAME_FORMAT) do
104
+ list.each { |value| xml["saml"].AttributeValue(value.to_s) }
137
105
  end
138
106
  end
139
107
  end
@@ -9,6 +9,9 @@ module Lyrebird
9
9
  "keyUsage" => "digitalSignature,keyEncipherment"
10
10
  }.freeze
11
11
 
12
+ private_constant :LOCK
13
+ private_constant :EXTENSIONS
14
+
12
15
  attr_reader :key, :x509
13
16
 
14
17
  def self.default
@@ -3,14 +3,25 @@
3
3
  module Lyrebird
4
4
  SAML_ASSERTION_NS = "urn:oasis:names:tc:SAML:2.0:assertion"
5
5
  SAML_PROTOCOL_NS = "urn:oasis:names:tc:SAML:2.0:protocol"
6
+
7
+ NAMEID_EMAIL = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
8
+ NAMEID_PERSISTENT = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
9
+ NAMEID_TRANSIENT = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
10
+ NAMEID_UNSPECIFIED = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
11
+
12
+ ppt = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
13
+ AUTHN_PASSWORD_PROTECTED_TRANSPORT = ppt
14
+
15
+ CM_BEARER = "urn:oasis:names:tc:SAML:2.0:cm:bearer"
16
+ ATTR_NAME_FORMAT = "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"
17
+ STATUS_SUCCESS = "urn:oasis:names:tc:SAML:2.0:status:Success"
18
+
6
19
  XMLDSIG_NS = "http://www.w3.org/2000/09/xmldsig#"
7
20
  ENVELOPED_SIG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
8
21
  EXC_C14N = "http://www.w3.org/2001/10/xml-exc-c14n#"
9
22
  SHA256_DIGEST = "http://www.w3.org/2001/04/xmlenc#sha256"
10
23
  RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
11
- CM_BEARER = "urn:oasis:names:tc:SAML:2.0:cm:bearer"
12
- ATTR_NAME_FORMAT = "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"
13
- STATUS_SUCCESS = "urn:oasis:names:tc:SAML:2.0:status:Success"
24
+
14
25
  XMLENC_NS = "http://www.w3.org/2001/04/xmlenc#"
15
26
  AES256_CBC = "http://www.w3.org/2001/04/xmlenc#aes256-cbc"
16
27
  RSA_OAEP = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"
@@ -1,11 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lyrebird
4
- NAMEID_EMAIL = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
5
- NAMEID_PERSISTENT = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
6
- NAMEID_TRANSIENT = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
7
- NAMEID_UNSPECIFIED = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
8
-
9
4
  class Defaults
10
5
  attr_accessor :issuer
11
6
  attr_accessor :name_id
@@ -26,13 +21,11 @@ module Lyrebird
26
21
  @valid_for = 300 # 5 minutes
27
22
  @audience = "https://sp.example.com"
28
23
 
29
- @authn_context =
30
- "urn:oasis:names:tc:SAML:2.0:ac:classes:" \
31
- "PasswordProtectedTransport"
24
+ @authn_context = AUTHN_PASSWORD_PROTECTED_TRANSPORT
32
25
 
33
26
  @attributes = {
34
27
  first_name: "Test",
35
- last_name: "User",
28
+ last_name: "User"
36
29
  }.freeze
37
30
  end
38
31
  end
@@ -5,92 +5,60 @@ module Lyrebird
5
5
  def initialize(element, certificate)
6
6
  @element = element
7
7
  @certificate = certificate
8
- @doc = element.document
9
8
  @aes_key = SecureRandom.random_bytes(32)
10
9
  end
11
10
 
12
11
  def encrypt
13
- build_encrypted_assertion
12
+ Nokogiri::XML::Builder.new do |xml|
13
+ xml["saml"].EncryptedAssertion("xmlns:saml" => SAML_ASSERTION_NS) do
14
+ encrypted_data(xml)
15
+ end
16
+ end.doc.root
14
17
  end
15
18
 
16
19
  private
17
20
 
18
- def build_encrypted_assertion
19
- @doc.create_element("EncryptedAssertion").tap do |ea|
20
- @saml = ea.add_namespace_definition("saml", SAML_ASSERTION_NS)
21
- ea.namespace = @saml
22
- ea.add_child(build_encrypted_data)
23
- end
24
- end
21
+ def encrypted_data(xml)
22
+ attrs = {
23
+ "xmlns:xenc" => XMLENC_NS,
24
+ Type: "#{XMLENC_NS}Element"
25
+ }
25
26
 
26
- def build_encrypted_data
27
- @doc.create_element("EncryptedData").tap do |ed|
28
- @xenc = ed.add_namespace_definition("xenc", XMLENC_NS)
29
- ed.namespace = @xenc
30
- ed["Type"] = "#{XMLENC_NS}Element"
31
-
32
- ed.add_child(@doc.create_element("EncryptionMethod")).tap do |em|
33
- em.namespace = @xenc
34
- em["Algorithm"] = AES256_CBC
35
- end
36
-
37
- ed.add_child(build_key_info)
38
- ed.add_child(build_cipher_data)
27
+ xml["xenc"].EncryptedData(attrs) do
28
+ xml["xenc"].EncryptionMethod(Algorithm: AES256_CBC)
29
+ key_info(xml)
30
+ cipher_data(xml, ciphertext)
39
31
  end
40
32
  end
41
33
 
42
- def build_key_info
43
- @doc.create_element("KeyInfo").tap do |ki|
44
- @ds = ki.add_namespace_definition("ds", XMLDSIG_NS)
45
- ki.namespace = @ds
46
- ki.add_child(build_encrypted_key)
34
+ def key_info(xml)
35
+ xml["ds"].KeyInfo("xmlns:ds" => XMLDSIG_NS) do
36
+ xml["xenc"].EncryptedKey do
37
+ xml["xenc"].EncryptionMethod(Algorithm: RSA_OAEP)
38
+ cipher_data(xml, wrapped_key)
39
+ end
47
40
  end
48
41
  end
49
42
 
50
- def build_encrypted_key
51
- @doc.create_element("EncryptedKey").tap do |ek|
52
- ek.namespace = @xenc
53
-
54
- ek.add_child(@doc.create_element("EncryptionMethod")).tap do |em|
55
- em.namespace = @xenc
56
- em["Algorithm"] = RSA_OAEP
57
- end
58
-
59
- ek.add_child(build_encrypted_key_cipher_data)
43
+ def cipher_data(xml, value)
44
+ xml["xenc"].CipherData do
45
+ xml["xenc"].CipherValue(value)
60
46
  end
61
47
  end
62
48
 
63
- def build_encrypted_key_cipher_data
49
+ def wrapped_key
64
50
  public_key = @certificate.x509.public_key
65
51
  padding = OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING
66
- encrypted_aes_key = public_key.public_encrypt(@aes_key, padding)
67
-
68
- @doc.create_element("CipherData").tap do |cd|
69
- cd.namespace = @xenc
70
-
71
- cd.add_child(@doc.create_element("CipherValue")).tap do |cv|
72
- cv.namespace = @xenc
73
- cv.content = [encrypted_aes_key].pack("m0")
74
- end
75
- end
52
+ [public_key.public_encrypt(@aes_key, padding)].pack("m0")
76
53
  end
77
54
 
78
- def build_cipher_data
55
+ def ciphertext
79
56
  cipher = OpenSSL::Cipher.new("AES-256-CBC")
80
57
  cipher.encrypt
81
58
  cipher.key = @aes_key
82
59
  iv = cipher.random_iv
83
60
  plaintext = @element.to_xml(save_with: 0)
84
- ciphertext = cipher.update(plaintext) + cipher.final
85
-
86
- @doc.create_element("CipherData").tap do |cd|
87
- cd.namespace = @xenc
88
-
89
- cd.add_child(@doc.create_element("CipherValue")).tap do |cv|
90
- cv.namespace = @xenc
91
- cv.content = [iv + ciphertext].pack("m0")
92
- end
93
- end
61
+ [iv + cipher.update(plaintext) + cipher.final].pack("m0")
94
62
  end
95
63
  end
96
64
  end
@@ -48,67 +48,42 @@ module Lyrebird
48
48
  end
49
49
 
50
50
  def document
51
- @document ||= Nokogiri::XML::Document.new.tap do |doc|
52
- doc.root = build_response(doc)
53
- sign_assertion(doc) if @sign_with && !@encrypt_with
54
- sign_response(doc) if @sign_with
51
+ @document ||= build_response.doc.tap do |doc|
52
+ Signature.new(doc.root, @sign_with).sign! if @sign_with
55
53
  end
56
54
  end
57
55
 
58
56
  private
59
57
 
60
- def build_response(doc)
61
- doc.create_element("Response").tap do |r|
62
- @samlp = r.add_namespace_definition("samlp", SAML_PROTOCOL_NS)
63
- @saml = r.add_namespace_definition("saml", SAML_ASSERTION_NS)
64
-
65
- r.namespace = @samlp
66
- r["ID"] = ID.generate
67
- r["Version"] = "2.0"
68
- r["IssueInstant"] = Time.now.utc.iso8601
69
- r["Destination"] = @destination if @destination
70
- r["InResponseTo"] = @in_response_to if @in_response_to
71
-
72
- r.add_child(doc.create_element("Issuer")).tap do |i|
73
- i.namespace = @saml
74
- i.content = @issuer
58
+ def build_response
59
+ attrs = {
60
+ "xmlns:samlp" => SAML_PROTOCOL_NS,
61
+ "xmlns:saml" => SAML_ASSERTION_NS,
62
+ ID: ID.generate,
63
+ Version: "2.0",
64
+ IssueInstant: Time.now.utc.iso8601,
65
+ Destination: @destination,
66
+ InResponseTo: @in_response_to
67
+ }.compact
68
+
69
+ Nokogiri::XML::Builder.new do |xml|
70
+ xml["samlp"].Response(attrs) do
71
+ xml["saml"].Issuer(@issuer.to_s)
72
+
73
+ xml["samlp"].Status do
74
+ xml["samlp"].StatusCode(Value: STATUS_SUCCESS)
75
+ end
76
+
77
+ xml.parent << build_assertion_element
75
78
  end
76
-
77
- r.add_child(build_status(doc))
78
- r.add_child(build_assertion_element)
79
79
  end
80
80
  end
81
81
 
82
82
  def build_assertion_element
83
83
  element = @assertion.document.root
84
-
85
- if @encrypt_with
86
- Signature.new(element, @sign_with).sign! if @sign_with
87
- Encryption.new(element, @encrypt_with).encrypt
88
- else
89
- element
90
- end
91
- end
92
-
93
- def sign_assertion(doc)
94
- ns = { "saml" => SAML_ASSERTION_NS }
95
- assertion = doc.at_xpath("//saml:Assertion", ns)
96
- Signature.new(assertion, @sign_with).sign!
97
- end
98
-
99
- def sign_response(doc)
100
- Signature.new(doc.root, @sign_with).sign!
101
- end
102
-
103
- def build_status(doc)
104
- doc.create_element("Status").tap do |s|
105
- s.namespace = @samlp
106
-
107
- s.add_child(doc.create_element("StatusCode")).tap do |sc|
108
- sc.namespace = @samlp
109
- sc["Value"] = STATUS_SUCCESS
110
- end
111
- end
84
+ Signature.new(element, @sign_with).sign! if @sign_with
85
+ element = Encryption.new(element, @encrypt_with).encrypt if @encrypt_with
86
+ element
112
87
  end
113
88
  end
114
89
  end
@@ -4,112 +4,59 @@ module Lyrebird
4
4
  class Signature
5
5
  C14N_EXCLUSIVE = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
6
6
 
7
+ private_constant :C14N_EXCLUSIVE
8
+
7
9
  def initialize(element, certificate)
8
10
  @element = element
9
11
  @certificate = certificate
10
- @doc = element.document
11
12
  end
12
13
 
13
14
  def sign!
14
15
  ns = { "saml" => SAML_ASSERTION_NS, "ds" => XMLDSIG_NS }
15
16
  @element.at_xpath("ds:Signature", ns)&.remove
17
+ signature = build_signature
16
18
  issuer = @element.at_xpath("saml:Issuer", ns)
17
- issuer.add_next_sibling(build_signature)
18
- populate_signature_value
19
+ issuer.add_next_sibling(signature)
20
+ populate_signature_value(signature)
19
21
  self
20
22
  end
21
23
 
22
24
  private
23
25
 
24
26
  def build_signature
25
- @doc.create_element("Signature").tap do |sig|
26
- @ds = sig.add_namespace_definition("ds", XMLDSIG_NS)
27
- sig.namespace = @ds
28
-
29
- sig.add_child(build_signed_info)
30
- sig.add_child(build_signature_value)
31
- sig.add_child(build_key_info)
32
- end
33
- end
34
-
35
- def build_signed_info
36
- @doc.create_element("SignedInfo").tap do |si|
37
- @signed_info = si
38
- si.namespace = @ds
39
-
40
- si.add_child(@doc.create_element("CanonicalizationMethod")).tap do |cm|
41
- cm.namespace = @ds
42
- cm["Algorithm"] = EXC_C14N
43
- end
44
-
45
- si.add_child(@doc.create_element("SignatureMethod")).tap do |sm|
46
- sm.namespace = @ds
47
- sm["Algorithm"] = RSA_SHA256
27
+ Nokogiri::XML::Builder.new do |xml|
28
+ xml["ds"].Signature("xmlns:ds" => XMLDSIG_NS) do
29
+ signed_info(xml)
30
+ xml["ds"].SignatureValue
31
+ key_info(xml)
48
32
  end
49
-
50
- si.add_child(build_reference)
51
- end
52
- end
53
-
54
- def build_signature_value
55
- @doc.create_element("SignatureValue").tap do |sv|
56
- sv.namespace = @ds
57
- @signature_value = sv
58
- end
59
- end
60
-
61
- def populate_signature_value
62
- canonical = @signed_info.canonicalize(C14N_EXCLUSIVE)
63
- sig = @certificate.key.sign(OpenSSL::Digest.new("SHA256"), canonical)
64
- @signature_value.content = [sig].pack("m0")
33
+ end.doc.root
65
34
  end
66
35
 
67
- def build_key_info
68
- @doc.create_element("KeyInfo").tap do |ki|
69
- ki.namespace = @ds
70
-
71
- ki.add_child(@doc.create_element("X509Data")).tap do |xd|
72
- xd.namespace = @ds
73
-
74
- xd.add_child(@doc.create_element("X509Certificate")).tap do |xc|
75
- xc.namespace = @ds
76
- xc.content = @certificate.base64
77
- end
78
- end
36
+ def signed_info(xml)
37
+ xml["ds"].SignedInfo do
38
+ xml["ds"].CanonicalizationMethod(Algorithm: EXC_C14N)
39
+ xml["ds"].SignatureMethod(Algorithm: RSA_SHA256)
40
+ reference(xml)
79
41
  end
80
42
  end
81
43
 
82
- def build_reference
83
- @doc.create_element("Reference").tap do |ref|
84
- ref.namespace = @ds
85
- ref["URI"] = "##{@element["ID"]}"
86
-
87
- ref.add_child(build_transforms)
88
-
89
- ref.add_child(@doc.create_element("DigestMethod")).tap do |dm|
90
- dm.namespace = @ds
91
- dm["Algorithm"] = SHA256_DIGEST
44
+ def reference(xml)
45
+ xml["ds"].Reference(URI: "##{@element["ID"]}") do
46
+ xml["ds"].Transforms do
47
+ xml["ds"].Transform(Algorithm: ENVELOPED_SIG)
48
+ xml["ds"].Transform(Algorithm: EXC_C14N)
92
49
  end
93
50
 
94
- ref.add_child(@doc.create_element("DigestValue")).tap do |dv|
95
- dv.namespace = @ds
96
- dv.content = compute_digest
97
- end
51
+ xml["ds"].DigestMethod(Algorithm: SHA256_DIGEST)
52
+ xml["ds"].DigestValue(compute_digest)
98
53
  end
99
54
  end
100
55
 
101
- def build_transforms
102
- @doc.create_element("Transforms").tap do |t|
103
- t.namespace = @ds
104
-
105
- t.add_child(@doc.create_element("Transform")).tap do |tr|
106
- tr.namespace = @ds
107
- tr["Algorithm"] = ENVELOPED_SIG
108
- end
109
-
110
- t.add_child(@doc.create_element("Transform")).tap do |tr|
111
- tr.namespace = @ds
112
- tr["Algorithm"] = EXC_C14N
56
+ def key_info(xml)
57
+ xml["ds"].KeyInfo do
58
+ xml["ds"].X509Data do
59
+ xml["ds"].X509Certificate(@certificate.base64)
113
60
  end
114
61
  end
115
62
  end
@@ -119,5 +66,14 @@ module Lyrebird
119
66
  digest = OpenSSL::Digest.new("SHA256").digest(canonical)
120
67
  [digest].pack("m0")
121
68
  end
69
+
70
+ def populate_signature_value(signature)
71
+ ns = { "ds" => XMLDSIG_NS }
72
+ signed_info = signature.at_xpath("ds:SignedInfo", ns)
73
+ canonical = signed_info.canonicalize(C14N_EXCLUSIVE)
74
+ sig = @certificate.key.sign(OpenSSL::Digest.new("SHA256"), canonical)
75
+ value = signature.at_xpath("ds:SignatureValue", ns)
76
+ value.content = [sig].pack("m0")
77
+ end
122
78
  end
123
79
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lyrebird
4
- VERSION = "2.1.0"
4
+ VERSION = "2.1.1"
5
5
  end
data/lib/lyrebird.rb CHANGED
@@ -7,10 +7,10 @@ require "time"
7
7
 
8
8
  require_relative "lyrebird/assertion"
9
9
  require_relative "lyrebird/certificate"
10
+ require_relative "lyrebird/constants"
10
11
  require_relative "lyrebird/defaults"
11
12
  require_relative "lyrebird/encryption"
12
13
  require_relative "lyrebird/id"
13
- require_relative "lyrebird/namespaces"
14
14
  require_relative "lyrebird/options"
15
15
  require_relative "lyrebird/response"
16
16
  require_relative "lyrebird/signature"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lyrebird
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-08-28 00:00:00.000000000 Z
11
+ date: 2026-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -24,76 +24,6 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.14'
27
- - !ruby/object:Gem::Dependency
28
- name: base64
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: logger
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: minitest
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: rake
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: ruby-saml
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
27
  description:
98
28
  email:
99
29
  executables: []
@@ -105,10 +35,10 @@ files:
105
35
  - lib/lyrebird.rb
106
36
  - lib/lyrebird/assertion.rb
107
37
  - lib/lyrebird/certificate.rb
38
+ - lib/lyrebird/constants.rb
108
39
  - lib/lyrebird/defaults.rb
109
40
  - lib/lyrebird/encryption.rb
110
41
  - lib/lyrebird/id.rb
111
- - lib/lyrebird/namespaces.rb
112
42
  - lib/lyrebird/options.rb
113
43
  - lib/lyrebird/response.rb
114
44
  - lib/lyrebird/signature.rb