moxml 0.1.25 → 0.1.26

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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +9 -0
  3. data/README.adoc +64 -0
  4. data/docs/signature/algorithms.md +108 -0
  5. data/docs/signature/architecture.md +148 -0
  6. data/docs/signature/c14n.md +110 -0
  7. data/docs/signature/examples.md +109 -0
  8. data/docs/signature/flows.md +132 -0
  9. data/docs/signature/quick-reference.md +115 -0
  10. data/docs/signature/security.md +143 -0
  11. data/examples/signature/auto_key_extraction.rb +24 -0
  12. data/examples/signature/enveloped_rsa.rb +45 -0
  13. data/examples/signature/hmac.rb +33 -0
  14. data/lib/moxml/c14n/attribute_handler.rb +71 -0
  15. data/lib/moxml/c14n/character_encoder.rb +29 -0
  16. data/lib/moxml/c14n/data_model.rb +145 -0
  17. data/lib/moxml/c14n/exclusive.rb +188 -0
  18. data/lib/moxml/c14n/inclusive_10.rb +42 -0
  19. data/lib/moxml/c14n/inclusive_11.rb +21 -0
  20. data/lib/moxml/c14n/namespace_context.rb +68 -0
  21. data/lib/moxml/c14n/namespace_handler.rb +71 -0
  22. data/lib/moxml/c14n/node.rb +42 -0
  23. data/lib/moxml/c14n/nodes/attribute_node.rb +50 -0
  24. data/lib/moxml/c14n/nodes/comment_node.rb +29 -0
  25. data/lib/moxml/c14n/nodes/element_node.rb +62 -0
  26. data/lib/moxml/c14n/nodes/namespace_node.rb +41 -0
  27. data/lib/moxml/c14n/nodes/processing_instruction_node.rb +30 -0
  28. data/lib/moxml/c14n/nodes/root_node.rb +22 -0
  29. data/lib/moxml/c14n/nodes/text_node.rb +29 -0
  30. data/lib/moxml/c14n/nodes.rb +17 -0
  31. data/lib/moxml/c14n/processor.rb +118 -0
  32. data/lib/moxml/c14n/writer.rb +121 -0
  33. data/lib/moxml/c14n/xml_base_handler.rb +140 -0
  34. data/lib/moxml/c14n.rb +96 -0
  35. data/lib/moxml/signature/algorithms/base64_transform.rb +40 -0
  36. data/lib/moxml/signature/algorithms/canonicalization_base.rb +87 -0
  37. data/lib/moxml/signature/algorithms/digest_base.rb +51 -0
  38. data/lib/moxml/signature/algorithms/dsa_sha.rb +107 -0
  39. data/lib/moxml/signature/algorithms/ecdsa_sha.rb +125 -0
  40. data/lib/moxml/signature/algorithms/enveloped_signature_transform.rb +79 -0
  41. data/lib/moxml/signature/algorithms/exc_c14n_10.rb +23 -0
  42. data/lib/moxml/signature/algorithms/hmac_sha.rb +117 -0
  43. data/lib/moxml/signature/algorithms/inclusive_c14n_10.rb +21 -0
  44. data/lib/moxml/signature/algorithms/inclusive_c14n_11.rb +23 -0
  45. data/lib/moxml/signature/algorithms/rsa_pkcs1_sha.rb +56 -0
  46. data/lib/moxml/signature/algorithms/sha1.rb +13 -0
  47. data/lib/moxml/signature/algorithms/sha224.rb +13 -0
  48. data/lib/moxml/signature/algorithms/sha256.rb +13 -0
  49. data/lib/moxml/signature/algorithms/sha384.rb +13 -0
  50. data/lib/moxml/signature/algorithms/sha512.rb +13 -0
  51. data/lib/moxml/signature/algorithms/signature_method_base.rb +58 -0
  52. data/lib/moxml/signature/algorithms/transform_base.rb +40 -0
  53. data/lib/moxml/signature/algorithms.rb +113 -0
  54. data/lib/moxml/signature/errors.rb +99 -0
  55. data/lib/moxml/signature/key_extractor.rb +172 -0
  56. data/lib/moxml/signature/model/algorithm_method.rb +18 -0
  57. data/lib/moxml/signature/model/digest_method.rb +15 -0
  58. data/lib/moxml/signature/model/key/dsa_key_value.rb +29 -0
  59. data/lib/moxml/signature/model/key/ec_key_value.rb +22 -0
  60. data/lib/moxml/signature/model/key/rsa_key_value.rb +19 -0
  61. data/lib/moxml/signature/model/key/x509_data.rb +32 -0
  62. data/lib/moxml/signature/model/key/x509_digest.rb +19 -0
  63. data/lib/moxml/signature/model/key/x509_issuer_serial.rb +19 -0
  64. data/lib/moxml/signature/model/key_info.rb +21 -0
  65. data/lib/moxml/signature/model/key_value.rb +20 -0
  66. data/lib/moxml/signature/model/object_element.rb +18 -0
  67. data/lib/moxml/signature/model/reference.rb +22 -0
  68. data/lib/moxml/signature/model/signature.rb +21 -0
  69. data/lib/moxml/signature/model/signature_value.rb +16 -0
  70. data/lib/moxml/signature/model/signed_info.rb +20 -0
  71. data/lib/moxml/signature/model/transform.rb +16 -0
  72. data/lib/moxml/signature/model/transforms.rb +36 -0
  73. data/lib/moxml/signature/model.rb +35 -0
  74. data/lib/moxml/signature/parser.rb +270 -0
  75. data/lib/moxml/signature/reference_resolver.rb +79 -0
  76. data/lib/moxml/signature/reference_result.rb +22 -0
  77. data/lib/moxml/signature/serializer.rb +163 -0
  78. data/lib/moxml/signature/signer.rb +73 -0
  79. data/lib/moxml/signature/single_verification_result.rb +31 -0
  80. data/lib/moxml/signature/transform_pipeline.rb +106 -0
  81. data/lib/moxml/signature/verification_result.rb +27 -0
  82. data/lib/moxml/signature/verifier.rb +129 -0
  83. data/lib/moxml/signature.rb +94 -0
  84. data/lib/moxml/version.rb +1 -1
  85. data/lib/moxml.rb +2 -0
  86. data/reference-docs/w3c-xmldsig-bestpractices.md +216 -0
  87. data/reference-docs/w3c-xmldsig-core.md +400 -0
  88. data/spec/fixtures/xmldsig/keys/rsa_private.pem +28 -0
  89. data/spec/fixtures/xmldsig/keys/rsa_public.pem +9 -0
  90. data/spec/fixtures/xmldsig/keys/rsa_ref.pem +15 -0
  91. data/spec/fixtures/xmldsig/keys/rsa_ref.pub +6 -0
  92. data/spec/fixtures/xmldsig/sign2-doc.xml +6 -0
  93. data/spec/fixtures/xmldsig/sign2-result.xml +25 -0
  94. data/spec/fixtures/xmldsig/sign3-result.xml +39 -0
  95. data/spec/moxml/c14n/api_spec.rb +117 -0
  96. data/spec/moxml/c14n/c14n_spec.rb +88 -0
  97. data/spec/moxml/c14n/comments_pis_spec.rb +65 -0
  98. data/spec/moxml/c14n/inclusive10_spec.rb +60 -0
  99. data/spec/moxml/c14n/namespace_edge_cases_spec.rb +88 -0
  100. data/spec/moxml/c14n/xml_attributes_spec.rb +54 -0
  101. data/spec/moxml/signature/algorithms/base64_transform_spec.rb +26 -0
  102. data/spec/moxml/signature/algorithms/digest_base_spec.rb +72 -0
  103. data/spec/moxml/signature/algorithms/dsa_sha_spec.rb +46 -0
  104. data/spec/moxml/signature/algorithms/ecdsa_sha_spec.rb +72 -0
  105. data/spec/moxml/signature/algorithms/enveloped_signature_transform_spec.rb +40 -0
  106. data/spec/moxml/signature/algorithms/hmac_sha_spec.rb +71 -0
  107. data/spec/moxml/signature/algorithms/rsa_pkcs1_sha_spec.rb +55 -0
  108. data/spec/moxml/signature/algorithms_spec.rb +76 -0
  109. data/spec/moxml/signature/cross_verify_spec.rb +64 -0
  110. data/spec/moxml/signature/edge_cases_spec.rb +284 -0
  111. data/spec/moxml/signature/fixtures_spec.rb +80 -0
  112. data/spec/moxml/signature/key_extractor_spec.rb +96 -0
  113. data/spec/moxml/signature/model_spec.rb +54 -0
  114. data/spec/moxml/signature/round_trip_spec.rb +113 -0
  115. metadata +111 -2
@@ -0,0 +1,400 @@
1
+ # XML Signature Syntax and Processing Version 1.1 (W3C Recommendation, 11 April 2013)
2
+
3
+ Curated implementation-focused extract. Source: https://www.w3.org/TR/xmldsig-core/
4
+
5
+ Editors: Donald Eastlake, Joseph Reagle, David Solo, Frederick Hirsch, Magnus Nyström, Thomas Roessler, Kelvin Yiu.
6
+
7
+ ## Abstract
8
+
9
+ XML Signatures provide integrity, message authentication, and/or signer
10
+ authentication services for data of any type, whether located within the XML
11
+ that includes the signature or elsewhere.
12
+
13
+ ## Namespaces
14
+
15
+ | URI | prefix |
16
+ | --- | --- |
17
+ | `http://www.w3.org/2000/09/xmldsig#` | `ds:` / `dsig:` |
18
+ | `http://www.w3.org/2009/xmldsig11#` | `dsig11:` |
19
+
20
+ Algorithm identifiers in the `http://www.w3.org/2001/04/xmldsig-more#` namespace
21
+ are defined by RFC 6931.
22
+
23
+ ## Signature Structure
24
+
25
+ ```
26
+ <Signature ID?>
27
+ <SignedInfo>
28
+ <CanonicalizationMethod />
29
+ <SignatureMethod />
30
+ (<Reference URI? >
31
+ (<Transforms>)?
32
+ <DigestMethod>
33
+ <DigestValue>
34
+ </Reference>)+
35
+ </SignedInfo>
36
+ <SignatureValue>
37
+ (<KeyInfo>)?
38
+ (<Object ID?>)*
39
+ </Signature>
40
+ ```
41
+
42
+ ## Processing Rules
43
+
44
+ ### 3.1 Signature Generation
45
+
46
+ #### 3.1.1 Reference Generation
47
+
48
+ For each data object being signed:
49
+
50
+ 1. Apply the `Transforms`, as determined by the application, to the data object.
51
+ 2. Calculate the digest value over the resulting data object.
52
+ 3. Create a `Reference` element, including the (optional) identification of the
53
+ data object, any (optional) transform elements, the digest algorithm and the
54
+ `DigestValue`.
55
+
56
+ #### 3.1.2 Signature Generation
57
+
58
+ 1. Create `SignedInfo` element with `SignatureMethod`, `CanonicalizationMethod`
59
+ and `Reference`(s).
60
+ 2. Canonicalize and then calculate the `SignatureValue` over `SignedInfo` based
61
+ on algorithms specified in `SignedInfo`.
62
+ 3. Construct the `Signature` element that includes `SignedInfo`, `Object`(s),
63
+ `KeyInfo` (if required), and `SignatureValue`.
64
+
65
+ ### 3.2 Core Validation
66
+
67
+ The required steps of core validation include:
68
+
69
+ 1. **Reference validation** — verification of the digest contained in each
70
+ `Reference` in `SignedInfo`.
71
+ 2. **Signature validation** — cryptographic signature validation of the signature
72
+ calculated over `SignedInfo`.
73
+
74
+ Comparison of each value is over the numeric (integer) or decoded octet sequence
75
+ of the value.
76
+
77
+ #### 3.2.1 Reference Validation
78
+
79
+ 1. Canonicalize the `SignedInfo` element based on the `CanonicalizationMethod`.
80
+ 2. For each `Reference` in `SignedInfo`:
81
+ 1. Obtain the data object (dereference `URI`, execute `Transforms`, or fetch
82
+ from local cache).
83
+ 2. Digest using the `DigestMethod`.
84
+ 3. Compare against `DigestValue`; mismatch ⇒ validation fails.
85
+
86
+ #### 3.2.2 Signature Validation
87
+
88
+ 1. Obtain the keying information from `KeyInfo` or external source.
89
+ 2. Obtain the canonical form of `SignedInfo` using `CanonicalizationMethod` and
90
+ confirm the `SignatureValue` over `SignedInfo`.
91
+
92
+ ## Schema
93
+
94
+ ### `ds:CryptoBinary`
95
+
96
+ Integer-to-octet conversion equivalent to IEEE 1363 I2OSP with minimal length,
97
+ then base64-encoded.
98
+
99
+ ### 4.2 `Signature`
100
+
101
+ ```xml
102
+ <element name="Signature" type="ds:SignatureType"/>
103
+ <complexType name="SignatureType">
104
+ <sequence>
105
+ <element ref="ds:SignedInfo"/>
106
+ <element ref="ds:SignatureValue"/>
107
+ <element ref="ds:KeyInfo" minOccurs="0"/>
108
+ <element ref="ds:Object" minOccurs="0" maxOccurs="unbounded"/>
109
+ </sequence>
110
+ <attribute name="Id" type="ID" use="optional"/>
111
+ </complexType>
112
+ ```
113
+
114
+ ### 4.3 `SignatureValue`
115
+
116
+ ```xml
117
+ <complexType name="SignatureValueType">
118
+ <simpleContent>
119
+ <extension base="base64Binary">
120
+ <attribute name="Id" type="ID" use="optional"/>
121
+ </extension>
122
+ </simpleContent>
123
+ </complexType>
124
+ ```
125
+
126
+ ### 4.4 `SignedInfo`
127
+
128
+ ```xml
129
+ <complexType name="SignedInfoType">
130
+ <sequence>
131
+ <element ref="ds:CanonicalizationMethod"/>
132
+ <element ref="ds:SignatureMethod"/>
133
+ <element ref="ds:Reference" maxOccurs="unbounded"/>
134
+ </sequence>
135
+ <attribute name="Id" type="ID" use="optional"/>
136
+ </complexType>
137
+ ```
138
+
139
+ ### 4.4.1 `CanonicalizationMethod`
140
+
141
+ ```xml
142
+ <complexType name="CanonicalizationMethodType" mixed="true">
143
+ <sequence>
144
+ <any namespace="##any" minOccurs="0" maxOccurs="unbounded"/>
145
+ </sequence>
146
+ <attribute name="Algorithm" type="anyURI" use="required"/>
147
+ </complexType>
148
+ ```
149
+
150
+ ### 4.4.2 `SignatureMethod`
151
+
152
+ ```xml
153
+ <complexType name="SignatureMethodType" mixed="true">
154
+ <sequence>
155
+ <element name="HMACOutputLength" minOccurs="0" type="ds:HMACOutputLengthType"/>
156
+ <any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
157
+ </sequence>
158
+ <attribute name="Algorithm" type="anyURI" use="required"/>
159
+ </complexType>
160
+ ```
161
+
162
+ Signatures MUST be deemed invalid if the HMAC truncation length is below the
163
+ larger of (a) half the underlying hash output length, and (b) 80 bits.
164
+
165
+ ### 4.4.3 `Reference`
166
+
167
+ ```xml
168
+ <complexType name="ReferenceType">
169
+ <sequence>
170
+ <element ref="ds:Transforms" minOccurs="0"/>
171
+ <element ref="ds:DigestMethod"/>
172
+ <element ref="ds:DigestValue"/>
173
+ </sequence>
174
+ <attribute name="Id" type="ID" use="optional"/>
175
+ <attribute name="URI" type="anyURI" use="optional"/>
176
+ <attribute name="Type" type="anyURI" use="optional"/>
177
+ </complexType>
178
+ ```
179
+
180
+ #### 4.4.3.2 Reference Processing Model
181
+
182
+ Result of URI dereferencing or transforms is either octet stream or XPath node-set.
183
+
184
+ Defaults:
185
+ - octet stream + node-set transform expected ⇒ parse octets as XML.
186
+ - node-set + octet transform expected ⇒ canonicalize via C14N.
187
+
188
+ A **same-document reference** is a URI-Reference that consists of `#` followed by
189
+ a fragment, or an empty URI.
190
+
191
+ - `URI=""` ⇒ node-set (minus comments) of the entire document.
192
+ - `URI="#id"` ⇒ node-set of the element with that ID + descendants + in-scope
193
+ namespaces/attributes, no comments.
194
+ - `#xpointer(/)` retains comments at root.
195
+ - `#xpointer(id('ID'))` retains comments for the element.
196
+
197
+ ### 4.4.3.4 `Transforms`
198
+
199
+ ```xml
200
+ <complexType name="TransformsType">
201
+ <sequence>
202
+ <element ref="ds:Transform" maxOccurs="unbounded"/>
203
+ </sequence>
204
+ </complexType>
205
+
206
+ <complexType name="TransformType" mixed="true">
207
+ <choice minOccurs="0" maxOccurs="unbounded">
208
+ <any namespace="##other" processContents="lax"/>
209
+ <element name="XPath" type="string"/>
210
+ </choice>
211
+ <attribute name="Algorithm" type="anyURI" use="required"/>
212
+ </complexType>
213
+ ```
214
+
215
+ ### 4.5 `KeyInfo`
216
+
217
+ ```xml
218
+ <complexType name="KeyInfoType" mixed="true">
219
+ <choice maxOccurs="unbounded">
220
+ <element ref="ds:KeyName"/>
221
+ <element ref="ds:KeyValue"/>
222
+ <element ref="ds:RetrievalMethod"/>
223
+ <element ref="ds:X509Data"/>
224
+ <element ref="ds:PGPData"/>
225
+ <element ref="ds:SPKIData"/>
226
+ <element ref="ds:MgmtData"/>
227
+ <any processContents="lax" namespace="##other"/>
228
+ </choice>
229
+ <attribute name="Id" type="ID" use="optional"/>
230
+ </complexType>
231
+ ```
232
+
233
+ `KeyValue` contains one of `DSAKeyValue`, `RSAKeyValue`, or (1.1) `ECKeyValue`.
234
+
235
+ X509Data child elements: `X509IssuerSerial` (deprecated), `X509SKI`,
236
+ `X509SubjectName`, `X509Certificate`, `X509CRL`, `dsig11:X509Digest`.
237
+
238
+ ### 4.6 `Object`
239
+
240
+ Optional element for including data objects. Has `Id`, `MimeType`, `Encoding`
241
+ attributes.
242
+
243
+ ## Additional Syntax
244
+
245
+ ### 5.1 `Manifest`
246
+
247
+ A list of `Reference`s where digest checking is application-defined (not core).
248
+
249
+ ### 5.2 `SignatureProperties`
250
+
251
+ For signature-time, hardware serial, etc. `SignatureProperty` has required
252
+ `Target` attribute referencing the `Signature` element.
253
+
254
+ ### 5.3 PIs and 5.4 Comments
255
+
256
+ Unless `CanonicalizationMethod` strips comments or PIs, they are signed.
257
+
258
+ ## Algorithms
259
+
260
+ ### Mandatory & Recommended URIs
261
+
262
+ **Digest**:
263
+ - Required: SHA1 `http://www.w3.org/2000/09/xmldsig#sha1` (discouraged); SHA256
264
+ `http://www.w3.org/2001/04/xmlenc#sha256`
265
+ - Optional: SHA224 `…xmldsig-more#sha224`; SHA384 `…xmldsig-more#sha384`; SHA512
266
+ `…xmlenc#sha512`
267
+
268
+ **MAC**:
269
+ - Required: HMAC-SHA1 `…xmldsig#hmac-sha1` (discouraged); HMAC-SHA256
270
+ `…xmldsig-more#hmac-sha256`
271
+ - Recommended: HMAC-SHA384, HMAC-SHA512
272
+
273
+ **Signature**:
274
+ - Required: RSA-SHA256 `…xmldsig-more#rsa-sha256`; ECDSA-SHA256
275
+ `…xmldsig-more#ecdsa-sha256`; DSA-SHA1 (verification only) `…xmldsig#dsa-sha1`
276
+ - Recommended: RSA-SHA1 (verification; discouraged for generation)
277
+ - Optional: RSA-{SHA224,SHA384,SHA512}; ECDSA-{SHA1,SHA224,SHA384,SHA512};
278
+ DSA-SHA256 `…xmldsig11#dsa-sha256`
279
+
280
+ **Canonicalization**:
281
+ - Required:
282
+ - Canonical XML 1.0 (omit comments): `http://www.w3.org/TR/2001/REC-xml-c14n-20010315`
283
+ - Canonical XML 1.1 (omit comments): `http://www.w3.org/2006/12/xml-c14n11`
284
+ - Exclusive XML Canonicalization 1.0 (omit comments): `http://www.w3.org/2001/10/xml-exc-c14n#`
285
+ - Recommended variants append `#WithComments`
286
+
287
+ **Transform**:
288
+ - Required: base64 `…xmldsig#base64`; Enveloped Signature
289
+ `…xmldsig#enveloped-signature`
290
+ - Recommended: XPath `…REC-xpath-19991116`; XPath Filter 2.0
291
+ `…2002/06/xmldsig-filter2`
292
+ - Optional: XSLT `…REC-xslt-19991116`
293
+
294
+ ### Digest Algorithms
295
+
296
+ - SHA-1: 160-bit / 20 octets, base64-encoded.
297
+ - SHA-256: 256-bit / 32 octets.
298
+ - SHA-384: 384-bit / 48 octets.
299
+ - SHA-512: 512-bit / 64 octets.
300
+
301
+ ### HMAC
302
+
303
+ Identifier family: `…xmldsig#hmac-sha1`, `…xmldsig-more#hmac-{sha224,sha256,sha384,sha512}`.
304
+ Truncation length (`HMACOutputLength`) MUST be a multiple of 8 bits. If below
305
+ half the hash output length, signature MUST be deemed invalid.
306
+
307
+ ### DSA
308
+
309
+ Identifier: `…xmldsig#dsa-sha1` (1024/160), `…xmldsig11#dsa-sha256` (2048/256 or
310
+ 3072/256).
311
+
312
+ Output (r, s): base64-encoded concatenation of two octet streams using I2OSP with
313
+ length parameter 20 (for SHA-1) or `N` (for SHA-256 with N=|q|).
314
+
315
+ ### RSA (PKCS#1 v1.5)
316
+
317
+ Identifiers: `…xmldsig#rsa-sha1`, `…xmldsig-more#rsa-{sha224,sha256,sha384,sha512}`.
318
+ RSASSA-PKCS1-v1_5 per RFC 3447 §8.2.1.
319
+
320
+ ### ECDSA
321
+
322
+ Identifiers: `…xmldsig-more#ecdsa-{sha1,sha224,sha256,sha384,sha512}`.
323
+ Output (r, s): base64-encoded concatenation of I2OSP of r and s, each of length
324
+ equal to the base point order in bytes (32 for P-256, 66 for P-521).
325
+
326
+ Required curve: P-256 (FIPS 186-3 §D.2.3). Recommended: P-384, P-521.
327
+
328
+ ### Canonicalization
329
+
330
+ All algorithms take octet-stream or node-set, produce octet-stream output.
331
+ Output is UTF-8 (no BOM), NFC. See [XML-C14N], [XML-C14N11], [XML-EXC-C14N].
332
+
333
+ ### Transforms
334
+
335
+ - **base64** (octet→octet, node-set→octet): decodes base64. For node-set input,
336
+ logically applies `self::text()`, sorts by document order, concatenates.
337
+ - **XPath Filtering** (octet|node-set → node-set): evaluates XPath per node,
338
+ includes nodes where boolean result is true. Includes `here()` function.
339
+ - **Enveloped Signature**: removes the containing `Signature` element from
340
+ digest calculation. Equivalent to specific XPath.
341
+ - **XSLT**: octet-stream → octet-stream via XSL stylesheet (sole child).
342
+
343
+ ## XML Canonicalization Considerations
344
+
345
+ Signatures only work if verification uses the same bits as signing. XML surface
346
+ forms vary, so canonicalization standardizes before signing/verification.
347
+
348
+ Categories of change to canonicalize:
349
+ 1. XML 1.0 syntax (line endings, attribute defaults, entity refs, attribute
350
+ normalization).
351
+ 2. DOM/SAX information loss (attribute order, insignificant whitespace, namespace
352
+ declaration locations).
353
+ 3. Charset conversion.
354
+ 4. Namespace inheritance/context.
355
+
356
+ All canonicalization algorithms identified use UTF-8 (no BOM) and do not perform
357
+ character normalization. Applications SHOULD produce content in NFC.
358
+
359
+ ### Namespace Context and Portable Signatures
360
+
361
+ Inclusive canonicalization "attracts" ancestor namespace context, breaking
362
+ signatures when subdocuments are moved. Exclusive canonicalization "repels"
363
+ ancestor context, preserving portability.
364
+
365
+ ## Security Considerations
366
+
367
+ ### 8.1 Transforms
368
+
369
+ - Only what is signed is secure.
370
+ - Only what is "seen" should be signed.
371
+ - "See" what is signed (operate over canonicalized form).
372
+
373
+ ### 8.2 Security Models
374
+
375
+ Public-key signatures vs keyed-hash MACs have different trust models. Public
376
+ keys verify; only private-key holders can sign. MAC keys are shared; any
377
+ verifier can forge.
378
+
379
+ ### 8.3 Algorithms, Key Lengths, Certificates
380
+
381
+ Conforming implementations MUST support RSA signature generation and
382
+ verification with public keys at least 2048 bits. 3072-bit recommended for
383
+ signatures verified beyond 2030.
384
+
385
+ ### 8.4 Error Messages
386
+
387
+ Generic error responses; avoid leaking specifics about algorithm processing.
388
+
389
+ ## References
390
+
391
+ - [XML-C14N] Canonical XML 1.0
392
+ - [XML-C14N11] Canonical XML 1.1
393
+ - [XML-EXC-C14N] Exclusive XML Canonicalization 1.0
394
+ - [PKCS1] RFC 3447 (RSA Cryptography Specifications v2.1)
395
+ - [FIPS-186-3] Digital Signature Standard
396
+ - [FIPS-180-3] Secure Hash Standard
397
+ - [HMAC] RFC 2104
398
+ - [RFC6931] Additional XML Security URIs
399
+ - [XPATH] XML Path Language 1.0
400
+ - [XMLDSIG-BESTPRACTICES] XML Signature Best Practices
@@ -0,0 +1,28 @@
1
+ -----BEGIN PRIVATE KEY-----
2
+ MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8F3ut738mezjz
3
+ Wg6J11OJCe28lyJXHcb6PbMJVm9NUC3MeMr27PI9s7ildxEodm9bOkXTIGD1ev4Y
4
+ 4p69cjMeyn1vFoxUyE+IN9+0pSVs/ABf19/349MFMTC1mpWlBvRtOKyCkLObQ0hn
5
+ i+lMd4mQ+Ht+6+ZQNpNGIE0HcUUTH/r13AkmYLGYjlf2BwiqLP5MaxsdARormXwJ
6
+ wsJD92us5MHN2P4Pew+J+TyCMdM94h7TCR4vmhZrX7EQcbIfsIEYpfRs3IeORW90
7
+ CoXt7AgLmC+myMt8N/VF6OTKzXrShjGGeF88sikG0GSsIVWy7+tReqe4rfx/iavd
8
+ da0LhPJVAgMBAAECggEAMSugaPmFj11CJ5fg2hcA3v/J9vW5g/WkWTG89pFyek8e
9
+ Eeh4ArIxp4Cuog7s3NuNQ9eJfmZmAnZ7K60+mz7Z71A3F03ZNKbC59TXdeWAUavV
10
+ Ozj3c1nLBf30gl0dhq05Q74/lshWM54UtQEF5bgQLeZPfoATzt9dg8UY7ful95f/
11
+ StU1jJB16r7l8Oy1KyJE094ZL+dYSAaJaG/kb/hq857UJxU/9mmnqq0AsAseXrHm
12
+ 9T4AR5vJjhh71+3BHsQNxP6oO2ErV+Bd4MddMRYZbyo7jVIN6t6nti+uB+reKAi0
13
+ FdcKNL+zldxPyKzt/dXh43M1Y60aFJ9jre/x+aqW9wKBgQDmGrguBixFJiBYdJXw
14
+ NXyIDKDYPRlP3+B28piybne3JVvnyZQ+v5KmDCAtE4i/hIkX9/pdyXAoAfsEI8dc
15
+ sarcFPynMjl+BpBxRKiMgIJvAsRh83HfrkewUTk9uhD3hqW4RlKZZIRlsPuEhIcG
16
+ 2oWJzwuDlRsZyrlqLef94O59FwKBgQDRQmJgZm0faJfIFkmy17iCSmpLCHsef3iJ
17
+ gKr2yQBxOJMwConPauZEQ+YdTjk2XPo81TNpjWyBKZD+1RPPpJ5sFzUAfAqCfsxY
18
+ EZWCzuZKntiegwOsdRrp5FdAFrFCmttW0p5AnTH23PMKxT+7fMDmuwq1X3pS712X
19
+ hGujrnXncwKBgDKKrefWDUVHAZXMTd7MMMVFWNMGYJftycT2tlmC8CK3Pv+jhD9g
20
+ HtsAENU11DSU7PPp0QFmrI7tGHCpVzHiCHB8353t5zjqQjHO7eKmm3+8sNv++AU0
21
+ p6Rvws3vH6ju11mpgJ0WugoIHMbXwTzdJLXHV3UYfDJnF+DdonZeQKQTAoGAIo7K
22
+ /k6MAN7eCg4cN6vGbcXqTd/lrUCx4EtecIj7SLdmH03uOlHWGzn3W6maay7pqHgx
23
+ GGJho+cAagU4U1dFTmZ6u0zA05IrHvQwc7zFbVdUQME0LxvbyPqLqirVNUGrrWf0
24
+ +Ii9Qp50iLfQcZ8FoFUNTKyq391l4Gre99YM5J8CgYEA2EsA5AVf3Q4hkY5ImWN9
25
+ hWgcvKaa1Fw8n59iGExgoh1Zr4p9SWxaNxa/iY3q0wdiQBMihEgFdc3JMBiS3RSe
26
+ KZCpPAFeNW9RnXIQwnW06oyaiZkymLq96cYT5ToSLEoFl9/Z+ulMAYrfRJGszyR+
27
+ hJAW5QUsrp1RKksKftYOAlA=
28
+ -----END PRIVATE KEY-----
@@ -0,0 +1,9 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvBd7re9/Jns481oOiddT
3
+ iQntvJciVx3G+j2zCVZvTVAtzHjK9uzyPbO4pXcRKHZvWzpF0yBg9Xr+GOKevXIz
4
+ Hsp9bxaMVMhPiDfftKUlbPwAX9ff9+PTBTEwtZqVpQb0bTisgpCzm0NIZ4vpTHeJ
5
+ kPh7fuvmUDaTRiBNB3FFEx/69dwJJmCxmI5X9gcIqiz+TGsbHQEaK5l8CcLCQ/dr
6
+ rOTBzdj+D3sPifk8gjHTPeIe0wkeL5oWa1+xEHGyH7CBGKX0bNyHjkVvdAqF7ewI
7
+ C5gvpsjLfDf1Rejkys160oYxhnhfPLIpBtBkrCFVsu/rUXqnuK38f4mr3XWtC4Ty
8
+ VQIDAQAB
9
+ -----END PUBLIC KEY-----
@@ -0,0 +1,15 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIICXgIBAAKBgQC15La+LSmHNUs/yqzSuzKdBUED1OfaOZpBp8zxAAQy7VlTrqRh
3
+ /eiJH3VSeRRZEygORvtLgi/teF2P+z/mfJ6IHIdCdkn8MF4CCCQKkjm7JKRrKfK5
4
+ fOUp1NZF22oP8x0L4j67NYCtR9F6KIkV5A6FPAZGI8nsHnyJzRwqmG2xbQIDAQAB
5
+ AoGBAJDT2UW3g/dqUc4rPExWTUiFJG0+mpVBhDd+ukmyL6W1Iojk53I2z25PJAVU
6
+ 7wS1ohEsJ27J7Aty6Vx5Ozn0Q+zYVaKRSxcazNeGbwS0UaGrN0lMvWDs7RmVGCdx
7
+ bI2LUTQ88Bl94dW4QObAub+wMOL6xmVEVrJssZnm+CIqS2UBAkEA49QDNB//oHmi
8
+ iqD4SFotE8Lz80qBGHN15YIm80TKUR2k1LusZl6R5+2nYTF2vPsG+HGXPbkGhqTn
9
+ JL9GMBv7TQJBAMxinne8+bKTvOl/hhdAohFs7aHUBZhZOEuXIf1jYENASk2weYC6
10
+ 95SlHvWcwPHfqVbpwt83sGL8aDm8CCPYPqECQQDEFRQQx72GC0oG0FYAR4RmbrLx
11
+ YN1NAwqkVmlZlIogWEgmQ8Q0cw5Ws+cMMrtEGTU9nN4TZGymc8TwjqNFAsA9AkEA
12
+ ol8Cp/uQn6cxIIt4Gsb1OkTAcJ0BKOxQhfT2QtiNJEBSB3BYxsVCZWvcsaGrwzw9
13
+ yteBQlZ6odkGcD+Kc/eaoQJAH+0a7jlHDu2VCHI63OiNZQJ8J9oxaPvWZyKYSaCO
14
+ iGvon/Z6KGQhXMedPDaCH7UjeMle5AVhjSrSvF6OglgZ9g==
15
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,6 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC15La+LSmHNUs/yqzSuzKdBUED
3
+ 1OfaOZpBp8zxAAQy7VlTrqRh/eiJH3VSeRRZEygORvtLgi/teF2P+z/mfJ6IHIdC
4
+ dkn8MF4CCCQKkjm7JKRrKfK5fOUp1NZF22oP8x0L4j67NYCtR9F6KIkV5A6FPAZG
5
+ I8nsHnyJzRwqmG2xbQIDAQAB
6
+ -----END PUBLIC KEY-----
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0"?>
2
+ <Envelope xmlns="urn:envelope">
3
+ <Data>
4
+ Hello, World!
5
+ </Data>
6
+ </Envelope>
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0"?>
2
+ <Envelope xmlns="urn:envelope">
3
+ <Data>
4
+ Hello, World!
5
+ </Data>
6
+ <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
7
+ <SignedInfo>
8
+ <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
9
+ <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
10
+ <Reference>
11
+ <Transforms>
12
+ <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
13
+ <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
14
+ </Transforms>
15
+ <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
16
+ <DigestValue>Gx8CGUsbi2qvBLd15VCmwELbDMND8F4vY3jPOc7/FJ0=</DigestValue>
17
+ </Reference>
18
+ </SignedInfo>
19
+ <SignatureValue>T2c7nqOw55P8hcP1qhvfPCwOSEAuo8HstZf9shlrggcarxfgWTKhA6UdrF4McfrS
20
+ XtcgHA7zy0Yzd2cgeGkKA2jgI+9QRhoQsifOMuI55sE5r+fpBs+goaxC57gmcBXj
21
+ XnuwIiWf7nfpF4hYZ841HzYd2HcpQKPTdbhvZUprvx8=</SignatureValue>
22
+ <KeyInfo>
23
+ <KeyName>test</KeyName>
24
+ </KeyInfo>
25
+ </Signature></Envelope>
@@ -0,0 +1,39 @@
1
+ <?xml version="1.0"?>
2
+ <Envelope xmlns="urn:envelope">
3
+ <Data>
4
+ Hello, World!
5
+ </Data>
6
+ <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
7
+ <SignedInfo>
8
+ <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
9
+ <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
10
+ <Reference>
11
+ <Transforms>
12
+ <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
13
+ <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
14
+ </Transforms>
15
+ <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
16
+ <DigestValue>Gx8CGUsbi2qvBLd15VCmwELbDMND8F4vY3jPOc7/FJ0=</DigestValue>
17
+ </Reference>
18
+ </SignedInfo>
19
+ <SignatureValue>TGJ9fCzjppp3LgG4fiBJx+0R34wRa7il9XKKZ+kkOAdKkcW0PIAYKmjn0Tn8krGd
20
+ Gw6qtFFqjdohXfhkKmajXAFunEtd3J0kHFkf3obIwRB1qdsYmKXVFxUx3GqcIlph
21
+ vt9v/9FC12JAxwAiJXHuY2xN5uo3xSDER4+tCCy3/AI=</SignatureValue>
22
+ <KeyInfo>
23
+ <X509Data>
24
+ <X509Certificate>MIICLzCCAZgCCQCVuhhQ38rw0TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQGEwJV
25
+ UzEQMA4GA1UECAwHR2VvcmdpYTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
26
+ dHkgTHRkMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTAgFw0xMzA1MjUxODQwMDRa
27
+ GA8zMDEyMDkyNTE4NDAwNFowWzELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB0dlb3Jn
28
+ aWExITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAwwO
29
+ d3d3Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALE4oSql
30
+ eymfHtzOeY86WyvfsjZmaz2XnIo9dzZsK71yMEKkgvXQnnYy9pK0NaYcG0B0hcii
31
+ 3fqGBiHMkZY2BOGWwCC/wOmJCzLq9q6caPWUs71Zko+h59LaqV93vzDmZaXYfFoQ
32
+ gSVEWpEpCSo560x0mSuLnJYdQQzZ/L6xvxZ1AgMBAAEwDQYJKoZIhvcNAQEFBQAD
33
+ gYEATyK/RlfpohUVimgFkycTF2hyusjctseXoZDCctgg/STMsL8iA0P9YB6k91GC
34
+ kWpwevuiwarD1MfSUV6goPINFkIBvfK+5R9lpHaTqqs615z8T9R5VJgaLcFe3tWd
35
+ 7oq3V2q5Nl6MrZfXj2N07qe6/9zfdauxYO26vAEKCvIkbMo=
36
+ </X509Certificate>
37
+ </X509Data>
38
+ </KeyInfo>
39
+ </Signature></Envelope>
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "moxml/c14n"
5
+
6
+ RSpec.describe "Moxml::C14n API" do
7
+ let(:ctx) { Moxml.new(:nokogiri) }
8
+
9
+ describe ".canonicalize (algorithm selector)" do
10
+ it "defaults to inclusive C14N 1.0" do
11
+ doc = ctx.parse("<root xmlns:foo='urn:foo'><foo:a/></root>")
12
+ default_result = Moxml::C14n.canonicalize(doc.root)
13
+ incl10_result = Moxml::C14n.canonicalize(doc.root, algorithm: :inclusive10)
14
+ expect(default_result).to eq(incl10_result)
15
+ end
16
+
17
+ it "raises ArgumentError for unknown algorithm" do
18
+ expect do
19
+ Moxml::C14n.canonicalize("<x/>", algorithm: :bogus)
20
+ end.to raise_error(ArgumentError, /unknown C14N algorithm/)
21
+ end
22
+
23
+ it "accepts inclusive_11 algorithm" do
24
+ doc = ctx.parse("<root/>")
25
+ result = Moxml::C14n.canonicalize(doc.root, algorithm: :inclusive11)
26
+ expect(result).to eq("<root></root>")
27
+ end
28
+
29
+ it "accepts exclusive_10 algorithm" do
30
+ doc = ctx.parse("<root xmlns:foo='urn:foo'><foo:a/></root>")
31
+ result = Moxml::C14n.canonicalize(doc.root, algorithm: :exclusive10)
32
+ # Exclusive renders ns where visibly used (on foo:a), not on root
33
+ expect(result).to include('<foo:a xmlns:foo="urn:foo">')
34
+ end
35
+ end
36
+
37
+ describe ".equivalent?" do
38
+ it "returns true for byte-identical inputs" do
39
+ expect(Moxml::C14n).to be_equivalent("<root>x</root>", "<root>x</root>")
40
+ end
41
+
42
+ it "returns true when only attribute whitespace differs" do
43
+ # Whitespace inside the tag is not significant — both parse identically.
44
+ a = %(<root attr="value"/>)
45
+ b = %(<root attr="value" />)
46
+ expect(Moxml::C14n).to be_equivalent(a, b)
47
+ end
48
+
49
+ it "returns false when text whitespace differs (text IS significant)" do
50
+ a = "<root><child>a</child> </root>"
51
+ b = "<root> <child>a</child></root>"
52
+ expect(Moxml::C14n).not_to be_equivalent(a, b)
53
+ end
54
+
55
+ it "returns true when only attribute order differs" do
56
+ a = %(<root a="1" b="2"/>)
57
+ b = %(<root b="2" a="1"/>)
58
+ expect(Moxml::C14n).to be_equivalent(a, b)
59
+ end
60
+
61
+ it "returns true when only namespace prefix differs" do
62
+ a = "<root xmlns:foo='urn:foo'><foo:a/></root>"
63
+ b = "<root xmlns:bar='urn:foo'><bar:a/></root>"
64
+ # Inclusive canonical form: prefix is part of qname → not equivalent
65
+ expect(Moxml::C14n).not_to be_equivalent(a, b)
66
+ end
67
+
68
+ it "returns false for different content" do
69
+ expect(Moxml::C14n).not_to be_equivalent("<root>a</root>", "<root>b</root>")
70
+ end
71
+
72
+ it "respects algorithm argument" do
73
+ a = "<root xmlns:foo='urn:foo'><foo:a/></root>"
74
+ b = "<root><a xmlns:foo='urn:foo'/></root>"
75
+ # Inclusive: ns on root vs on child → different
76
+ expect(Moxml::C14n).not_to be_equivalent(a, b, algorithm: :inclusive10)
77
+ end
78
+
79
+ it "accepts Moxml::Node inputs" do
80
+ doc1 = ctx.parse("<root/>")
81
+ doc2 = ctx.parse("<root></root>")
82
+ expect(Moxml::C14n).to be_equivalent(doc1.root, doc2.root)
83
+ end
84
+ end
85
+
86
+ describe "convenience methods" do
87
+ it ".canonicalize_inclusive10 matches default" do
88
+ doc = ctx.parse("<root/>")
89
+ expect(Moxml::C14n.canonicalize_inclusive10(doc.root))
90
+ .to eq(Moxml::C14n.canonicalize(doc.root))
91
+ end
92
+
93
+ it ".canonicalize_inclusive11 produces output" do
94
+ doc = ctx.parse("<root/>")
95
+ expect(Moxml::C14n.canonicalize_inclusive11(doc.root)).to eq("<root></root>")
96
+ end
97
+
98
+ it ".canonicalize_exclusive accepts inclusive_namespaces" do
99
+ doc = ctx.parse("<root xmlns:foo='urn:foo'/>")
100
+ result = Moxml::C14n.canonicalize_exclusive(doc.root, inclusive_namespaces: ["foo"])
101
+ # Even though foo is not visibly used, inclusive list forces render
102
+ expect(result).to include('xmlns:foo="urn:foo"')
103
+ end
104
+ end
105
+
106
+ describe "escape helpers (backward compat)" do
107
+ it ".escape_text escapes & < >" do
108
+ expect(Moxml::C14n.escape_text("a & b < c > d"))
109
+ .to eq("a &amp; b &lt; c &gt; d")
110
+ end
111
+
112
+ it ".escape_attribute escapes quotes and whitespace" do
113
+ expect(Moxml::C14n.escape_attribute(%(a"b\tc\nd)))
114
+ .to eq(%(a&quot;b&#x9;c&#xA;d))
115
+ end
116
+ end
117
+ end