moxml 0.1.25 → 0.2.0
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 +4 -4
- data/.github/workflows/release.yml +9 -0
- data/.github/workflows/windows.yml +62 -0
- data/Gemfile +3 -1
- data/README.adoc +133 -6
- data/docs/signature/algorithms.md +108 -0
- data/docs/signature/architecture.md +148 -0
- data/docs/signature/c14n.md +110 -0
- data/docs/signature/examples.md +109 -0
- data/docs/signature/flows.md +132 -0
- data/docs/signature/quick-reference.md +115 -0
- data/docs/signature/security.md +143 -0
- data/examples/signature/auto_key_extraction.rb +24 -0
- data/examples/signature/enveloped_rsa.rb +45 -0
- data/examples/signature/hmac.rb +33 -0
- data/lib/compat/opal/moxml_boot.rb +5 -0
- data/lib/moxml/adapter/base.rb +53 -49
- data/lib/moxml/adapter/customized_leptris/declaration.rb +32 -0
- data/lib/moxml/adapter/customized_leptris/doctype.rb +31 -0
- data/lib/moxml/adapter/customized_leptris/entity_reference.rb +12 -0
- data/lib/moxml/adapter/customized_leptris/text_segment.rb +26 -0
- data/lib/moxml/adapter/customized_leptris.rb +16 -0
- data/lib/moxml/adapter/customized_libxml/cdata.rb +3 -12
- data/lib/moxml/adapter/customized_libxml/comment.rb +2 -9
- data/lib/moxml/adapter/customized_libxml/declaration.rb +1 -10
- data/lib/moxml/adapter/customized_libxml/node.rb +8 -0
- data/lib/moxml/adapter/customized_libxml/processing_instruction.rb +2 -10
- data/lib/moxml/adapter/customized_libxml/text.rb +2 -9
- data/lib/moxml/adapter/customized_oga/entity_decoder.rb +37 -0
- data/lib/moxml/adapter/customized_oga/raw_value_override.rb +52 -0
- data/lib/moxml/adapter/customized_oga/xml_generator.rb +4 -34
- data/lib/moxml/adapter/customized_oga.rb +2 -0
- data/lib/moxml/adapter/customized_ox/entity_reference.rb +1 -17
- data/lib/moxml/adapter/customized_rexml/entity_reference.rb +1 -11
- data/lib/moxml/adapter/headed_ox.rb +9 -128
- data/lib/moxml/adapter/leptris.rb +965 -0
- data/lib/moxml/adapter/libxml.rb +131 -99
- data/lib/moxml/adapter/nokogiri.rb +29 -8
- data/lib/moxml/adapter/oga.rb +91 -54
- data/lib/moxml/adapter/ox.rb +93 -137
- data/lib/moxml/adapter/rexml.rb +61 -50
- data/lib/moxml/adapter.rb +2 -1
- data/lib/moxml/attribute.rb +8 -4
- data/lib/moxml/attribute_resolver.rb +189 -0
- data/lib/moxml/c14n/attribute_handler.rb +71 -0
- data/lib/moxml/c14n/character_encoder.rb +29 -0
- data/lib/moxml/c14n/data_model.rb +145 -0
- data/lib/moxml/c14n/exclusive.rb +188 -0
- data/lib/moxml/c14n/inclusive_10.rb +42 -0
- data/lib/moxml/c14n/inclusive_11.rb +21 -0
- data/lib/moxml/c14n/namespace_context.rb +68 -0
- data/lib/moxml/c14n/namespace_handler.rb +71 -0
- data/lib/moxml/c14n/node.rb +42 -0
- data/lib/moxml/c14n/nodes/attribute_node.rb +50 -0
- data/lib/moxml/c14n/nodes/comment_node.rb +29 -0
- data/lib/moxml/c14n/nodes/element_node.rb +62 -0
- data/lib/moxml/c14n/nodes/namespace_node.rb +41 -0
- data/lib/moxml/c14n/nodes/processing_instruction_node.rb +30 -0
- data/lib/moxml/c14n/nodes/root_node.rb +22 -0
- data/lib/moxml/c14n/nodes/text_node.rb +29 -0
- data/lib/moxml/c14n/nodes.rb +17 -0
- data/lib/moxml/c14n/processor.rb +118 -0
- data/lib/moxml/c14n/writer.rb +121 -0
- data/lib/moxml/c14n/xml_base_handler.rb +140 -0
- data/lib/moxml/c14n.rb +96 -0
- data/lib/moxml/config.rb +29 -3
- data/lib/moxml/context.rb +12 -0
- data/lib/moxml/element.rb +29 -12
- data/lib/moxml/entity/reference.rb +28 -0
- data/lib/moxml/entity.rb +125 -0
- data/lib/moxml/node.rb +90 -1
- data/lib/moxml/sax/namespace_splitter.rb +5 -2
- data/lib/moxml/signature/algorithms/base64_transform.rb +40 -0
- data/lib/moxml/signature/algorithms/canonicalization_base.rb +87 -0
- data/lib/moxml/signature/algorithms/digest_base.rb +51 -0
- data/lib/moxml/signature/algorithms/dsa_sha.rb +107 -0
- data/lib/moxml/signature/algorithms/ecdsa_sha.rb +125 -0
- data/lib/moxml/signature/algorithms/enveloped_signature_transform.rb +79 -0
- data/lib/moxml/signature/algorithms/exc_c14n_10.rb +23 -0
- data/lib/moxml/signature/algorithms/hmac_sha.rb +117 -0
- data/lib/moxml/signature/algorithms/inclusive_c14n_10.rb +21 -0
- data/lib/moxml/signature/algorithms/inclusive_c14n_11.rb +23 -0
- data/lib/moxml/signature/algorithms/rsa_pkcs1_sha.rb +56 -0
- data/lib/moxml/signature/algorithms/sha1.rb +13 -0
- data/lib/moxml/signature/algorithms/sha224.rb +13 -0
- data/lib/moxml/signature/algorithms/sha256.rb +13 -0
- data/lib/moxml/signature/algorithms/sha384.rb +13 -0
- data/lib/moxml/signature/algorithms/sha512.rb +13 -0
- data/lib/moxml/signature/algorithms/signature_method_base.rb +58 -0
- data/lib/moxml/signature/algorithms/transform_base.rb +40 -0
- data/lib/moxml/signature/algorithms.rb +113 -0
- data/lib/moxml/signature/errors.rb +99 -0
- data/lib/moxml/signature/key_extractor.rb +172 -0
- data/lib/moxml/signature/model/algorithm_method.rb +18 -0
- data/lib/moxml/signature/model/digest_method.rb +15 -0
- data/lib/moxml/signature/model/key/dsa_key_value.rb +28 -0
- data/lib/moxml/signature/model/key/ec_key_value.rb +22 -0
- data/lib/moxml/signature/model/key/rsa_key_value.rb +19 -0
- data/lib/moxml/signature/model/key/x509_data.rb +32 -0
- data/lib/moxml/signature/model/key/x509_digest.rb +19 -0
- data/lib/moxml/signature/model/key/x509_issuer_serial.rb +19 -0
- data/lib/moxml/signature/model/key_info.rb +21 -0
- data/lib/moxml/signature/model/key_value.rb +20 -0
- data/lib/moxml/signature/model/object_element.rb +18 -0
- data/lib/moxml/signature/model/reference.rb +22 -0
- data/lib/moxml/signature/model/signature.rb +21 -0
- data/lib/moxml/signature/model/signature_value.rb +16 -0
- data/lib/moxml/signature/model/signed_info.rb +20 -0
- data/lib/moxml/signature/model/transform.rb +16 -0
- data/lib/moxml/signature/model/transforms.rb +36 -0
- data/lib/moxml/signature/model.rb +35 -0
- data/lib/moxml/signature/parser.rb +270 -0
- data/lib/moxml/signature/reference_resolver.rb +79 -0
- data/lib/moxml/signature/reference_result.rb +22 -0
- data/lib/moxml/signature/serializer.rb +163 -0
- data/lib/moxml/signature/signer.rb +73 -0
- data/lib/moxml/signature/single_verification_result.rb +31 -0
- data/lib/moxml/signature/transform_pipeline.rb +106 -0
- data/lib/moxml/signature/verification_result.rb +27 -0
- data/lib/moxml/signature/verifier.rb +129 -0
- data/lib/moxml/signature.rb +94 -0
- data/lib/moxml/version.rb +1 -1
- data/lib/moxml/xml_emitter.rb +71 -0
- data/lib/moxml/xpath/compiler.rb +60 -7
- data/lib/moxml/xpath/parser.rb +22 -15
- data/lib/moxml.rb +5 -0
- data/reference-docs/w3c-xmldsig-bestpractices.md +216 -0
- data/reference-docs/w3c-xmldsig-core.md +400 -0
- data/spec/examples/readme_examples_spec.rb +0 -4
- data/spec/examples/xpath_examples_spec.rb +0 -11
- data/spec/fixtures/xmldsig/keys/rsa_private.pem +28 -0
- data/spec/fixtures/xmldsig/keys/rsa_public.pem +9 -0
- data/spec/fixtures/xmldsig/keys/rsa_ref.pem +15 -0
- data/spec/fixtures/xmldsig/keys/rsa_ref.pub +6 -0
- data/spec/fixtures/xmldsig/sign2-doc.xml +6 -0
- data/spec/fixtures/xmldsig/sign2-result.xml +25 -0
- data/spec/fixtures/xmldsig/sign3-result.xml +39 -0
- data/spec/integration/all_adapters_spec.rb +17 -0
- data/spec/integration/sax_parity_spec.rb +58 -0
- data/spec/integration/shared_examples/edge_cases.rb +0 -10
- data/spec/integration/shared_examples/integration_workflows.rb +0 -10
- data/spec/integration/shared_examples/node_wrappers/attribute_behavior.rb +98 -0
- data/spec/integration/shared_examples/node_wrappers/namespace_behavior.rb +26 -0
- data/spec/integration/shared_examples/node_wrappers/node_behavior.rb +0 -8
- data/spec/moxml/adapter/leptris_spec.rb +75 -0
- data/spec/moxml/adapter/ox_spec.rb +20 -6
- data/spec/moxml/adapter/platform_spec.rb +15 -2
- data/spec/moxml/adapter/shared_examples/adapter_contract.rb +180 -0
- data/spec/moxml/attribute_resolver_spec.rb +108 -0
- data/spec/moxml/c14n/api_spec.rb +117 -0
- data/spec/moxml/c14n/c14n_spec.rb +88 -0
- data/spec/moxml/c14n/comments_pis_spec.rb +65 -0
- data/spec/moxml/c14n/inclusive10_spec.rb +60 -0
- data/spec/moxml/c14n/namespace_edge_cases_spec.rb +88 -0
- data/spec/moxml/c14n/xml_attributes_spec.rb +54 -0
- data/spec/moxml/doctype_spec.rb +1 -1
- data/spec/moxml/entity_spec.rb +76 -0
- data/spec/moxml/node_spec.rb +104 -0
- data/spec/moxml/sax_entity_parity_spec.rb +358 -0
- data/spec/moxml/signature/algorithms/base64_transform_spec.rb +26 -0
- data/spec/moxml/signature/algorithms/digest_base_spec.rb +72 -0
- data/spec/moxml/signature/algorithms/dsa_sha_spec.rb +46 -0
- data/spec/moxml/signature/algorithms/ecdsa_sha_spec.rb +72 -0
- data/spec/moxml/signature/algorithms/enveloped_signature_transform_spec.rb +40 -0
- data/spec/moxml/signature/algorithms/hmac_sha_spec.rb +71 -0
- data/spec/moxml/signature/algorithms/rsa_pkcs1_sha_spec.rb +55 -0
- data/spec/moxml/signature/algorithms_spec.rb +76 -0
- data/spec/moxml/signature/cross_verify_spec.rb +64 -0
- data/spec/moxml/signature/edge_cases_spec.rb +284 -0
- data/spec/moxml/signature/fixtures_spec.rb +80 -0
- data/spec/moxml/signature/key_extractor_spec.rb +96 -0
- data/spec/moxml/signature/model_spec.rb +54 -0
- data/spec/moxml/signature/round_trip_spec.rb +113 -0
- data/spec/moxml/xml_emitter_spec.rb +64 -0
- data/spec/moxml/xpath/axes_spec.rb +72 -0
- data/spec/moxml/xpath/parser_spec.rb +6 -0
- data/spec/moxml/xpath_capabilities_spec.rb +5 -3
- data/spec/performance/benchmark_spec.rb +13 -6
- data/spec/performance/thread_safety_spec.rb +0 -4
- data/spec/spec_helper.rb +5 -1
- metadata +130 -2
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moxml
|
|
4
|
+
module Signature
|
|
5
|
+
class Error < ::Moxml::Error; end
|
|
6
|
+
|
|
7
|
+
class SignatureError < Error; end
|
|
8
|
+
|
|
9
|
+
class UnknownAlgorithm < Error
|
|
10
|
+
attr_reader :category, :uri
|
|
11
|
+
|
|
12
|
+
def initialize(category, uri)
|
|
13
|
+
@category = category
|
|
14
|
+
@uri = uri
|
|
15
|
+
super("Unknown #{category} algorithm: #{uri}")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class DuplicateAlgorithm < Error
|
|
20
|
+
attr_reader :category, :uri
|
|
21
|
+
|
|
22
|
+
def initialize(category, uri)
|
|
23
|
+
@category = category
|
|
24
|
+
@uri = uri
|
|
25
|
+
super("Algorithm already registered for #{category}: #{uri}")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class SigningError < Error
|
|
30
|
+
attr_reader :algorithm
|
|
31
|
+
|
|
32
|
+
def initialize(message, algorithm: nil)
|
|
33
|
+
@algorithm = algorithm
|
|
34
|
+
super(message)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class VerificationError < Error
|
|
39
|
+
attr_reader :signature_id
|
|
40
|
+
|
|
41
|
+
def initialize(message, signature_id: nil)
|
|
42
|
+
@signature_id = signature_id
|
|
43
|
+
super(message)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class ReferenceDigestMismatch < VerificationError
|
|
48
|
+
attr_reader :reference_uri, :expected, :computed
|
|
49
|
+
|
|
50
|
+
def initialize(reference_uri:, expected:, computed:, signature_id: nil)
|
|
51
|
+
@reference_uri = reference_uri
|
|
52
|
+
@expected = expected
|
|
53
|
+
@computed = computed
|
|
54
|
+
super(
|
|
55
|
+
"Digest mismatch for reference #{reference_uri.inspect}",
|
|
56
|
+
signature_id: signature_id,
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
class SignatureValueMismatch < VerificationError
|
|
62
|
+
attr_reader :algorithm
|
|
63
|
+
|
|
64
|
+
def initialize(algorithm: nil, signature_id: nil)
|
|
65
|
+
@algorithm = algorithm
|
|
66
|
+
super("SignatureValue did not verify", signature_id: signature_id)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class TransformError < Error
|
|
71
|
+
attr_reader :algorithm
|
|
72
|
+
|
|
73
|
+
def initialize(message, algorithm: nil)
|
|
74
|
+
@algorithm = algorithm
|
|
75
|
+
super(message)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class CanonicalizationError < Error
|
|
80
|
+
attr_reader :algorithm
|
|
81
|
+
|
|
82
|
+
def initialize(message, algorithm: nil)
|
|
83
|
+
@algorithm = algorithm
|
|
84
|
+
super(message)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
class MalformedSignatureError < Error
|
|
89
|
+
attr_reader :detail
|
|
90
|
+
|
|
91
|
+
def initialize(message, detail: nil)
|
|
92
|
+
@detail = detail
|
|
93
|
+
super(message)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
class SignatureKeyError < Error; end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "openssl"
|
|
4
|
+
require "base64"
|
|
5
|
+
|
|
6
|
+
module Moxml
|
|
7
|
+
module Signature
|
|
8
|
+
# Extracts an OpenSSL verification key from a Model::KeyInfo.
|
|
9
|
+
#
|
|
10
|
+
# Strategy (per spec §4.5): X509Certificate is preferred for key
|
|
11
|
+
# reconstruction because it carries the certified binding. Falls back
|
|
12
|
+
# to RSAKeyValue / DSAKeyValue / ECKeyValue if no certificate is
|
|
13
|
+
# present. KeyName is resolved via an application-supplied key map.
|
|
14
|
+
class KeyExtractor
|
|
15
|
+
attr_reader :key_map, :cert_store
|
|
16
|
+
|
|
17
|
+
def initialize(key_map: {}, cert_store: nil)
|
|
18
|
+
@key_map = key_map || {}
|
|
19
|
+
@cert_store = cert_store
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def extract(key_info)
|
|
23
|
+
return nil if key_info.nil?
|
|
24
|
+
|
|
25
|
+
from_x509_data(key_info.x509_data) ||
|
|
26
|
+
from_key_value(key_info.key_value) ||
|
|
27
|
+
from_key_name(key_info.key_name)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def from_x509_data(x509_data)
|
|
33
|
+
return nil unless x509_data
|
|
34
|
+
|
|
35
|
+
cert_b64 = x509_data.certificates.first
|
|
36
|
+
return nil unless cert_b64
|
|
37
|
+
|
|
38
|
+
# Certificates are stored as base64-encoded DER (spec §4.5.4).
|
|
39
|
+
# Decode here so we hand OpenSSL raw DER bytes.
|
|
40
|
+
cert_der = Base64.strict_decode64(cert_b64.to_s.gsub(/\s+/, ""))
|
|
41
|
+
cert = OpenSSL::X509::Certificate.new(cert_der)
|
|
42
|
+
cert.public_key
|
|
43
|
+
rescue OpenSSL::X509::CertificateError, ArgumentError
|
|
44
|
+
nil
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def from_key_value(key_value)
|
|
48
|
+
return nil unless key_value
|
|
49
|
+
|
|
50
|
+
if key_value.rsa_key_value
|
|
51
|
+
rsa_public_key(key_value.rsa_key_value)
|
|
52
|
+
elsif key_value.dsa_key_value
|
|
53
|
+
dsa_public_key(key_value.dsa_key_value)
|
|
54
|
+
elsif key_value.ec_key_value
|
|
55
|
+
ec_public_key(key_value.ec_key_value)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def from_key_name(key_name)
|
|
60
|
+
return nil unless key_name
|
|
61
|
+
|
|
62
|
+
key_map[key_name]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def rsa_public_key(rsa_kv)
|
|
66
|
+
n = crypto_binary_to_integer(rsa_kv.modulus)
|
|
67
|
+
e = crypto_binary_to_integer(rsa_kv.exponent)
|
|
68
|
+
return nil unless n && e
|
|
69
|
+
|
|
70
|
+
# OpenSSL 3.x removed RSA.new(n, e). Reconstruct via the
|
|
71
|
+
# SubjectPublicKeyInfo form (PKCS#1 RSA public key DER).
|
|
72
|
+
asn1 = OpenSSL::ASN1::Sequence.new(
|
|
73
|
+
[
|
|
74
|
+
OpenSSL::ASN1::Integer.new(n),
|
|
75
|
+
OpenSSL::ASN1::Integer.new(e),
|
|
76
|
+
],
|
|
77
|
+
)
|
|
78
|
+
OpenSSL::PKey::RSA.new(asn1.to_der)
|
|
79
|
+
rescue OpenSSL::PKey::RSAError
|
|
80
|
+
nil
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def dsa_public_key(dsa_kv)
|
|
84
|
+
p = crypto_binary_to_integer(dsa_kv.p)
|
|
85
|
+
q = crypto_binary_to_integer(dsa_kv.q)
|
|
86
|
+
g = crypto_binary_to_integer(dsa_kv.g) if dsa_kv.g
|
|
87
|
+
y = crypto_binary_to_integer(dsa_kv.y)
|
|
88
|
+
return nil unless p && q && y
|
|
89
|
+
|
|
90
|
+
params = OpenSSL::ASN1::Sequence.new(
|
|
91
|
+
[
|
|
92
|
+
OpenSSL::ASN1::Integer.new(p),
|
|
93
|
+
OpenSSL::ASN1::Integer.new(q),
|
|
94
|
+
OpenSSL::ASN1::Integer.new(g || 0),
|
|
95
|
+
OpenSSL::ASN1::Integer.new(y),
|
|
96
|
+
],
|
|
97
|
+
)
|
|
98
|
+
OpenSSL::PKey::DSA.new(params.to_der)
|
|
99
|
+
rescue OpenSSL::PKey::DSAError
|
|
100
|
+
nil
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def ec_public_key(ec_kv)
|
|
104
|
+
return nil unless ec_kv.named_curve_uri && ec_kv.public_key
|
|
105
|
+
|
|
106
|
+
curve_name = curve_name_for(ec_kv.named_curve_uri)
|
|
107
|
+
return nil unless curve_name
|
|
108
|
+
|
|
109
|
+
# The XML signature PublicKey element already contains the
|
|
110
|
+
# uncompressed-point form (0x04 || x || y) per spec §4.5.2.3.
|
|
111
|
+
public_key_decoded = Base64.strict_decode64(ec_kv.public_key)
|
|
112
|
+
spki_der = build_ec_subject_public_key_info(curve_name, public_key_decoded)
|
|
113
|
+
OpenSSL::PKey::EC.new(spki_der)
|
|
114
|
+
rescue OpenSSL::PKey::ECError, ArgumentError
|
|
115
|
+
nil
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Build a SubjectPublicKeyInfo DER for an EC public key.
|
|
119
|
+
# OpenSSL 3.0 makes PKey instances immutable; we can no longer
|
|
120
|
+
# create an empty EC key and assign public_key=. Constructing the
|
|
121
|
+
# full SPKI and reading it back via OpenSSL::PKey::EC.new(der)
|
|
122
|
+
# is the supported path.
|
|
123
|
+
def build_ec_subject_public_key_info(curve_name, point_octets)
|
|
124
|
+
algorithm = OpenSSL::ASN1::Sequence.new(
|
|
125
|
+
[
|
|
126
|
+
OpenSSL::ASN1::ObjectId.new("id-ecPublicKey"),
|
|
127
|
+
OpenSSL::ASN1::ObjectId.new(curve_name_to_oid(curve_name)),
|
|
128
|
+
],
|
|
129
|
+
)
|
|
130
|
+
OpenSSL::ASN1::Sequence.new(
|
|
131
|
+
[
|
|
132
|
+
algorithm,
|
|
133
|
+
OpenSSL::ASN1::BitString.new(point_octets),
|
|
134
|
+
],
|
|
135
|
+
).to_der
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
CURVE_NAME_TO_OID = {
|
|
139
|
+
"prime256v1" => "1.2.840.10045.3.1.7",
|
|
140
|
+
"secp384r1" => "1.3.132.0.34",
|
|
141
|
+
"secp521r1" => "1.3.132.0.35",
|
|
142
|
+
}.freeze
|
|
143
|
+
private_constant :CURVE_NAME_TO_OID
|
|
144
|
+
|
|
145
|
+
def curve_name_to_oid(curve_name)
|
|
146
|
+
CURVE_NAME_TO_OID.fetch(curve_name)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# ds:CryptoBinary is base64-encoded I2OSP output.
|
|
150
|
+
def crypto_binary_to_integer(base64_text)
|
|
151
|
+
return nil unless base64_text
|
|
152
|
+
|
|
153
|
+
raw = Base64.strict_decode64(base64_text.to_s.gsub(/\s+/, ""))
|
|
154
|
+
raw.bytes.reduce(0) { |acc, byte| (acc << 8) | byte }
|
|
155
|
+
rescue ArgumentError
|
|
156
|
+
nil
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Map NamedCurve URN OIDs to OpenSSL curve names (RFC 5480 §2.1).
|
|
160
|
+
CURVE_OID_TO_NAME = {
|
|
161
|
+
"urn:oid:1.2.840.10045.3.1.7" => "prime256v1", # P-256
|
|
162
|
+
"urn:oid:1.3.132.0.34" => "secp384r1", # P-384
|
|
163
|
+
"urn:oid:1.3.132.0.35" => "secp521r1", # P-521
|
|
164
|
+
}.freeze
|
|
165
|
+
private_constant :CURVE_OID_TO_NAME
|
|
166
|
+
|
|
167
|
+
def curve_name_for(uri)
|
|
168
|
+
CURVE_OID_TO_NAME[uri]
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moxml
|
|
4
|
+
module Signature
|
|
5
|
+
module Model
|
|
6
|
+
# Generic algorithm method element. Used for CanonicalizationMethod and
|
|
7
|
+
# SignatureMethod, both of which are `<Foo Algorithm="uri" />` shape.
|
|
8
|
+
class AlgorithmMethod
|
|
9
|
+
attr_accessor :algorithm, :parameters
|
|
10
|
+
|
|
11
|
+
def initialize(algorithm:, parameters: {})
|
|
12
|
+
@algorithm = algorithm
|
|
13
|
+
@parameters = parameters
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moxml
|
|
4
|
+
module Signature
|
|
5
|
+
module Model
|
|
6
|
+
module Key
|
|
7
|
+
# ds:DSAKeyValue — P, Q, G, Y required; J, Seed, PgenCounter optional.
|
|
8
|
+
# Attribute names mirror the W3C spec §4.5.2.1 element names
|
|
9
|
+
# (P, Q, G, Y, J, Seed, PgenCounter).
|
|
10
|
+
class DSAKeyValue
|
|
11
|
+
attr_accessor :p, :q, :g, :y, :j, :seed, :pgen_counter
|
|
12
|
+
|
|
13
|
+
# rubocop:disable-next Naming/MethodParameterName -- P/Q/G/Y/J are W3C spec names
|
|
14
|
+
def initialize(p:, q:, y:, g: nil, j: nil, seed: nil,
|
|
15
|
+
pgen_counter: nil)
|
|
16
|
+
@p = p
|
|
17
|
+
@q = q
|
|
18
|
+
@y = y
|
|
19
|
+
@g = g
|
|
20
|
+
@j = j
|
|
21
|
+
@seed = seed
|
|
22
|
+
@pgen_counter = pgen_counter
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moxml
|
|
4
|
+
module Signature
|
|
5
|
+
module Model
|
|
6
|
+
module Key
|
|
7
|
+
# dsig11:ECKeyValue — NamedCurve URI + PublicKey octets, or
|
|
8
|
+
# explicit ECParameters.
|
|
9
|
+
class ECKeyValue
|
|
10
|
+
attr_accessor :named_curve_uri, :public_key, :ec_parameters
|
|
11
|
+
|
|
12
|
+
def initialize(named_curve_uri: nil, public_key: nil,
|
|
13
|
+
ec_parameters: nil)
|
|
14
|
+
@named_curve_uri = named_curve_uri
|
|
15
|
+
@public_key = public_key
|
|
16
|
+
@ec_parameters = ec_parameters
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moxml
|
|
4
|
+
module Signature
|
|
5
|
+
module Model
|
|
6
|
+
module Key
|
|
7
|
+
# ds:RSAKeyValue — base64-encoded CryptoBinary Modulus + Exponent.
|
|
8
|
+
class RSAKeyValue
|
|
9
|
+
attr_accessor :modulus, :exponent
|
|
10
|
+
|
|
11
|
+
def initialize(modulus:, exponent:)
|
|
12
|
+
@modulus = modulus
|
|
13
|
+
@exponent = exponent
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moxml
|
|
4
|
+
module Signature
|
|
5
|
+
module Model
|
|
6
|
+
module Key
|
|
7
|
+
# ds:X509Data (spec §4.5.4) — container for certificate identifiers.
|
|
8
|
+
# May carry X509IssuerSerial, X509SubjectName, X509SKI, X509Certificate,
|
|
9
|
+
# X509CRL, and dsig11:X509Digest children, all describing the same key.
|
|
10
|
+
class X509Data
|
|
11
|
+
attr_accessor :issuer_serial, :subject_name, :subject_key_id,
|
|
12
|
+
:certificates, :crls, :digests
|
|
13
|
+
|
|
14
|
+
def initialize(issuer_serial: nil, subject_name: nil,
|
|
15
|
+
subject_key_id: nil, certificates: [],
|
|
16
|
+
crls: [], digests: [])
|
|
17
|
+
@issuer_serial = issuer_serial
|
|
18
|
+
@subject_name = subject_name
|
|
19
|
+
@subject_key_id = subject_key_id
|
|
20
|
+
@certificates = Array(certificates)
|
|
21
|
+
@crls = Array(crls)
|
|
22
|
+
@digests = Array(digests)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def first_certificate
|
|
26
|
+
certificates.first
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moxml
|
|
4
|
+
module Signature
|
|
5
|
+
module Model
|
|
6
|
+
module Key
|
|
7
|
+
# dsig11:X509Digest — algorithm URI + base64 digest of a cert.
|
|
8
|
+
class X509Digest
|
|
9
|
+
attr_accessor :algorithm, :digest
|
|
10
|
+
|
|
11
|
+
def initialize(algorithm:, digest:)
|
|
12
|
+
@algorithm = algorithm
|
|
13
|
+
@digest = digest
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moxml
|
|
4
|
+
module Signature
|
|
5
|
+
module Model
|
|
6
|
+
module Key
|
|
7
|
+
# ds:X509IssuerSerial — deprecated in favor of X509Digest.
|
|
8
|
+
class X509IssuerSerial
|
|
9
|
+
attr_accessor :issuer_name, :serial_number
|
|
10
|
+
|
|
11
|
+
def initialize(issuer_name:, serial_number:)
|
|
12
|
+
@issuer_name = issuer_name
|
|
13
|
+
@serial_number = serial_number
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moxml
|
|
4
|
+
module Signature
|
|
5
|
+
module Model
|
|
6
|
+
class KeyInfo
|
|
7
|
+
attr_accessor :id, :key_name, :key_value, :x509_data,
|
|
8
|
+
:raw_elements
|
|
9
|
+
|
|
10
|
+
def initialize(id: nil, key_name: nil, key_value: nil,
|
|
11
|
+
x509_data: nil, raw_elements: [])
|
|
12
|
+
@id = id
|
|
13
|
+
@key_name = key_name
|
|
14
|
+
@key_value = key_value
|
|
15
|
+
@x509_data = x509_data
|
|
16
|
+
@raw_elements = raw_elements
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moxml
|
|
4
|
+
module Signature
|
|
5
|
+
module Model
|
|
6
|
+
class KeyValue
|
|
7
|
+
attr_accessor :rsa_key_value, :dsa_key_value, :ec_key_value,
|
|
8
|
+
:raw_element
|
|
9
|
+
|
|
10
|
+
def initialize(rsa_key_value: nil, dsa_key_value: nil,
|
|
11
|
+
ec_key_value: nil, raw_element: nil)
|
|
12
|
+
@rsa_key_value = rsa_key_value
|
|
13
|
+
@dsa_key_value = dsa_key_value
|
|
14
|
+
@ec_key_value = ec_key_value
|
|
15
|
+
@raw_element = raw_element
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moxml
|
|
4
|
+
module Signature
|
|
5
|
+
module Model
|
|
6
|
+
class ObjectElement
|
|
7
|
+
attr_accessor :id, :mime_type, :encoding, :content
|
|
8
|
+
|
|
9
|
+
def initialize(id: nil, mime_type: nil, encoding: nil, content: nil)
|
|
10
|
+
@id = id
|
|
11
|
+
@mime_type = mime_type
|
|
12
|
+
@encoding = encoding
|
|
13
|
+
@content = content
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moxml
|
|
4
|
+
module Signature
|
|
5
|
+
module Model
|
|
6
|
+
class Reference
|
|
7
|
+
attr_accessor :id, :uri, :type, :transforms, :digest_method,
|
|
8
|
+
:digest_value
|
|
9
|
+
|
|
10
|
+
def initialize(id: nil, uri: nil, type: nil, transforms: nil,
|
|
11
|
+
digest_method: nil, digest_value: nil)
|
|
12
|
+
@id = id
|
|
13
|
+
@uri = uri
|
|
14
|
+
@type = type
|
|
15
|
+
@transforms = transforms
|
|
16
|
+
@digest_method = digest_method
|
|
17
|
+
@digest_value = digest_value
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moxml
|
|
4
|
+
module Signature
|
|
5
|
+
module Model
|
|
6
|
+
class Signature
|
|
7
|
+
attr_accessor :id, :signed_info, :signature_value,
|
|
8
|
+
:key_info, :objects
|
|
9
|
+
|
|
10
|
+
def initialize(id: nil, signed_info: nil, signature_value: nil,
|
|
11
|
+
key_info: nil, objects: [])
|
|
12
|
+
@id = id
|
|
13
|
+
@signed_info = signed_info
|
|
14
|
+
@signature_value = signature_value
|
|
15
|
+
@key_info = key_info
|
|
16
|
+
@objects = objects
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moxml
|
|
4
|
+
module Signature
|
|
5
|
+
module Model
|
|
6
|
+
class SignedInfo
|
|
7
|
+
attr_accessor :id, :canonicalization_method, :signature_method,
|
|
8
|
+
:references
|
|
9
|
+
|
|
10
|
+
def initialize(id: nil, canonicalization_method: nil,
|
|
11
|
+
signature_method: nil, references: [])
|
|
12
|
+
@id = id
|
|
13
|
+
@canonicalization_method = canonicalization_method
|
|
14
|
+
@signature_method = signature_method
|
|
15
|
+
@references = references
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moxml
|
|
4
|
+
module Signature
|
|
5
|
+
module Model
|
|
6
|
+
class Transform
|
|
7
|
+
attr_accessor :algorithm, :parameters
|
|
8
|
+
|
|
9
|
+
def initialize(algorithm:, parameters: {})
|
|
10
|
+
@algorithm = algorithm
|
|
11
|
+
@parameters = parameters
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moxml
|
|
4
|
+
module Signature
|
|
5
|
+
module Model
|
|
6
|
+
class Transforms
|
|
7
|
+
attr_accessor :transforms
|
|
8
|
+
|
|
9
|
+
def initialize(transforms: [])
|
|
10
|
+
@transforms = Array(transforms)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def <<(transform)
|
|
14
|
+
@transforms << transform
|
|
15
|
+
self
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def each(&block)
|
|
19
|
+
@transforms.each(&block)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def empty?
|
|
23
|
+
@transforms.empty?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def size
|
|
27
|
+
@transforms.size
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def length
|
|
31
|
+
@transforms.size
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moxml
|
|
4
|
+
module Signature
|
|
5
|
+
# Plain-Ruby-Object model of the W3C ds:Signature schema.
|
|
6
|
+
#
|
|
7
|
+
# Models carry data; they do not (de)serialize themselves. The Serializer
|
|
8
|
+
# walks a model and emits a Moxml document; the Parser walks a Moxml
|
|
9
|
+
# document and constructs a model. This separation keeps the data shape
|
|
10
|
+
# and the wire shape independent.
|
|
11
|
+
module Model
|
|
12
|
+
autoload :Signature, "moxml/signature/model/signature"
|
|
13
|
+
autoload :SignedInfo, "moxml/signature/model/signed_info"
|
|
14
|
+
autoload :Reference, "moxml/signature/model/reference"
|
|
15
|
+
autoload :Transforms, "moxml/signature/model/transforms"
|
|
16
|
+
autoload :Transform, "moxml/signature/model/transform"
|
|
17
|
+
autoload :DigestMethod, "moxml/signature/model/digest_method"
|
|
18
|
+
autoload :AlgorithmMethod, "moxml/signature/model/algorithm_method"
|
|
19
|
+
autoload :SignatureValue, "moxml/signature/model/signature_value"
|
|
20
|
+
autoload :KeyInfo, "moxml/signature/model/key_info"
|
|
21
|
+
autoload :KeyValue, "moxml/signature/model/key_value"
|
|
22
|
+
autoload :ObjectElement, "moxml/signature/model/object_element"
|
|
23
|
+
|
|
24
|
+
module Key
|
|
25
|
+
autoload :X509Data, "moxml/signature/model/key/x509_data"
|
|
26
|
+
autoload :X509IssuerSerial,
|
|
27
|
+
"moxml/signature/model/key/x509_issuer_serial"
|
|
28
|
+
autoload :X509Digest, "moxml/signature/model/key/x509_digest"
|
|
29
|
+
autoload :RSAKeyValue, "moxml/signature/model/key/rsa_key_value"
|
|
30
|
+
autoload :DSAKeyValue, "moxml/signature/model/key/dsa_key_value"
|
|
31
|
+
autoload :ECKeyValue, "moxml/signature/model/key/ec_key_value"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|