|
1 /** |
|
2 * XML Security Library (http://www.aleksey.com/xmlsec). |
|
3 * |
|
4 * "XML Digital Signature" implementation |
|
5 * http://www.w3.org/TR/xmldsig-core/ |
|
6 * http://www.w3.org/Signature/Overview.html |
|
7 * |
|
8 * This is free software; see Copyright file in the source |
|
9 * distribution for preciese wording. |
|
10 * |
|
11 * Copyright (C) 2002-2003 Aleksey Sanin <aleksey@aleksey.com> |
|
12 * Portion Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. |
|
13 */ |
|
14 #include "xmlsec_config.h" |
|
15 #ifndef XMLSEC_NO_XMLDSIG |
|
16 #include "xmlsec_globals.h" |
|
17 |
|
18 #include <stdlib.h> |
|
19 #include <stdio.h> |
|
20 #include <string.h> |
|
21 |
|
22 #include <libxml2_tree.h> |
|
23 #include <libxml2_parser.h> |
|
24 #include <libxml2_globals.h> |
|
25 |
|
26 #include "xmlsec_xmlsec.h" |
|
27 #include "xmlsec_buffer.h" |
|
28 #include "xmlsec_xmltree.h" |
|
29 #include "xmlsec_keys.h" |
|
30 #include "xmlsec_keysmngr.h" |
|
31 #include "xmlsec_transforms.h" |
|
32 #include "xmlsec_membuf.h" |
|
33 #include "xmlsec_xmldsig.h" |
|
34 #include "xmlsec_errors.h" |
|
35 |
|
36 /************************************************************************** |
|
37 * |
|
38 * xmlSecDSigCtx |
|
39 * |
|
40 *************************************************************************/ |
|
41 static int xmlSecDSigCtxProcessSignatureNode (xmlSecDSigCtxPtr dsigCtx, |
|
42 xmlNodePtr node); |
|
43 static int xmlSecDSigCtxProcessSignedInfoNode (xmlSecDSigCtxPtr dsigCtx, |
|
44 xmlNodePtr node); |
|
45 static int xmlSecDSigCtxProcessKeyInfoNode (xmlSecDSigCtxPtr dsigCtx, |
|
46 xmlNodePtr node); |
|
47 static int xmlSecDSigCtxProcessObjectNode (xmlSecDSigCtxPtr dsigCtx, |
|
48 xmlNodePtr node); |
|
49 static int xmlSecDSigCtxProcessManifestNode (xmlSecDSigCtxPtr dsigCtx, |
|
50 xmlNodePtr node); |
|
51 |
|
52 /* The ID attribute in XMLDSig is 'Id' */ |
|
53 static const xmlChar* xmlSecDSigIds[] = { xmlSecAttrId, NULL }; |
|
54 |
|
55 /** |
|
56 * xmlSecDSigCtxCreate: |
|
57 * @keysMngr: the pointer to keys manager. |
|
58 * |
|
59 * Creates <dsig:Signature/> element processing context. |
|
60 * The caller is responsible for destroying returend object by calling |
|
61 * #xmlSecDSigCtxDestroy function. |
|
62 * |
|
63 * Returns pointer to newly allocated context object or NULL if an error |
|
64 * occurs. |
|
65 */ |
|
66 EXPORT_C |
|
67 xmlSecDSigCtxPtr |
|
68 xmlSecDSigCtxCreate(xmlSecKeysMngrPtr keysMngr) { |
|
69 xmlSecDSigCtxPtr dsigCtx; |
|
70 int ret; |
|
71 |
|
72 dsigCtx = (xmlSecDSigCtxPtr) xmlMalloc(sizeof(xmlSecDSigCtx)); |
|
73 if(dsigCtx == NULL) { |
|
74 xmlSecError(XMLSEC_ERRORS_HERE, |
|
75 NULL, |
|
76 NULL, |
|
77 XMLSEC_ERRORS_R_MALLOC_FAILED, |
|
78 "sizeof(xmlSecDSigCtx)=%d", |
|
79 sizeof(xmlSecDSigCtx)); |
|
80 return(NULL); |
|
81 } |
|
82 |
|
83 ret = xmlSecDSigCtxInitialize(dsigCtx, keysMngr); |
|
84 if(ret < 0) { |
|
85 xmlSecError(XMLSEC_ERRORS_HERE, |
|
86 NULL, |
|
87 "xmlSecDSigCtxInitialize", |
|
88 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
89 XMLSEC_ERRORS_NO_MESSAGE); |
|
90 xmlSecDSigCtxDestroy(dsigCtx); |
|
91 return(NULL); |
|
92 } |
|
93 return(dsigCtx); |
|
94 } |
|
95 |
|
96 /** |
|
97 * xmlSecDSigCtxDestroy: |
|
98 * @dsigCtx: the pointer to <dsig:Signature/> processing context. |
|
99 * |
|
100 * Destroy context object created with #xmlSecDSigCtxCreate function. |
|
101 */ |
|
102 EXPORT_C |
|
103 void |
|
104 xmlSecDSigCtxDestroy(xmlSecDSigCtxPtr dsigCtx) { |
|
105 xmlSecAssert(dsigCtx != NULL); |
|
106 |
|
107 xmlSecDSigCtxFinalize(dsigCtx); |
|
108 xmlFree(dsigCtx); |
|
109 } |
|
110 |
|
111 /** |
|
112 * xmlSecDSigCtxInitialize: |
|
113 * @dsigCtx: the pointer to <dsig:Signature/> processing context. |
|
114 * @keysMngr: the pointer to keys manager. |
|
115 * |
|
116 * Initializes <dsig:Signature/> element processing context. |
|
117 * The caller is responsible for cleaing up returend object by calling |
|
118 * #xmlSecDSigCtxFinalize function. |
|
119 * |
|
120 * Returns 0 on success or a negative value if an error occurs. |
|
121 */ |
|
122 EXPORT_C |
|
123 int |
|
124 xmlSecDSigCtxInitialize(xmlSecDSigCtxPtr dsigCtx, xmlSecKeysMngrPtr keysMngr) { |
|
125 int ret; |
|
126 |
|
127 xmlSecAssert2(dsigCtx != NULL, -1); |
|
128 |
|
129 memset(dsigCtx, 0, sizeof(xmlSecDSigCtx)); |
|
130 |
|
131 /* initialize key info */ |
|
132 ret = xmlSecKeyInfoCtxInitialize(&(dsigCtx->keyInfoReadCtx), keysMngr); |
|
133 if(ret < 0) { |
|
134 xmlSecError(XMLSEC_ERRORS_HERE, |
|
135 NULL, |
|
136 "xmlSecKeyInfoCtxInitialize", |
|
137 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
138 XMLSEC_ERRORS_NO_MESSAGE); |
|
139 return(-1); |
|
140 } |
|
141 dsigCtx->keyInfoReadCtx.mode = xmlSecKeyInfoModeRead; |
|
142 |
|
143 ret = xmlSecKeyInfoCtxInitialize(&(dsigCtx->keyInfoWriteCtx), keysMngr); |
|
144 if(ret < 0) { |
|
145 xmlSecError(XMLSEC_ERRORS_HERE, |
|
146 NULL, |
|
147 "xmlSecKeyInfoCtxInitialize", |
|
148 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
149 XMLSEC_ERRORS_NO_MESSAGE); |
|
150 return(-1); |
|
151 } |
|
152 dsigCtx->keyInfoWriteCtx.mode = xmlSecKeyInfoModeWrite; |
|
153 /* it's not wise to write private key :) */ |
|
154 dsigCtx->keyInfoWriteCtx.keyReq.keyType = xmlSecKeyDataTypePublic; |
|
155 |
|
156 /* initializes transforms dsigCtx */ |
|
157 ret = xmlSecTransformCtxInitialize(&(dsigCtx->transformCtx)); |
|
158 if(ret < 0) { |
|
159 xmlSecError(XMLSEC_ERRORS_HERE, |
|
160 NULL, |
|
161 "xmlSecTransformCtxInitialize", |
|
162 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
163 XMLSEC_ERRORS_NO_MESSAGE); |
|
164 return(-1); |
|
165 } |
|
166 |
|
167 /* references lists from SignedInfo and Manifest elements */ |
|
168 xmlSecPtrListInitialize(&(dsigCtx->signedInfoReferences), |
|
169 xmlSecDSigReferenceCtxListId); |
|
170 xmlSecPtrListInitialize(&(dsigCtx->manifestReferences), |
|
171 xmlSecDSigReferenceCtxListId); |
|
172 |
|
173 dsigCtx->enabledReferenceUris = xmlSecTransformUriTypeAny; |
|
174 return(0); |
|
175 } |
|
176 |
|
177 /** |
|
178 * xmlSecDSigCtxFinalize: |
|
179 * @dsigCtx: the pointer to <dsig:Signature/> processing context. |
|
180 * |
|
181 * Cleans up @dsigCtx object initialized with #xmlSecDSigCtxInitialize function. |
|
182 */ |
|
183 EXPORT_C |
|
184 void |
|
185 xmlSecDSigCtxFinalize(xmlSecDSigCtxPtr dsigCtx) { |
|
186 xmlSecAssert(dsigCtx != NULL); |
|
187 |
|
188 xmlSecTransformCtxFinalize(&(dsigCtx->transformCtx)); |
|
189 xmlSecKeyInfoCtxFinalize(&(dsigCtx->keyInfoReadCtx)); |
|
190 xmlSecKeyInfoCtxFinalize(&(dsigCtx->keyInfoWriteCtx)); |
|
191 xmlSecPtrListFinalize(&(dsigCtx->signedInfoReferences)); |
|
192 xmlSecPtrListFinalize(&(dsigCtx->manifestReferences)); |
|
193 |
|
194 if(dsigCtx->enabledReferenceTransforms != NULL) { |
|
195 xmlSecPtrListDestroy(dsigCtx->enabledReferenceTransforms); |
|
196 } |
|
197 if(dsigCtx->signKey != NULL) { |
|
198 xmlSecKeyDestroy(dsigCtx->signKey); |
|
199 } |
|
200 if(dsigCtx->id != NULL) { |
|
201 xmlFree(dsigCtx->id); |
|
202 } |
|
203 memset(dsigCtx, 0, sizeof(xmlSecDSigCtx)); |
|
204 } |
|
205 |
|
206 /** |
|
207 * xmlSecDSigCtxEnableReferenceTransform: |
|
208 * @dsigCtx: the pointer to <dsig:Signature/> processing context. |
|
209 * @transformId: the transform klass. |
|
210 * |
|
211 * Enables @transformId for <dsig:Reference/> elements processing. |
|
212 * |
|
213 * Returns 0 on success or a negative value if an error occurs. |
|
214 */ |
|
215 EXPORT_C |
|
216 int |
|
217 xmlSecDSigCtxEnableReferenceTransform(xmlSecDSigCtxPtr dsigCtx, xmlSecTransformId transformId) { |
|
218 int ret; |
|
219 |
|
220 xmlSecAssert2(dsigCtx != NULL, -1); |
|
221 xmlSecAssert2(dsigCtx->result == NULL, -1); |
|
222 xmlSecAssert2(transformId != xmlSecTransformIdUnknown, -1); |
|
223 |
|
224 if(dsigCtx->enabledReferenceTransforms == NULL) { |
|
225 dsigCtx->enabledReferenceTransforms = xmlSecPtrListCreate(xmlSecTransformIdListId); |
|
226 if(dsigCtx->enabledReferenceTransforms == NULL) { |
|
227 xmlSecError(XMLSEC_ERRORS_HERE, |
|
228 NULL, |
|
229 "xmlSecPtrListCreate", |
|
230 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
231 XMLSEC_ERRORS_NO_MESSAGE); |
|
232 return(-1); |
|
233 } |
|
234 } |
|
235 |
|
236 ret = xmlSecPtrListAdd(dsigCtx->enabledReferenceTransforms, (void*)transformId); |
|
237 if(ret < 0) { |
|
238 xmlSecError(XMLSEC_ERRORS_HERE, |
|
239 NULL, |
|
240 "xmlSecPtrListAdd", |
|
241 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
242 XMLSEC_ERRORS_NO_MESSAGE); |
|
243 return(-1); |
|
244 } |
|
245 return(0); |
|
246 } |
|
247 |
|
248 /** |
|
249 * xmlSecDSigCtxEnableSignatureTransform: |
|
250 * @dsigCtx: the pointer to <dsig:Signature/> processing context. |
|
251 * @transformId: the transform klass. |
|
252 * |
|
253 * Enables @transformId for <dsig:SignedInfo/> element processing. |
|
254 * |
|
255 * Returns 0 on success or a negative value if an error occurs. |
|
256 */ |
|
257 EXPORT_C |
|
258 int |
|
259 xmlSecDSigCtxEnableSignatureTransform(xmlSecDSigCtxPtr dsigCtx, xmlSecTransformId transformId) { |
|
260 xmlSecAssert2(dsigCtx != NULL, -1); |
|
261 xmlSecAssert2(dsigCtx->result == NULL, -1); |
|
262 xmlSecAssert2(transformId != xmlSecTransformIdUnknown, -1); |
|
263 |
|
264 return(xmlSecPtrListAdd(&(dsigCtx->transformCtx.enabledTransforms), (void*)transformId)); |
|
265 } |
|
266 |
|
267 /** |
|
268 * xmlSecDSigCtxGetPreSignBuffer: |
|
269 * @dsigCtx: the pointer to <dsig:Signature/> processing context. |
|
270 * |
|
271 * Gets pointer to the buffer with serialized <dsig:SignedInfo/> element |
|
272 * just before signature claculation (valid if and only if |
|
273 * #XMLSEC_DSIG_FLAGS_STORE_SIGNATURE context flag is set. |
|
274 * |
|
275 * Returns 0 on success or a negative value if an error occurs. |
|
276 */ |
|
277 EXPORT_C |
|
278 xmlSecBufferPtr |
|
279 xmlSecDSigCtxGetPreSignBuffer(xmlSecDSigCtxPtr dsigCtx) { |
|
280 xmlSecAssert2(dsigCtx != NULL, NULL); |
|
281 |
|
282 return((dsigCtx->preSignMemBufMethod != NULL) ? |
|
283 xmlSecTransformMemBufGetBuffer(dsigCtx->preSignMemBufMethod) : NULL); |
|
284 } |
|
285 |
|
286 /** |
|
287 * xmlSecDSigCtxSign: |
|
288 * @dsigCtx: the pointer to <dsig:Signature/> processing context. |
|
289 * @tmpl: the pointer to <dsig:Signature/> node with signature template. |
|
290 * |
|
291 * Signs the data as described in @tmpl node. |
|
292 * |
|
293 * Returns 0 on success or a negative value if an error occurs. |
|
294 */ |
|
295 EXPORT_C |
|
296 int |
|
297 xmlSecDSigCtxSign(xmlSecDSigCtxPtr dsigCtx, xmlNodePtr tmpl) { |
|
298 int ret; |
|
299 |
|
300 xmlSecAssert2(dsigCtx != NULL, -1); |
|
301 xmlSecAssert2(dsigCtx->result == NULL, -1); |
|
302 xmlSecAssert2(tmpl != NULL, -1); |
|
303 xmlSecAssert2(tmpl->doc != NULL, -1); |
|
304 |
|
305 /* add ids for Signature nodes */ |
|
306 dsigCtx->operation = xmlSecTransformOperationSign; |
|
307 dsigCtx->status = xmlSecDSigStatusUnknown; |
|
308 xmlSecAddIDs(tmpl->doc, tmpl, xmlSecDSigIds); |
|
309 |
|
310 /* read signature template */ |
|
311 ret = xmlSecDSigCtxProcessSignatureNode(dsigCtx, tmpl); |
|
312 if(ret < 0) { |
|
313 xmlSecError(XMLSEC_ERRORS_HERE, |
|
314 NULL, |
|
315 "xmlSecDSigCtxSigantureProcessNode", |
|
316 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
317 XMLSEC_ERRORS_NO_MESSAGE); |
|
318 return(-1); |
|
319 } |
|
320 xmlSecAssert2(dsigCtx->signMethod != NULL, -1); |
|
321 xmlSecAssert2(dsigCtx->signValueNode != NULL, -1); |
|
322 |
|
323 /* references processing might change the status */ |
|
324 if(dsigCtx->status != xmlSecDSigStatusUnknown) { |
|
325 return(0); |
|
326 } |
|
327 |
|
328 /* check what we've got */ |
|
329 dsigCtx->result = dsigCtx->transformCtx.result; |
|
330 if((dsigCtx->result == NULL) || (xmlSecBufferGetData(dsigCtx->result) == NULL)) { |
|
331 xmlSecError(XMLSEC_ERRORS_HERE, |
|
332 NULL, |
|
333 NULL, |
|
334 XMLSEC_ERRORS_R_INVALID_RESULT, |
|
335 XMLSEC_ERRORS_NO_MESSAGE); |
|
336 return(-1); |
|
337 } |
|
338 |
|
339 /* write signed data to xml */ |
|
340 xmlNodeSetContentLen(dsigCtx->signValueNode, |
|
341 xmlSecBufferGetData(dsigCtx->result), |
|
342 xmlSecBufferGetSize(dsigCtx->result)); |
|
343 if ( OOM_FLAG ) |
|
344 { |
|
345 return(-1); |
|
346 } |
|
347 |
|
348 /* set success status and we are done */ |
|
349 dsigCtx->status = xmlSecDSigStatusSucceeded; |
|
350 return(0); |
|
351 } |
|
352 |
|
353 /** |
|
354 * xmlSecDSigCtxVerify: |
|
355 * @dsigCtx: the pointer to <dsig:Signature/> processing context. |
|
356 * @node: the pointer with <dsig:Signature/> node. |
|
357 * |
|
358 * Vaidates signature in the @node. The verification result is returned |
|
359 * in #status member of the @dsigCtx object. |
|
360 * |
|
361 * Returns 0 on success (check #status member of @dsigCtx to get |
|
362 * signature verification result) or a negative value if an error occurs. |
|
363 */ |
|
364 EXPORT_C |
|
365 int |
|
366 xmlSecDSigCtxVerify(xmlSecDSigCtxPtr dsigCtx, xmlNodePtr node) { |
|
367 int ret; |
|
368 |
|
369 xmlSecAssert2(dsigCtx != NULL, -1); |
|
370 xmlSecAssert2(node != NULL, -1); |
|
371 xmlSecAssert2(node->doc != NULL, -1); |
|
372 |
|
373 /* add ids for Signature nodes */ |
|
374 dsigCtx->operation = xmlSecTransformOperationVerify; |
|
375 dsigCtx->status = xmlSecDSigStatusUnknown; |
|
376 xmlSecAddIDs(node->doc, node, xmlSecDSigIds); |
|
377 |
|
378 /* read siganture info */ |
|
379 ret = xmlSecDSigCtxProcessSignatureNode(dsigCtx, node); |
|
380 if(ret < 0) { |
|
381 xmlSecError(XMLSEC_ERRORS_HERE, |
|
382 NULL, |
|
383 "xmlSecDSigCtxSigantureProcessNode", |
|
384 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
385 XMLSEC_ERRORS_NO_MESSAGE); |
|
386 return(-1); |
|
387 } |
|
388 xmlSecAssert2(dsigCtx->signMethod != NULL, -1); |
|
389 xmlSecAssert2(dsigCtx->signValueNode != NULL, -1); |
|
390 |
|
391 /* references processing might change the status */ |
|
392 if(dsigCtx->status != xmlSecDSigStatusUnknown) { |
|
393 return(0); |
|
394 } |
|
395 |
|
396 /* verify SignatureValue node content */ |
|
397 ret = xmlSecTransformVerifyNodeContent(dsigCtx->signMethod, dsigCtx->signValueNode, |
|
398 &(dsigCtx->transformCtx)); |
|
399 if(ret < 0) { |
|
400 xmlSecError(XMLSEC_ERRORS_HERE, |
|
401 NULL, |
|
402 "xmlSecTransformVerifyNodeContent", |
|
403 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
404 XMLSEC_ERRORS_NO_MESSAGE); |
|
405 return(-1); |
|
406 } |
|
407 |
|
408 /* set status and we are done */ |
|
409 if(dsigCtx->signMethod->status == xmlSecTransformStatusOk) { |
|
410 dsigCtx->status = xmlSecDSigStatusSucceeded; |
|
411 } else { |
|
412 dsigCtx->status = xmlSecDSigStatusInvalid; |
|
413 } |
|
414 return(0); |
|
415 } |
|
416 |
|
417 /** |
|
418 * xmlSecDSigCtxProcessSignatureNode: |
|
419 * |
|
420 * The Signature element (http://www.w3.org/TR/xmldsig-core/#sec-Signature) |
|
421 * |
|
422 * The Signature element is the root element of an XML Signature. |
|
423 * Implementation MUST generate laxly schema valid [XML-schema] Signature |
|
424 * elements as specified by the following schema: |
|
425 * The way in which the SignedInfo element is presented to the |
|
426 * canonicalization method is dependent on that method. The following |
|
427 * applies to algorithms which process XML as nodes or characters: |
|
428 * |
|
429 * - XML based canonicalization implementations MUST be provided with |
|
430 * a [XPath] node-set originally formed from the document containing |
|
431 * the SignedInfo and currently indicating the SignedInfo, its descendants, |
|
432 * and the attribute and namespace nodes of SignedInfo and its descendant |
|
433 * elements. |
|
434 * |
|
435 * - Text based canonicalization algorithms (such as CRLF and charset |
|
436 * normalization) should be provided with the UTF-8 octets that represent |
|
437 * the well-formed SignedInfo element, from the first character to the |
|
438 * last character of the XML representation, inclusive. This includes |
|
439 * the entire text of the start and end tags of the SignedInfo element |
|
440 * as well as all descendant markup and character data (i.e., the text) |
|
441 * between those tags. Use of text based canonicalization of SignedInfo |
|
442 * is NOT RECOMMENDED. |
|
443 * |
|
444 * ================================= |
|
445 * we do not support any non XML based C14N |
|
446 * |
|
447 * Schema Definition: |
|
448 * |
|
449 * <element name="Signature" type="ds:SignatureType"/> |
|
450 * <complexType name="SignatureType"> |
|
451 * <sequence> |
|
452 * <element ref="ds:SignedInfo"/> |
|
453 * <element ref="ds:SignatureValue"/> |
|
454 * <element ref="ds:KeyInfo" minOccurs="0"/> |
|
455 * <element ref="ds:Object" minOccurs="0" maxOccurs="unbounded"/> |
|
456 * </sequence> <attribute name="Id" type="ID" use="optional"/> |
|
457 * </complexType> |
|
458 * |
|
459 * DTD: |
|
460 * |
|
461 * <!ELEMENT Signature (SignedInfo, SignatureValue, KeyInfo?, Object*) > |
|
462 * <!ATTLIST Signature |
|
463 * xmlns CDATA #FIXED 'http://www.w3.org/2000/09/xmldsig#' |
|
464 * Id ID #IMPLIED > |
|
465 * |
|
466 */ |
|
467 static int |
|
468 xmlSecDSigCtxProcessSignatureNode(xmlSecDSigCtxPtr dsigCtx, xmlNodePtr node) { |
|
469 xmlSecTransformDataType firstType; |
|
470 xmlNodePtr signedInfoNode = NULL; |
|
471 xmlNodePtr keyInfoNode = NULL; |
|
472 xmlNodePtr cur; |
|
473 int ret; |
|
474 |
|
475 xmlSecAssert2(dsigCtx != NULL, -1); |
|
476 xmlSecAssert2((dsigCtx->operation == xmlSecTransformOperationSign) || (dsigCtx->operation == xmlSecTransformOperationVerify), -1); |
|
477 xmlSecAssert2(dsigCtx->status == xmlSecDSigStatusUnknown, -1); |
|
478 xmlSecAssert2(dsigCtx->signValueNode == NULL, -1); |
|
479 xmlSecAssert2(dsigCtx->signMethod == NULL, -1); |
|
480 xmlSecAssert2(dsigCtx->c14nMethod == NULL, -1); |
|
481 xmlSecAssert2(node != NULL, -1); |
|
482 |
|
483 if(!xmlSecCheckNodeName(node, xmlSecNodeSignature, xmlSecDSigNs)) { |
|
484 xmlSecError(XMLSEC_ERRORS_HERE, |
|
485 NULL, |
|
486 xmlSecErrorsSafeString(xmlSecNodeGetName(node)), |
|
487 XMLSEC_ERRORS_R_INVALID_NODE, |
|
488 "expected=%s", |
|
489 xmlSecErrorsSafeString(xmlSecNodeSignature)); |
|
490 return(-1); |
|
491 } |
|
492 |
|
493 /* read node data */ |
|
494 xmlSecAssert2(dsigCtx->id == NULL, -1); |
|
495 dsigCtx->id = xmlGetProp(node, xmlSecAttrId); |
|
496 |
|
497 /* first node is required SignedInfo */ |
|
498 cur = xmlSecGetNextElementNode(node->children); |
|
499 if((cur == NULL) || (!xmlSecCheckNodeName(cur, xmlSecNodeSignedInfo, xmlSecDSigNs))) { |
|
500 xmlSecError(XMLSEC_ERRORS_HERE, |
|
501 NULL, |
|
502 xmlSecErrorsSafeString(xmlSecNodeGetName(cur)), |
|
503 XMLSEC_ERRORS_R_INVALID_NODE, |
|
504 "expected=%s", |
|
505 xmlSecErrorsSafeString(xmlSecNodeSignedInfo)); |
|
506 return(-1); |
|
507 } |
|
508 signedInfoNode = cur; |
|
509 cur = xmlSecGetNextElementNode(cur->next); |
|
510 |
|
511 /* next node is required SignatureValue */ |
|
512 if((cur == NULL) || (!xmlSecCheckNodeName(cur, xmlSecNodeSignatureValue, xmlSecDSigNs))) { |
|
513 xmlSecError(XMLSEC_ERRORS_HERE, |
|
514 NULL, |
|
515 xmlSecErrorsSafeString(xmlSecNodeGetName(cur)), |
|
516 XMLSEC_ERRORS_R_INVALID_NODE, |
|
517 "expected=%s", |
|
518 xmlSecErrorsSafeString(xmlSecNodeSignatureValue)); |
|
519 return(-1); |
|
520 } |
|
521 dsigCtx->signValueNode = cur; |
|
522 cur = xmlSecGetNextElementNode(cur->next); |
|
523 |
|
524 /* next node is optional KeyInfo */ |
|
525 if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeKeyInfo, xmlSecDSigNs))) { |
|
526 keyInfoNode = cur; |
|
527 cur = xmlSecGetNextElementNode(cur->next); |
|
528 } else { |
|
529 keyInfoNode = NULL; |
|
530 } |
|
531 |
|
532 /* next nodes are optional Object nodes */ |
|
533 while((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeObject, xmlSecDSigNs))) { |
|
534 /* read manifests from objects */ |
|
535 if((dsigCtx->flags & XMLSEC_DSIG_FLAGS_IGNORE_MANIFESTS) == 0) { |
|
536 ret = xmlSecDSigCtxProcessObjectNode(dsigCtx, cur); |
|
537 if(ret < 0) { |
|
538 xmlSecError(XMLSEC_ERRORS_HERE, |
|
539 NULL, |
|
540 "xmlSecDSigCtxProcessObjectNode", |
|
541 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
542 XMLSEC_ERRORS_NO_MESSAGE); |
|
543 return(-1); |
|
544 } |
|
545 } |
|
546 cur = xmlSecGetNextElementNode(cur->next); |
|
547 } |
|
548 |
|
549 /* if there is something left than it's an error */ |
|
550 if(cur != NULL) { |
|
551 xmlSecError(XMLSEC_ERRORS_HERE, |
|
552 NULL, |
|
553 xmlSecErrorsSafeString(xmlSecNodeGetName(cur)), |
|
554 XMLSEC_ERRORS_R_UNEXPECTED_NODE, |
|
555 XMLSEC_ERRORS_NO_MESSAGE); |
|
556 return(-1); |
|
557 } |
|
558 |
|
559 /* now validated all the references and prepare transform */ |
|
560 ret = xmlSecDSigCtxProcessSignedInfoNode(dsigCtx, signedInfoNode); |
|
561 if(ret < 0) { |
|
562 xmlSecError(XMLSEC_ERRORS_HERE, |
|
563 NULL, |
|
564 "xmlSecDSigCtxProcessSignedInfoNode", |
|
565 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
566 XMLSEC_ERRORS_NO_MESSAGE); |
|
567 return(-1); |
|
568 } |
|
569 /* references processing might change the status */ |
|
570 if(dsigCtx->status != xmlSecDSigStatusUnknown) { |
|
571 return(0); |
|
572 } |
|
573 |
|
574 /* as the result, we should have sign and c14n methods set */ |
|
575 xmlSecAssert2(dsigCtx->signMethod != NULL, -1); |
|
576 xmlSecAssert2(dsigCtx->c14nMethod != NULL, -1); |
|
577 |
|
578 ret = xmlSecDSigCtxProcessKeyInfoNode(dsigCtx, keyInfoNode); |
|
579 if(ret < 0) { |
|
580 xmlSecError(XMLSEC_ERRORS_HERE, |
|
581 NULL, |
|
582 "xmlSecDSigCtxProcessKeyInfoNode", |
|
583 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
584 XMLSEC_ERRORS_NO_MESSAGE); |
|
585 return(-1); |
|
586 } |
|
587 /* as the result, we should have a key */ |
|
588 xmlSecAssert2(dsigCtx->signKey != NULL, -1); |
|
589 |
|
590 /* if we need to write result to xml node then we need base64 encode result */ |
|
591 if(dsigCtx->operation == xmlSecTransformOperationSign) { |
|
592 xmlSecTransformPtr base64Encode; |
|
593 |
|
594 /* we need to add base64 encode transform */ |
|
595 base64Encode = xmlSecTransformCtxCreateAndAppend(&(dsigCtx->transformCtx), |
|
596 xmlSecTransformBase64Id); |
|
597 if(base64Encode == NULL) { |
|
598 xmlSecError(XMLSEC_ERRORS_HERE, |
|
599 NULL, |
|
600 "xmlSecTransformCtxCreateAndAppend", |
|
601 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
602 XMLSEC_ERRORS_NO_MESSAGE); |
|
603 return(-1); |
|
604 } |
|
605 base64Encode->operation = xmlSecTransformOperationEncode; |
|
606 } |
|
607 |
|
608 firstType = xmlSecTransformGetDataType(dsigCtx->transformCtx.first, |
|
609 xmlSecTransformModePush, |
|
610 &(dsigCtx->transformCtx)); |
|
611 if((firstType & xmlSecTransformDataTypeXml) != 0) { |
|
612 xmlSecNodeSetPtr nodeset = NULL; |
|
613 |
|
614 xmlSecAssert2(signedInfoNode != NULL, -1); |
|
615 nodeset = xmlSecNodeSetGetChildren(signedInfoNode->doc, signedInfoNode, 1, 0); |
|
616 if(nodeset == NULL) { |
|
617 xmlSecError(XMLSEC_ERRORS_HERE, |
|
618 NULL, |
|
619 "xmlSecNodeSetGetChildren", |
|
620 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
621 "node=%s", |
|
622 xmlSecErrorsSafeString(xmlSecNodeGetName(signedInfoNode))); |
|
623 return(-1); |
|
624 } |
|
625 |
|
626 /* calculate the signature */ |
|
627 ret = xmlSecTransformCtxXmlExecute(&(dsigCtx->transformCtx), nodeset); |
|
628 if(ret < 0) { |
|
629 xmlSecError(XMLSEC_ERRORS_HERE, |
|
630 NULL, |
|
631 "xmlSecTransformCtxXmlExecute", |
|
632 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
633 XMLSEC_ERRORS_NO_MESSAGE); |
|
634 xmlSecNodeSetDestroy(nodeset); |
|
635 return(-1); |
|
636 } |
|
637 xmlSecNodeSetDestroy(nodeset); |
|
638 } else { |
|
639 xmlSecError(XMLSEC_ERRORS_HERE, |
|
640 NULL, |
|
641 "the binary c14n transforms are not supported yet", |
|
642 XMLSEC_ERRORS_R_NOT_IMPLEMENTED, |
|
643 XMLSEC_ERRORS_NO_MESSAGE); |
|
644 return(-1); |
|
645 } |
|
646 return(0); |
|
647 } |
|
648 |
|
649 /** |
|
650 * xmlSecDSigCtxProcessSignedInfoNode: |
|
651 * |
|
652 * The SignedInfo Element (http://www.w3.org/TR/xmldsig-core/#sec-SignedInfo) |
|
653 * |
|
654 * The structure of SignedInfo includes the canonicalization algorithm, |
|
655 * a result algorithm, and one or more references. The SignedInfo element |
|
656 * may contain an optional ID attribute that will allow it to be referenced by |
|
657 * other signatures and objects. |
|
658 * |
|
659 * SignedInfo does not include explicit result or digest properties (such as |
|
660 * calculation time, cryptographic device serial number, etc.). If an |
|
661 * application needs to associate properties with the result or digest, |
|
662 * it may include such information in a SignatureProperties element within |
|
663 * an Object element. |
|
664 * |
|
665 * Schema Definition: |
|
666 * |
|
667 * <element name="SignedInfo" type="ds:SignedInfoType"/> |
|
668 * <complexType name="SignedInfoType"> |
|
669 * <sequence> |
|
670 * <element ref="ds:CanonicalizationMethod"/> |
|
671 * <element ref="ds:SignatureMethod"/> |
|
672 * <element ref="ds:Reference" maxOccurs="unbounded"/> |
|
673 * </sequence> |
|
674 * <attribute name="Id" type="ID" use="optional"/> |
|
675 * </complexType> |
|
676 * |
|
677 * DTD: |
|
678 * |
|
679 * <!ELEMENT SignedInfo (CanonicalizationMethod, SignatureMethod, Reference+) > |
|
680 * <!ATTLIST SignedInfo Id ID #IMPLIED> |
|
681 * |
|
682 */ |
|
683 static int |
|
684 xmlSecDSigCtxProcessSignedInfoNode(xmlSecDSigCtxPtr dsigCtx, xmlNodePtr node) { |
|
685 xmlSecDSigReferenceCtxPtr dsigRefCtx; |
|
686 xmlNodePtr cur; |
|
687 int ret; |
|
688 |
|
689 xmlSecAssert2(dsigCtx != NULL, -1); |
|
690 xmlSecAssert2(dsigCtx->status == xmlSecDSigStatusUnknown, -1); |
|
691 xmlSecAssert2(dsigCtx->signMethod == NULL, -1); |
|
692 xmlSecAssert2(dsigCtx->c14nMethod == NULL, -1); |
|
693 xmlSecAssert2((dsigCtx->operation == xmlSecTransformOperationSign) || (dsigCtx->operation == xmlSecTransformOperationVerify), -1); |
|
694 xmlSecAssert2(xmlSecPtrListGetSize(&(dsigCtx->signedInfoReferences)) == 0, -1); |
|
695 xmlSecAssert2(node != NULL, -1); |
|
696 |
|
697 /* first node is required CanonicalizationMethod. */ |
|
698 cur = xmlSecGetNextElementNode(node->children); |
|
699 if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeCanonicalizationMethod, xmlSecDSigNs))) { |
|
700 dsigCtx->c14nMethod = xmlSecTransformCtxNodeRead(&(dsigCtx->transformCtx), |
|
701 cur, xmlSecTransformUsageC14NMethod); |
|
702 if(dsigCtx->c14nMethod == NULL) { |
|
703 xmlSecError(XMLSEC_ERRORS_HERE, |
|
704 NULL, |
|
705 "xmlSecTransformCtxNodeRead", |
|
706 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
707 "node=%s", |
|
708 xmlSecErrorsSafeString(xmlSecNodeGetName(cur))); |
|
709 return(-1); |
|
710 } |
|
711 } else if(dsigCtx->defC14NMethodId != xmlSecTransformIdUnknown) { |
|
712 /* the dsig spec does require CanonicalizationMethod node |
|
713 * to be present but in some case it application might decide to |
|
714 * minimize traffic */ |
|
715 dsigCtx->c14nMethod = xmlSecTransformCtxCreateAndAppend(&(dsigCtx->transformCtx), |
|
716 dsigCtx->defC14NMethodId); |
|
717 if(dsigCtx->c14nMethod == NULL) { |
|
718 xmlSecError(XMLSEC_ERRORS_HERE, |
|
719 NULL, |
|
720 "xmlSecTransformCtxAppend", |
|
721 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
722 XMLSEC_ERRORS_NO_MESSAGE); |
|
723 return(-1); |
|
724 } |
|
725 } else { |
|
726 xmlSecError(XMLSEC_ERRORS_HERE, |
|
727 NULL, |
|
728 xmlSecErrorsSafeString(xmlSecNodeGetName(cur)), |
|
729 XMLSEC_ERRORS_R_INVALID_NODE, |
|
730 "expected=%s", |
|
731 xmlSecErrorsSafeString(xmlSecNodeCanonicalizationMethod)); |
|
732 return(-1); |
|
733 } |
|
734 |
|
735 /* insert membuf if requested */ |
|
736 if((dsigCtx->flags & XMLSEC_DSIG_FLAGS_STORE_SIGNATURE) != 0) { |
|
737 xmlSecAssert2(dsigCtx->preSignMemBufMethod == NULL, -1); |
|
738 dsigCtx->preSignMemBufMethod = xmlSecTransformCtxCreateAndAppend(&(dsigCtx->transformCtx), |
|
739 xmlSecTransformMemBufId); |
|
740 if(dsigCtx->preSignMemBufMethod == NULL) { |
|
741 xmlSecError(XMLSEC_ERRORS_HERE, |
|
742 NULL, |
|
743 "xmlSecTransformCtxCreateAndAppend", |
|
744 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
745 "transform=%s", |
|
746 xmlSecErrorsSafeString(xmlSecTransformKlassGetName(xmlSecTransformMemBufId))); |
|
747 } |
|
748 } |
|
749 |
|
750 /* next node is required SignatureMethod. */ |
|
751 if (cur != NULL){ |
|
752 cur = xmlSecGetNextElementNode(cur->next); |
|
753 } |
|
754 if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeSignatureMethod, xmlSecDSigNs))) { |
|
755 dsigCtx->signMethod = xmlSecTransformCtxNodeRead(&(dsigCtx->transformCtx), |
|
756 cur, xmlSecTransformUsageSignatureMethod); |
|
757 if(dsigCtx->signMethod == NULL) { |
|
758 xmlSecError(XMLSEC_ERRORS_HERE, |
|
759 NULL, |
|
760 "xmlSecTransformCtxNodeRead", |
|
761 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
762 "node=%s", |
|
763 xmlSecErrorsSafeString(xmlSecNodeGetName(cur))); |
|
764 return(-1); |
|
765 } |
|
766 } else if(dsigCtx->defSignMethodId != xmlSecTransformIdUnknown) { |
|
767 /* the dsig spec does require SignatureMethod node |
|
768 * to be present but in some case it application might decide to |
|
769 * minimize traffic */ |
|
770 dsigCtx->signMethod = xmlSecTransformCtxCreateAndAppend(&(dsigCtx->transformCtx), |
|
771 dsigCtx->defSignMethodId); |
|
772 if(dsigCtx->signMethod == NULL) { |
|
773 xmlSecError(XMLSEC_ERRORS_HERE, |
|
774 NULL, |
|
775 "xmlSecTransformCtxAppend", |
|
776 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
777 XMLSEC_ERRORS_NO_MESSAGE); |
|
778 return(-1); |
|
779 } |
|
780 } else { |
|
781 xmlSecError(XMLSEC_ERRORS_HERE, |
|
782 NULL, |
|
783 xmlSecErrorsSafeString(xmlSecNodeGetName(cur)), |
|
784 XMLSEC_ERRORS_R_INVALID_NODE, |
|
785 "expected=%s", |
|
786 xmlSecErrorsSafeString(xmlSecNodeSignatureMethod)); |
|
787 return(-1); |
|
788 } |
|
789 dsigCtx->signMethod->operation = dsigCtx->operation; |
|
790 |
|
791 /* calculate references */ |
|
792 if (cur != NULL) { |
|
793 cur = xmlSecGetNextElementNode(cur->next); |
|
794 } |
|
795 while((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeReference, xmlSecDSigNs))) { |
|
796 /* create reference */ |
|
797 dsigRefCtx = xmlSecDSigReferenceCtxCreate(dsigCtx, xmlSecDSigReferenceOriginSignedInfo); |
|
798 if(dsigRefCtx == NULL) { |
|
799 xmlSecError(XMLSEC_ERRORS_HERE, |
|
800 NULL, |
|
801 "xmlSecDSigReferenceCtxCreate", |
|
802 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
803 XMLSEC_ERRORS_NO_MESSAGE); |
|
804 return(-1); |
|
805 } |
|
806 |
|
807 /* add to the list */ |
|
808 ret = xmlSecPtrListAdd(&(dsigCtx->signedInfoReferences), dsigRefCtx); |
|
809 if(ret < 0) { |
|
810 xmlSecError(XMLSEC_ERRORS_HERE, |
|
811 NULL, |
|
812 "xmlSecPtrListAdd", |
|
813 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
814 XMLSEC_ERRORS_NO_MESSAGE); |
|
815 xmlSecDSigReferenceCtxDestroy(dsigRefCtx); |
|
816 return(-1); |
|
817 } |
|
818 |
|
819 /* process */ |
|
820 ret = xmlSecDSigReferenceCtxProcessNode(dsigRefCtx, cur); |
|
821 if(ret < 0) { |
|
822 xmlSecError(XMLSEC_ERRORS_HERE, |
|
823 NULL, |
|
824 "xmlSecDSigReferenceCtxProcessNode", |
|
825 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
826 "node=%s", |
|
827 xmlSecErrorsSafeString(xmlSecNodeGetName(cur))); |
|
828 return(-1); |
|
829 } |
|
830 |
|
831 /* bail out if next Reference processing failed */ |
|
832 if(dsigRefCtx->status != xmlSecDSigStatusSucceeded) { |
|
833 dsigCtx->status = xmlSecDSigStatusInvalid; |
|
834 return(0); |
|
835 } |
|
836 cur = xmlSecGetNextElementNode(cur->next); |
|
837 } |
|
838 |
|
839 /* check that we have at least one Reference */ |
|
840 if(xmlSecPtrListGetSize(&(dsigCtx->signedInfoReferences)) == 0) { |
|
841 xmlSecError(XMLSEC_ERRORS_HERE, |
|
842 NULL, |
|
843 NULL, |
|
844 XMLSEC_ERRORS_R_DSIG_NO_REFERENCES, |
|
845 XMLSEC_ERRORS_NO_MESSAGE); |
|
846 return(-1); |
|
847 } |
|
848 |
|
849 /* if there is something left than it's an error */ |
|
850 if(cur != NULL) { |
|
851 xmlSecError(XMLSEC_ERRORS_HERE, |
|
852 NULL, |
|
853 xmlSecErrorsSafeString(xmlSecNodeGetName(cur)), |
|
854 XMLSEC_ERRORS_R_UNEXPECTED_NODE, |
|
855 XMLSEC_ERRORS_NO_MESSAGE); |
|
856 return(-1); |
|
857 } |
|
858 return(0); |
|
859 } |
|
860 |
|
861 static int |
|
862 xmlSecDSigCtxProcessKeyInfoNode(xmlSecDSigCtxPtr dsigCtx, xmlNodePtr node) { |
|
863 int ret; |
|
864 |
|
865 xmlSecAssert2(dsigCtx != NULL, -1); |
|
866 xmlSecAssert2(dsigCtx->signMethod != NULL, -1); |
|
867 |
|
868 /* set key requirements */ |
|
869 ret = xmlSecTransformSetKeyReq(dsigCtx->signMethod, &(dsigCtx->keyInfoReadCtx.keyReq)); |
|
870 if(ret < 0) { |
|
871 xmlSecError(XMLSEC_ERRORS_HERE, |
|
872 NULL, |
|
873 "xmlSecTransformSetKeyReq", |
|
874 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
875 "transform=%s", |
|
876 xmlSecErrorsSafeString(xmlSecTransformGetName(dsigCtx->signMethod))); |
|
877 return(-1); |
|
878 } |
|
879 |
|
880 /* ignore <dsig:KeyInfo /> if there is the key is already set */ |
|
881 if((dsigCtx->signKey == NULL) && (dsigCtx->keyInfoReadCtx.keysMngr != NULL) |
|
882 && (dsigCtx->keyInfoReadCtx.keysMngr->getKey != NULL)) { |
|
883 dsigCtx->signKey = (dsigCtx->keyInfoReadCtx.keysMngr->getKey)(node, &(dsigCtx->keyInfoReadCtx)); |
|
884 } |
|
885 |
|
886 /* check that we have exactly what we want */ |
|
887 if((dsigCtx->signKey == NULL) || (!xmlSecKeyMatch(dsigCtx->signKey, NULL, &(dsigCtx->keyInfoReadCtx.keyReq)))) { |
|
888 xmlSecError(XMLSEC_ERRORS_HERE, |
|
889 NULL, |
|
890 NULL, |
|
891 XMLSEC_ERRORS_R_KEY_NOT_FOUND, |
|
892 XMLSEC_ERRORS_NO_MESSAGE); |
|
893 return(-1); |
|
894 } |
|
895 |
|
896 /* set the key to the transform */ |
|
897 ret = xmlSecTransformSetKey(dsigCtx->signMethod, dsigCtx->signKey); |
|
898 if(ret < 0) { |
|
899 xmlSecError(XMLSEC_ERRORS_HERE, |
|
900 NULL, |
|
901 "xmlSecTransformSetKey", |
|
902 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
903 "transform=%s", |
|
904 xmlSecErrorsSafeString(xmlSecTransformGetName(dsigCtx->signMethod))); |
|
905 return(-1); |
|
906 } |
|
907 |
|
908 /* if we are signing document, update <dsig:KeyInfo/> node */ |
|
909 if((node != NULL) && (dsigCtx->operation == xmlSecTransformOperationSign)) { |
|
910 ret = xmlSecKeyInfoNodeWrite(node, dsigCtx->signKey, &(dsigCtx->keyInfoWriteCtx)); |
|
911 if(ret < 0) { |
|
912 xmlSecError(XMLSEC_ERRORS_HERE, |
|
913 NULL, |
|
914 "xmlSecKeyInfoNodeWrite", |
|
915 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
916 XMLSEC_ERRORS_NO_MESSAGE); |
|
917 return(-1); |
|
918 } |
|
919 } |
|
920 |
|
921 return(0); |
|
922 } |
|
923 |
|
924 /** |
|
925 * xmlSecDSigCtxProcessObjectNode: |
|
926 * |
|
927 * The Object Element (http://www.w3.org/TR/xmldsig-core/#sec-Object) |
|
928 * |
|
929 * Object is an optional element that may occur one or more times. When |
|
930 * present, this element may contain any data. The Object element may include |
|
931 * optional MIME type, ID, and encoding attributes. |
|
932 * |
|
933 * Schema Definition: |
|
934 * |
|
935 * <element name="Object" type="ds:ObjectType"/> |
|
936 * <complexType name="ObjectType" mixed="true"> |
|
937 * <sequence minOccurs="0" maxOccurs="unbounded"> |
|
938 * <any namespace="##any" processContents="lax"/> |
|
939 * </sequence> |
|
940 * <attribute name="Id" type="ID" use="optional"/> |
|
941 * <attribute name="MimeType" type="string" use="optional"/> |
|
942 * <attribute name="Encoding" type="anyURI" use="optional"/> |
|
943 * </complexType> |
|
944 * |
|
945 * DTD: |
|
946 * |
|
947 * <!ELEMENT Object (#PCDATA|Signature|SignatureProperties|Manifest %Object.ANY;)* > |
|
948 * <!ATTLIST Object Id ID #IMPLIED |
|
949 * MimeType CDATA #IMPLIED |
|
950 * Encoding CDATA #IMPLIED > |
|
951 */ |
|
952 static int |
|
953 xmlSecDSigCtxProcessObjectNode(xmlSecDSigCtxPtr dsigCtx, xmlNodePtr node) { |
|
954 xmlNodePtr cur; |
|
955 int ret; |
|
956 |
|
957 xmlSecAssert2(dsigCtx != NULL, -1); |
|
958 xmlSecAssert2(dsigCtx->status == xmlSecDSigStatusUnknown, -1); |
|
959 xmlSecAssert2(node != NULL, -1); |
|
960 |
|
961 /* we care about Manifest nodes only; ignore everything else */ |
|
962 cur = xmlSecGetNextElementNode(node->children); |
|
963 while(cur != NULL) { |
|
964 if(xmlSecCheckNodeName(cur, xmlSecNodeManifest, xmlSecDSigNs)) { |
|
965 ret = xmlSecDSigCtxProcessManifestNode(dsigCtx, cur); |
|
966 if(ret < 0){ |
|
967 xmlSecError(XMLSEC_ERRORS_HERE, |
|
968 NULL, |
|
969 "xmlSecDSigCtxProcessManifestNode", |
|
970 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
971 XMLSEC_ERRORS_NO_MESSAGE); |
|
972 return(-1); |
|
973 } |
|
974 } |
|
975 cur = xmlSecGetNextElementNode(cur->next); |
|
976 } |
|
977 return(0); |
|
978 } |
|
979 |
|
980 /** |
|
981 * xmlSecDSigCtxProcessManifestNode: |
|
982 * |
|
983 * The Manifest Element (http://www.w3.org/TR/xmldsig-core/#sec-Manifest) |
|
984 * |
|
985 * The Manifest element provides a list of References. The difference from |
|
986 * the list in SignedInfo is that it is application defined which, if any, of |
|
987 * the digests are actually checked against the objects referenced and what to |
|
988 * do if the object is inaccessible or the digest compare fails. If a Manifest |
|
989 * is pointed to from SignedInfo, the digest over the Manifest itself will be |
|
990 * checked by the core result validation behavior. The digests within such |
|
991 * a Manifest are checked at the application's discretion. If a Manifest is |
|
992 * referenced from another Manifest, even the overall digest of this two level |
|
993 * deep Manifest might not be checked. |
|
994 * |
|
995 * Schema Definition: |
|
996 * |
|
997 * <element name="Manifest" type="ds:ManifestType"/> |
|
998 * <complexType name="ManifestType"> |
|
999 * <sequence> |
|
1000 * <element ref="ds:Reference" maxOccurs="unbounded"/> |
|
1001 * </sequence> |
|
1002 * <attribute name="Id" type="ID" use="optional"/> |
|
1003 * </complexType> |
|
1004 * |
|
1005 * DTD: |
|
1006 * |
|
1007 * <!ELEMENT Manifest (Reference+) > |
|
1008 * <!ATTLIST Manifest Id ID #IMPLIED > |
|
1009 */ |
|
1010 static int |
|
1011 xmlSecDSigCtxProcessManifestNode(xmlSecDSigCtxPtr dsigCtx, xmlNodePtr node) { |
|
1012 xmlSecDSigReferenceCtxPtr dsigRefCtx; |
|
1013 xmlNodePtr cur; |
|
1014 int ret; |
|
1015 |
|
1016 xmlSecAssert2(dsigCtx != NULL, -1); |
|
1017 xmlSecAssert2(dsigCtx->status == xmlSecDSigStatusUnknown, -1); |
|
1018 xmlSecAssert2(node != NULL, -1); |
|
1019 |
|
1020 /* calculate references */ |
|
1021 cur = xmlSecGetNextElementNode(node->children); |
|
1022 while((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeReference, xmlSecDSigNs))) { |
|
1023 /* create reference */ |
|
1024 dsigRefCtx = xmlSecDSigReferenceCtxCreate(dsigCtx, xmlSecDSigReferenceOriginManifest); |
|
1025 if(dsigRefCtx == NULL) { |
|
1026 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1027 NULL, |
|
1028 "xmlSecDSigReferenceCtxCreate", |
|
1029 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
1030 XMLSEC_ERRORS_NO_MESSAGE); |
|
1031 return(-1); |
|
1032 } |
|
1033 |
|
1034 /* add to the list */ |
|
1035 ret = xmlSecPtrListAdd(&(dsigCtx->manifestReferences), dsigRefCtx); |
|
1036 if(ret < 0) { |
|
1037 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1038 NULL, |
|
1039 "xmlSecPtrListAdd", |
|
1040 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
1041 XMLSEC_ERRORS_NO_MESSAGE); |
|
1042 xmlSecDSigReferenceCtxDestroy(dsigRefCtx); |
|
1043 return(-1); |
|
1044 } |
|
1045 |
|
1046 /* process */ |
|
1047 ret = xmlSecDSigReferenceCtxProcessNode(dsigRefCtx, cur); |
|
1048 if(ret < 0) { |
|
1049 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1050 NULL, |
|
1051 "xmlSecDSigReferenceCtxProcessNode", |
|
1052 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
1053 "node=%s", |
|
1054 xmlSecErrorsSafeString(xmlSecNodeGetName(cur))); |
|
1055 return(-1); |
|
1056 } |
|
1057 |
|
1058 /* we don;t care if Reference processing failed because |
|
1059 * it's Manifest node */ |
|
1060 cur = xmlSecGetNextElementNode(cur->next); |
|
1061 } |
|
1062 |
|
1063 /* we should have nothing else here */ |
|
1064 if(cur != NULL) { |
|
1065 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1066 NULL, |
|
1067 xmlSecErrorsSafeString(xmlSecNodeGetName(cur)), |
|
1068 XMLSEC_ERRORS_R_UNEXPECTED_NODE, |
|
1069 XMLSEC_ERRORS_NO_MESSAGE); |
|
1070 return(-1); |
|
1071 } |
|
1072 return(0); |
|
1073 } |
|
1074 |
|
1075 /** |
|
1076 * xmlSecDSigCtxDebugDump: |
|
1077 * @dsigCtx: the pointer to <dsig:Signature/> processing context. |
|
1078 * @output: the pointer to output FILE. |
|
1079 * |
|
1080 * Prints the debug information about @dsigCtx to @output. |
|
1081 */ |
|
1082 EXPORT_C |
|
1083 void |
|
1084 xmlSecDSigCtxDebugDump(xmlSecDSigCtxPtr dsigCtx, FILE* output) { |
|
1085 xmlSecAssert(dsigCtx != NULL); |
|
1086 xmlSecAssert(output != NULL); |
|
1087 |
|
1088 if(dsigCtx->operation == xmlSecTransformOperationSign) { |
|
1089 fprintf(output, "= SIGNATURE CONTEXT\n"); |
|
1090 } else { |
|
1091 fprintf(output, "= VERIFICATION CONTEXT\n"); |
|
1092 } |
|
1093 switch(dsigCtx->status) { |
|
1094 case xmlSecDSigStatusUnknown: |
|
1095 fprintf(output, "== Status: unknown\n"); |
|
1096 break; |
|
1097 case xmlSecDSigStatusSucceeded: |
|
1098 fprintf(output, "== Status: succeeded\n"); |
|
1099 break; |
|
1100 case xmlSecDSigStatusInvalid: |
|
1101 fprintf(output, "== Status: invalid\n"); |
|
1102 break; |
|
1103 } |
|
1104 fprintf(output, "== flags: 0x%08x\n", dsigCtx->flags); |
|
1105 fprintf(output, "== flags2: 0x%08x\n", dsigCtx->flags2); |
|
1106 |
|
1107 if(dsigCtx->id != NULL) { |
|
1108 fprintf(output, "== Id: \"%s\"\n", dsigCtx->id); |
|
1109 } |
|
1110 |
|
1111 fprintf(output, "== Key Info Read Ctx:\n"); |
|
1112 xmlSecKeyInfoCtxDebugDump(&(dsigCtx->keyInfoReadCtx), output); |
|
1113 fprintf(output, "== Key Info Write Ctx:\n"); |
|
1114 xmlSecKeyInfoCtxDebugDump(&(dsigCtx->keyInfoWriteCtx), output); |
|
1115 |
|
1116 fprintf(output, "== Signature Transform Ctx:\n"); |
|
1117 xmlSecTransformCtxDebugDump(&(dsigCtx->transformCtx), output); |
|
1118 |
|
1119 if(dsigCtx->signMethod != NULL) { |
|
1120 fprintf(output, "== Signature Method:\n"); |
|
1121 xmlSecTransformDebugDump(dsigCtx->signMethod, output); |
|
1122 } |
|
1123 |
|
1124 if(dsigCtx->signKey != NULL) { |
|
1125 fprintf(output, "== Signature Key:\n"); |
|
1126 xmlSecKeyDebugDump(dsigCtx->signKey, output); |
|
1127 } |
|
1128 |
|
1129 fprintf(output, "== SignedInfo References List:\n"); |
|
1130 xmlSecPtrListDebugDump(&(dsigCtx->signedInfoReferences), output); |
|
1131 |
|
1132 fprintf(output, "== Manifest References List:\n"); |
|
1133 xmlSecPtrListDebugDump(&(dsigCtx->manifestReferences), output); |
|
1134 |
|
1135 if((dsigCtx->result != NULL) && |
|
1136 (xmlSecBufferGetData(dsigCtx->result) != NULL)) { |
|
1137 |
|
1138 fprintf(output, "== Result - start buffer:\n"); |
|
1139 fwrite(xmlSecBufferGetData(dsigCtx->result), |
|
1140 xmlSecBufferGetSize(dsigCtx->result), |
|
1141 1, output); |
|
1142 fprintf(output, "\n== Result - end buffer\n"); |
|
1143 } |
|
1144 if(((dsigCtx->flags & XMLSEC_DSIG_FLAGS_STORE_SIGNATURE) != 0) && |
|
1145 (xmlSecDSigCtxGetPreSignBuffer(dsigCtx) != NULL) && |
|
1146 (xmlSecBufferGetData(xmlSecDSigCtxGetPreSignBuffer(dsigCtx)) != NULL)) { |
|
1147 |
|
1148 fprintf(output, "== PreSigned data - start buffer:\n"); |
|
1149 fwrite(xmlSecBufferGetData(xmlSecDSigCtxGetPreSignBuffer(dsigCtx)), |
|
1150 xmlSecBufferGetSize(xmlSecDSigCtxGetPreSignBuffer(dsigCtx)), |
|
1151 1, output); |
|
1152 fprintf(output, "\n== PreSigned data - end buffer\n"); |
|
1153 } |
|
1154 } |
|
1155 |
|
1156 /** |
|
1157 * xmlSecDSigCtxDebugXmlDump: |
|
1158 * @dsigCtx: the pointer to <dsig:Signature/> processing context. |
|
1159 * @output: the pointer to output FILE. |
|
1160 * |
|
1161 * Prints the debug information about @dsigCtx to @output in XML format. |
|
1162 */ |
|
1163 EXPORT_C |
|
1164 void |
|
1165 xmlSecDSigCtxDebugXmlDump(xmlSecDSigCtxPtr dsigCtx, FILE* output) { |
|
1166 xmlSecAssert(dsigCtx != NULL); |
|
1167 xmlSecAssert(output != NULL); |
|
1168 |
|
1169 if(dsigCtx->operation == xmlSecTransformOperationSign) { |
|
1170 fprintf(output, "<SignatureContext \n"); |
|
1171 } else { |
|
1172 fprintf(output, "<VerificationContext \n"); |
|
1173 } |
|
1174 switch(dsigCtx->status) { |
|
1175 case xmlSecDSigStatusUnknown: |
|
1176 fprintf(output, "status=\"unknown\" >\n"); |
|
1177 break; |
|
1178 case xmlSecDSigStatusSucceeded: |
|
1179 fprintf(output, "status=\"succeeded\" >\n"); |
|
1180 break; |
|
1181 case xmlSecDSigStatusInvalid: |
|
1182 fprintf(output, "status=\"invalid\" >\n"); |
|
1183 break; |
|
1184 } |
|
1185 |
|
1186 fprintf(output, "<Flags>%08x</Flags>\n", dsigCtx->flags); |
|
1187 fprintf(output, "<Flags2>%08x</Flags2>\n", dsigCtx->flags2); |
|
1188 |
|
1189 if(dsigCtx->id != NULL) { |
|
1190 fprintf(output, "<Id>%s</Id>\n", dsigCtx->id); |
|
1191 } |
|
1192 |
|
1193 fprintf(output, "<KeyInfoReadCtx>\n"); |
|
1194 xmlSecKeyInfoCtxDebugXmlDump(&(dsigCtx->keyInfoReadCtx), output); |
|
1195 fprintf(output, "</KeyInfoReadCtx>\n"); |
|
1196 |
|
1197 fprintf(output, "<KeyInfoWriteCtx>\n"); |
|
1198 xmlSecKeyInfoCtxDebugXmlDump(&(dsigCtx->keyInfoWriteCtx), output); |
|
1199 fprintf(output, "</KeyInfoWriteCtx>\n"); |
|
1200 |
|
1201 fprintf(output, "<SignatureTransformCtx>\n"); |
|
1202 xmlSecTransformCtxDebugXmlDump(&(dsigCtx->transformCtx), output); |
|
1203 fprintf(output, "</SignatureTransformCtx>\n"); |
|
1204 |
|
1205 if(dsigCtx->signMethod != NULL) { |
|
1206 fprintf(output, "<SignatureMethod>\n"); |
|
1207 xmlSecTransformDebugXmlDump(dsigCtx->signMethod, output); |
|
1208 fprintf(output, "</SignatureMethod>\n"); |
|
1209 } |
|
1210 |
|
1211 if(dsigCtx->signKey != NULL) { |
|
1212 fprintf(output, "<SignatureKey>\n"); |
|
1213 xmlSecKeyDebugXmlDump(dsigCtx->signKey, output); |
|
1214 fprintf(output, "</SignatureKey>\n"); |
|
1215 } |
|
1216 |
|
1217 fprintf(output, "<SignedInfoReferences>\n"); |
|
1218 xmlSecPtrListDebugXmlDump(&(dsigCtx->signedInfoReferences), output); |
|
1219 fprintf(output, "</SignedInfoReferences>\n"); |
|
1220 |
|
1221 fprintf(output, "<ManifestReferences>\n"); |
|
1222 xmlSecPtrListDebugXmlDump(&(dsigCtx->manifestReferences), output); |
|
1223 fprintf(output, "</ManifestReferences>\n"); |
|
1224 |
|
1225 if((dsigCtx->result != NULL) && |
|
1226 (xmlSecBufferGetData(dsigCtx->result) != NULL)) { |
|
1227 |
|
1228 fprintf(output, "<Result>"); |
|
1229 fwrite(xmlSecBufferGetData(dsigCtx->result), |
|
1230 xmlSecBufferGetSize(dsigCtx->result), |
|
1231 1, output); |
|
1232 fprintf(output, "</Result>\n"); |
|
1233 } |
|
1234 if(((dsigCtx->flags & XMLSEC_DSIG_FLAGS_STORE_SIGNATURE) != 0) && |
|
1235 (xmlSecDSigCtxGetPreSignBuffer(dsigCtx) != NULL) && |
|
1236 (xmlSecBufferGetData(xmlSecDSigCtxGetPreSignBuffer(dsigCtx)) != NULL)) { |
|
1237 |
|
1238 fprintf(output, "<PreSignedData>"); |
|
1239 fwrite(xmlSecBufferGetData(xmlSecDSigCtxGetPreSignBuffer(dsigCtx)), |
|
1240 xmlSecBufferGetSize(xmlSecDSigCtxGetPreSignBuffer(dsigCtx)), |
|
1241 1, output); |
|
1242 fprintf(output, "</PreSignedData>\n"); |
|
1243 } |
|
1244 |
|
1245 if(dsigCtx->operation == xmlSecTransformOperationSign) { |
|
1246 fprintf(output, "</SignatureContext>\n"); |
|
1247 } else { |
|
1248 fprintf(output, "</VerificationContext>\n"); |
|
1249 } |
|
1250 } |
|
1251 |
|
1252 /************************************************************************** |
|
1253 * |
|
1254 * xmlSecDSigReferenceCtx |
|
1255 * |
|
1256 *************************************************************************/ |
|
1257 /** |
|
1258 * xmlSecDSigReferenceCtxCreate: |
|
1259 * @dsigCtx: the pointer to parent <dsig:Signature/> node processing context. |
|
1260 * @origin: the reference origin (<dsig:SignedInfo/> or <dsig:Manifest/> node). |
|
1261 * |
|
1262 * Creates new <dsig:Reference/> element processing context. Caller is responsible |
|
1263 * for destroying the returned context by calling #xmlSecDSigReferenceCtxDestroy |
|
1264 * function. |
|
1265 * |
|
1266 * Returns pointer to newly created context or NULL if an error occurs. |
|
1267 */ |
|
1268 EXPORT_C |
|
1269 xmlSecDSigReferenceCtxPtr |
|
1270 xmlSecDSigReferenceCtxCreate(xmlSecDSigCtxPtr dsigCtx, xmlSecDSigReferenceOrigin origin) { |
|
1271 xmlSecDSigReferenceCtxPtr dsigRefCtx; |
|
1272 int ret; |
|
1273 |
|
1274 xmlSecAssert2(dsigCtx != NULL, NULL); |
|
1275 |
|
1276 dsigRefCtx = (xmlSecDSigReferenceCtxPtr) xmlMalloc(sizeof(xmlSecDSigReferenceCtx)); |
|
1277 if(dsigRefCtx == NULL) { |
|
1278 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1279 NULL, |
|
1280 NULL, |
|
1281 XMLSEC_ERRORS_R_MALLOC_FAILED, |
|
1282 "sizeof(xmlSecDSigReferenceCtx)=%d", |
|
1283 sizeof(xmlSecDSigReferenceCtx)); |
|
1284 return(NULL); |
|
1285 } |
|
1286 |
|
1287 ret = xmlSecDSigReferenceCtxInitialize(dsigRefCtx, dsigCtx, origin); |
|
1288 if(ret < 0) { |
|
1289 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1290 NULL, |
|
1291 "xmlSecDSigReferenceCtxInitialize", |
|
1292 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
1293 XMLSEC_ERRORS_NO_MESSAGE); |
|
1294 xmlSecDSigReferenceCtxDestroy(dsigRefCtx); |
|
1295 return(NULL); |
|
1296 } |
|
1297 return(dsigRefCtx); |
|
1298 } |
|
1299 |
|
1300 /** |
|
1301 * xmlSecDSigReferenceCtxDestroy: |
|
1302 * @dsigRefCtx: the pointer to <dsig:Reference/> element processing context. |
|
1303 * |
|
1304 * Destroy context object created with #xmlSecDSigReferenceCtxCreate function. |
|
1305 */ |
|
1306 EXPORT_C |
|
1307 void |
|
1308 xmlSecDSigReferenceCtxDestroy(xmlSecDSigReferenceCtxPtr dsigRefCtx) { |
|
1309 xmlSecAssert(dsigRefCtx != NULL); |
|
1310 |
|
1311 xmlSecDSigReferenceCtxFinalize(dsigRefCtx); |
|
1312 xmlFree(dsigRefCtx); |
|
1313 } |
|
1314 |
|
1315 /** |
|
1316 * xmlSecDSigReferenceCtxInitialize: |
|
1317 * @dsigRefCtx: the pointer to <dsig:Reference/> element processing context. |
|
1318 * @dsigCtx: the pointer to parent <dsig:Signature/> node processing context. |
|
1319 * @origin: the reference origin (<dsig:SignedInfo/> or <dsig:Manifest/> node). |
|
1320 * |
|
1321 * Initializes new <dsig:Reference/> element processing context. Caller is responsible |
|
1322 * for cleaning up the returned context by calling #xmlSecDSigReferenceCtxFinalize |
|
1323 * function. |
|
1324 * |
|
1325 * Returns 0 on succes or aa negative value otherwise. |
|
1326 */ |
|
1327 EXPORT_C |
|
1328 int |
|
1329 xmlSecDSigReferenceCtxInitialize(xmlSecDSigReferenceCtxPtr dsigRefCtx, xmlSecDSigCtxPtr dsigCtx, |
|
1330 xmlSecDSigReferenceOrigin origin) { |
|
1331 int ret; |
|
1332 |
|
1333 xmlSecAssert2(dsigCtx != NULL, -1); |
|
1334 xmlSecAssert2(dsigRefCtx != NULL, -1); |
|
1335 |
|
1336 memset(dsigRefCtx, 0, sizeof(xmlSecDSigReferenceCtx)); |
|
1337 |
|
1338 dsigRefCtx->dsigCtx = dsigCtx; |
|
1339 dsigRefCtx->origin = origin; |
|
1340 |
|
1341 /* initializes transforms dsigRefCtx */ |
|
1342 ret = xmlSecTransformCtxInitialize(&(dsigRefCtx->transformCtx)); |
|
1343 if(ret < 0) { |
|
1344 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1345 NULL, |
|
1346 "xmlSecTransformCtxInitialize", |
|
1347 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
1348 XMLSEC_ERRORS_NO_MESSAGE); |
|
1349 return(-1); |
|
1350 } |
|
1351 |
|
1352 /* copy enabled transforms */ |
|
1353 if(dsigCtx->enabledReferenceTransforms != NULL) { |
|
1354 ret = xmlSecPtrListCopy(&(dsigRefCtx->transformCtx.enabledTransforms), |
|
1355 dsigCtx->enabledReferenceTransforms); |
|
1356 if(ret < 0) { |
|
1357 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1358 NULL, |
|
1359 "xmlSecPtrListCopy", |
|
1360 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
1361 XMLSEC_ERRORS_NO_MESSAGE); |
|
1362 return(-1); |
|
1363 } |
|
1364 } |
|
1365 dsigRefCtx->transformCtx.preExecCallback = dsigCtx->referencePreExecuteCallback; |
|
1366 dsigRefCtx->transformCtx.enabledUris = dsigCtx->enabledReferenceUris; |
|
1367 |
|
1368 if((dsigCtx->flags & XMLSEC_DSIG_FLAGS_USE_VISA3D_HACK) != 0) { |
|
1369 dsigRefCtx->transformCtx.flags |= XMLSEC_TRANSFORMCTX_FLAGS_USE_VISA3D_HACK; |
|
1370 } |
|
1371 return(0); |
|
1372 } |
|
1373 |
|
1374 /** |
|
1375 * xmlSecDSigReferenceCtxFinalize: |
|
1376 * @dsigRefCtx: the pointer to <dsig:Reference/> element processing context. |
|
1377 * |
|
1378 * Cleans up context object created with #xmlSecDSigReferenceCtxInitialize function. |
|
1379 */ |
|
1380 EXPORT_C |
|
1381 void |
|
1382 xmlSecDSigReferenceCtxFinalize(xmlSecDSigReferenceCtxPtr dsigRefCtx) { |
|
1383 xmlSecAssert(dsigRefCtx != NULL); |
|
1384 |
|
1385 xmlSecTransformCtxFinalize(&(dsigRefCtx->transformCtx)); |
|
1386 if(dsigRefCtx->id != NULL) { |
|
1387 xmlFree(dsigRefCtx->id); |
|
1388 } |
|
1389 if(dsigRefCtx->uri != NULL) { |
|
1390 xmlFree(dsigRefCtx->uri); |
|
1391 } |
|
1392 if(dsigRefCtx->type != NULL) { |
|
1393 xmlFree(dsigRefCtx->type); |
|
1394 } |
|
1395 memset(dsigRefCtx, 0, sizeof(xmlSecDSigReferenceCtx)); |
|
1396 } |
|
1397 |
|
1398 /** |
|
1399 * xmlSecDSigReferenceCtxGetPreDigestBuffer: |
|
1400 * @dsigRefCtx: the pointer to <dsig:Reference/> element processing context. |
|
1401 * |
|
1402 * Gets the results of <dsig:Reference/> node processing just before digesting |
|
1403 * (valid only if #XMLSEC_DSIG_FLAGS_STORE_SIGNEDINFO_REFERENCES or |
|
1404 * #XMLSEC_DSIG_FLAGS_STORE_MANIFEST_REFERENCES flas of signature context |
|
1405 * is set). |
|
1406 * |
|
1407 * Returns pointer to the buffer or NULL if an error occurs. |
|
1408 */ |
|
1409 EXPORT_C |
|
1410 xmlSecBufferPtr |
|
1411 xmlSecDSigReferenceCtxGetPreDigestBuffer(xmlSecDSigReferenceCtxPtr dsigRefCtx) { |
|
1412 xmlSecAssert2(dsigRefCtx != NULL, NULL); |
|
1413 |
|
1414 return((dsigRefCtx->preDigestMemBufMethod != NULL) ? |
|
1415 xmlSecTransformMemBufGetBuffer(dsigRefCtx->preDigestMemBufMethod) : NULL); |
|
1416 } |
|
1417 |
|
1418 /** |
|
1419 * xmlSecDSigReferenceCtxProcessNode: |
|
1420 * @dsigRefCtx: the pointer to <dsig:Reference/> element processing context. |
|
1421 * @node: the pointer to <dsig:Reference/> node. |
|
1422 |
|
1423 * The Reference Element (http://www.w3.org/TR/xmldsig-core/#sec-Reference) |
|
1424 * |
|
1425 * Reference is an element that may occur one or more times. It specifies |
|
1426 * a digest algorithm and digest value, and optionally an identifier of the |
|
1427 * object being signed, the type of the object, and/or a list of transforms |
|
1428 * to be applied prior to digesting. The identification (URI) and transforms |
|
1429 * describe how the digested content (i.e., the input to the digest method) |
|
1430 * was created. The Type attribute facilitates the processing of referenced |
|
1431 * data. For example, while this specification makes no requirements over |
|
1432 * external data, an application may wish to signal that the referent is a |
|
1433 * Manifest. An optional ID attribute permits a Reference to be referenced |
|
1434 * from elsewhere. |
|
1435 * |
|
1436 * Returns 0 on succes or aa negative value otherwise. |
|
1437 */ |
|
1438 EXPORT_C |
|
1439 int |
|
1440 xmlSecDSigReferenceCtxProcessNode(xmlSecDSigReferenceCtxPtr dsigRefCtx, xmlNodePtr node) { |
|
1441 xmlSecTransformCtxPtr transformCtx; |
|
1442 xmlNodePtr digestValueNode; |
|
1443 xmlNodePtr cur; |
|
1444 int ret; |
|
1445 |
|
1446 xmlSecAssert2(dsigRefCtx != NULL, -1); |
|
1447 xmlSecAssert2(dsigRefCtx->dsigCtx != NULL, -1); |
|
1448 xmlSecAssert2(dsigRefCtx->digestMethod == NULL, -1); |
|
1449 xmlSecAssert2(dsigRefCtx->digestMethod == NULL, -1); |
|
1450 xmlSecAssert2(dsigRefCtx->preDigestMemBufMethod == NULL, -1); |
|
1451 xmlSecAssert2(node != NULL, -1); |
|
1452 xmlSecAssert2(node->doc != NULL, -1); |
|
1453 |
|
1454 transformCtx = &(dsigRefCtx->transformCtx); |
|
1455 |
|
1456 /* read attributes first */ |
|
1457 dsigRefCtx->uri = xmlGetProp(node, xmlSecAttrURI); |
|
1458 dsigRefCtx->id = xmlGetProp(node, xmlSecAttrId); |
|
1459 dsigRefCtx->type= xmlGetProp(node, xmlSecAttrType); |
|
1460 |
|
1461 /* set start URI (and check that it is enabled!) */ |
|
1462 ret = xmlSecTransformCtxSetUri(transformCtx, dsigRefCtx->uri, node); |
|
1463 if(ret < 0) { |
|
1464 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1465 NULL, |
|
1466 "xmlSecTransformCtxSetUri", |
|
1467 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
1468 "uri=%s", |
|
1469 xmlSecErrorsSafeString(dsigRefCtx->uri)); |
|
1470 return(-1); |
|
1471 } |
|
1472 |
|
1473 /* first is optional Transforms node */ |
|
1474 cur = xmlSecGetNextElementNode(node->children); |
|
1475 if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeTransforms, xmlSecDSigNs))) { |
|
1476 ret = xmlSecTransformCtxNodesListRead(transformCtx, |
|
1477 cur, xmlSecTransformUsageDSigTransform); |
|
1478 if(ret < 0) { |
|
1479 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1480 NULL, |
|
1481 "xmlSecTransformCtxNodesListRead", |
|
1482 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
1483 "node=%s", |
|
1484 xmlSecErrorsSafeString(xmlSecNodeGetName(cur))); |
|
1485 return(-1); |
|
1486 } |
|
1487 cur = xmlSecGetNextElementNode(cur->next); |
|
1488 } |
|
1489 |
|
1490 /* insert membuf if requested */ |
|
1491 if(((dsigRefCtx->origin == xmlSecDSigReferenceOriginSignedInfo) && |
|
1492 ((dsigRefCtx->dsigCtx->flags & XMLSEC_DSIG_FLAGS_STORE_SIGNEDINFO_REFERENCES) != 0)) || |
|
1493 ((dsigRefCtx->origin == xmlSecDSigReferenceOriginManifest) && |
|
1494 ((dsigRefCtx->dsigCtx->flags & XMLSEC_DSIG_FLAGS_STORE_MANIFEST_REFERENCES) != 0))) { |
|
1495 |
|
1496 xmlSecAssert2(dsigRefCtx->preDigestMemBufMethod == NULL, -1); |
|
1497 dsigRefCtx->preDigestMemBufMethod = xmlSecTransformCtxCreateAndAppend( |
|
1498 transformCtx, |
|
1499 xmlSecTransformMemBufId); |
|
1500 if(dsigRefCtx->preDigestMemBufMethod == NULL) { |
|
1501 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1502 NULL, |
|
1503 "xmlSecTransformCtxCreateAndAppend", |
|
1504 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
1505 "transform=%s", |
|
1506 xmlSecErrorsSafeString(xmlSecTransformKlassGetName(xmlSecTransformMemBufId))); |
|
1507 return(-1); |
|
1508 } |
|
1509 } |
|
1510 |
|
1511 /* next node is required DigestMethod. */ |
|
1512 if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeDigestMethod, xmlSecDSigNs))) { |
|
1513 dsigRefCtx->digestMethod = xmlSecTransformCtxNodeRead(&(dsigRefCtx->transformCtx), |
|
1514 cur, xmlSecTransformUsageDigestMethod); |
|
1515 if(dsigRefCtx->digestMethod == NULL) { |
|
1516 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1517 NULL, |
|
1518 "xmlSecTransformCtxNodeRead", |
|
1519 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
1520 "node=%s", |
|
1521 xmlSecErrorsSafeString(xmlSecNodeGetName(cur))); |
|
1522 return(-1); |
|
1523 } |
|
1524 } else if(dsigRefCtx->dsigCtx->defSignMethodId != xmlSecTransformIdUnknown) { |
|
1525 /* the dsig spec does require DigestMethod node |
|
1526 * to be present but in some case it application might decide to |
|
1527 * minimize traffic */ |
|
1528 dsigRefCtx->digestMethod = xmlSecTransformCtxCreateAndAppend(&(dsigRefCtx->transformCtx), |
|
1529 dsigRefCtx->dsigCtx->defSignMethodId); |
|
1530 if(dsigRefCtx->digestMethod == NULL) { |
|
1531 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1532 NULL, |
|
1533 "xmlSecTransformCtxAppend", |
|
1534 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
1535 XMLSEC_ERRORS_NO_MESSAGE); |
|
1536 return(-1); |
|
1537 } |
|
1538 } else { |
|
1539 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1540 NULL, |
|
1541 xmlSecErrorsSafeString(xmlSecNodeGetName(cur)), |
|
1542 XMLSEC_ERRORS_R_INVALID_NODE, |
|
1543 "expected=%s", |
|
1544 xmlSecErrorsSafeString(xmlSecNodeDigestMethod)); |
|
1545 return(-1); |
|
1546 } |
|
1547 dsigRefCtx->digestMethod->operation = dsigRefCtx->dsigCtx->operation; |
|
1548 |
|
1549 /* last node is required DigestValue */ |
|
1550 if (cur != NULL) { |
|
1551 cur = xmlSecGetNextElementNode(cur->next); |
|
1552 } |
|
1553 if((cur == NULL) || (!xmlSecCheckNodeName(cur, xmlSecNodeDigestValue, xmlSecDSigNs))) { |
|
1554 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1555 NULL, |
|
1556 xmlSecErrorsSafeString(xmlSecNodeGetName(cur)), |
|
1557 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
1558 "node=%s", |
|
1559 xmlSecErrorsSafeString(xmlSecNodeDigestValue)); |
|
1560 return(-1); |
|
1561 } |
|
1562 digestValueNode = cur; |
|
1563 cur = xmlSecGetNextElementNode(cur->next); |
|
1564 |
|
1565 /* if we have something else then it's an error */ |
|
1566 if(cur != NULL) { |
|
1567 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1568 NULL, |
|
1569 xmlSecErrorsSafeString(xmlSecNodeGetName(cur)), |
|
1570 XMLSEC_ERRORS_R_UNEXPECTED_NODE, |
|
1571 XMLSEC_ERRORS_NO_MESSAGE); |
|
1572 return(-1); |
|
1573 } |
|
1574 |
|
1575 /* if we need to write result to xml node then we need base64 encode result */ |
|
1576 if(dsigRefCtx->dsigCtx->operation == xmlSecTransformOperationSign) { |
|
1577 xmlSecTransformPtr base64Encode; |
|
1578 |
|
1579 /* we need to add base64 encode transform */ |
|
1580 base64Encode = xmlSecTransformCtxCreateAndAppend(transformCtx, xmlSecTransformBase64Id); |
|
1581 if(base64Encode == NULL) { |
|
1582 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1583 NULL, |
|
1584 "xmlSecTransformCtxCreateAndAppend", |
|
1585 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
1586 XMLSEC_ERRORS_NO_MESSAGE); |
|
1587 return(-1); |
|
1588 } |
|
1589 base64Encode->operation = xmlSecTransformOperationEncode; |
|
1590 } |
|
1591 |
|
1592 /* finally get transforms results */ |
|
1593 ret = xmlSecTransformCtxExecute(transformCtx, node->doc); |
|
1594 if(ret < 0) { |
|
1595 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1596 NULL, |
|
1597 "xmlSecTransformCtxExecute", |
|
1598 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
1599 XMLSEC_ERRORS_NO_MESSAGE); |
|
1600 return(-1); |
|
1601 } |
|
1602 dsigRefCtx->result = transformCtx->result; |
|
1603 |
|
1604 if(dsigRefCtx->dsigCtx->operation == xmlSecTransformOperationSign) { |
|
1605 if((dsigRefCtx->result == NULL) || (xmlSecBufferGetData(dsigRefCtx->result) == NULL)) { |
|
1606 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1607 NULL, |
|
1608 "xmlSecTransformCtxExecute", |
|
1609 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
1610 XMLSEC_ERRORS_NO_MESSAGE); |
|
1611 return(-1); |
|
1612 } |
|
1613 |
|
1614 /* write signed data to xml */ |
|
1615 xmlNodeSetContentLen(digestValueNode, |
|
1616 xmlSecBufferGetData(dsigRefCtx->result), |
|
1617 xmlSecBufferGetSize(dsigRefCtx->result)); |
|
1618 if ( OOM_FLAG ) |
|
1619 { |
|
1620 return(-1); |
|
1621 } |
|
1622 |
|
1623 /* set success status and we are done */ |
|
1624 dsigRefCtx->status = xmlSecDSigStatusSucceeded; |
|
1625 } else { |
|
1626 /* verify SignatureValue node content */ |
|
1627 ret = xmlSecTransformVerifyNodeContent(dsigRefCtx->digestMethod, |
|
1628 digestValueNode, transformCtx); |
|
1629 if(ret < 0) { |
|
1630 xmlSecError(XMLSEC_ERRORS_HERE, |
|
1631 NULL, |
|
1632 "xmlSecTransformVerifyNodeContent", |
|
1633 XMLSEC_ERRORS_R_XMLSEC_FAILED, |
|
1634 XMLSEC_ERRORS_NO_MESSAGE); |
|
1635 return(-1); |
|
1636 } |
|
1637 |
|
1638 /* set status and we are done */ |
|
1639 if(dsigRefCtx->digestMethod->status == xmlSecTransformStatusOk) { |
|
1640 dsigRefCtx->status = xmlSecDSigStatusSucceeded; |
|
1641 } else { |
|
1642 dsigRefCtx->status = xmlSecDSigStatusInvalid; |
|
1643 } |
|
1644 } |
|
1645 |
|
1646 return(0); |
|
1647 } |
|
1648 |
|
1649 /** |
|
1650 * xmlSecDSigReferenceCtxDebugDump: |
|
1651 * @dsigRefCtx: the pointer to <dsig:Reference/> element processing context. |
|
1652 * @output: the pointer to output FILE. |
|
1653 * |
|
1654 * Prints debug information about @dsigRefCtx to @output. |
|
1655 */ |
|
1656 EXPORT_C |
|
1657 void |
|
1658 xmlSecDSigReferenceCtxDebugDump(xmlSecDSigReferenceCtxPtr dsigRefCtx, FILE* output) { |
|
1659 xmlSecAssert(dsigRefCtx != NULL); |
|
1660 xmlSecAssert(dsigRefCtx->dsigCtx != NULL); |
|
1661 xmlSecAssert(output != NULL); |
|
1662 |
|
1663 if(dsigRefCtx->dsigCtx->operation == xmlSecTransformOperationSign) { |
|
1664 fprintf(output, "= REFERENCE CALCULATION CONTEXT\n"); |
|
1665 } else { |
|
1666 fprintf(output, "= REFERENCE VERIFICATION CONTEXT\n"); |
|
1667 } |
|
1668 switch(dsigRefCtx->status) { |
|
1669 case xmlSecDSigStatusUnknown: |
|
1670 fprintf(output, "== Status: unknown\n"); |
|
1671 break; |
|
1672 case xmlSecDSigStatusSucceeded: |
|
1673 fprintf(output, "== Status: succeeded\n"); |
|
1674 break; |
|
1675 case xmlSecDSigStatusInvalid: |
|
1676 fprintf(output, "== Status: invalid\n"); |
|
1677 break; |
|
1678 } |
|
1679 if(dsigRefCtx->id != NULL) { |
|
1680 fprintf(output, "== Id: \"%s\"\n", dsigRefCtx->id); |
|
1681 } |
|
1682 if(dsigRefCtx->uri != NULL) { |
|
1683 fprintf(output, "== URI: \"%s\"\n", dsigRefCtx->uri); |
|
1684 } |
|
1685 if(dsigRefCtx->type != NULL) { |
|
1686 fprintf(output, "== Type: \"%s\"\n", dsigRefCtx->type); |
|
1687 } |
|
1688 |
|
1689 fprintf(output, "== Reference Transform Ctx:\n"); |
|
1690 xmlSecTransformCtxDebugDump(&(dsigRefCtx->transformCtx), output); |
|
1691 |
|
1692 if(dsigRefCtx->digestMethod != NULL) { |
|
1693 fprintf(output, "== Digest Method:\n"); |
|
1694 xmlSecTransformDebugDump(dsigRefCtx->digestMethod, output); |
|
1695 } |
|
1696 |
|
1697 if((xmlSecDSigReferenceCtxGetPreDigestBuffer(dsigRefCtx) != NULL) && |
|
1698 (xmlSecBufferGetData(xmlSecDSigReferenceCtxGetPreDigestBuffer(dsigRefCtx)) != NULL)) { |
|
1699 |
|
1700 fprintf(output, "== PreDigest data - start buffer:\n"); |
|
1701 fwrite(xmlSecBufferGetData(xmlSecDSigReferenceCtxGetPreDigestBuffer(dsigRefCtx)), |
|
1702 xmlSecBufferGetSize(xmlSecDSigReferenceCtxGetPreDigestBuffer(dsigRefCtx)), |
|
1703 1, output); |
|
1704 fprintf(output, "\n== PreDigest data - end buffer\n"); |
|
1705 } |
|
1706 |
|
1707 if((dsigRefCtx->result != NULL) && |
|
1708 (xmlSecBufferGetData(dsigRefCtx->result) != NULL)) { |
|
1709 |
|
1710 fprintf(output, "== Result - start buffer:\n"); |
|
1711 fwrite(xmlSecBufferGetData(dsigRefCtx->result), |
|
1712 xmlSecBufferGetSize(dsigRefCtx->result), 1, |
|
1713 output); |
|
1714 fprintf(output, "\n== Result - end buffer\n"); |
|
1715 } |
|
1716 } |
|
1717 |
|
1718 /** |
|
1719 * xmlSecDSigReferenceCtxDebugXmlDump: |
|
1720 * @dsigRefCtx: the pointer to <dsig:Reference/> element processing context. |
|
1721 * @output: the pointer to output FILE. |
|
1722 * |
|
1723 * Prints debug information about @dsigRefCtx to @output in output format. |
|
1724 */ |
|
1725 EXPORT_C |
|
1726 void |
|
1727 xmlSecDSigReferenceCtxDebugXmlDump(xmlSecDSigReferenceCtxPtr dsigRefCtx, FILE* output) { |
|
1728 xmlSecAssert(dsigRefCtx != NULL); |
|
1729 xmlSecAssert(dsigRefCtx->dsigCtx != NULL); |
|
1730 xmlSecAssert(output != NULL); |
|
1731 |
|
1732 if(dsigRefCtx->dsigCtx->operation == xmlSecTransformOperationSign) { |
|
1733 fprintf(output, "<ReferenceCalculationContext "); |
|
1734 } else { |
|
1735 fprintf(output, "<ReferenceVerificationContext "); |
|
1736 } |
|
1737 switch(dsigRefCtx->status) { |
|
1738 case xmlSecDSigStatusUnknown: |
|
1739 fprintf(output, "status=\"unknown\" >\n"); |
|
1740 break; |
|
1741 case xmlSecDSigStatusSucceeded: |
|
1742 fprintf(output, "status=\"succeeded\" >\n"); |
|
1743 break; |
|
1744 case xmlSecDSigStatusInvalid: |
|
1745 fprintf(output, "status=\"invalid\" >\n"); |
|
1746 break; |
|
1747 } |
|
1748 |
|
1749 if(dsigRefCtx->id != NULL) { |
|
1750 fprintf(output, "<Id>%s</Id>\n", dsigRefCtx->id); |
|
1751 } |
|
1752 if(dsigRefCtx->uri != NULL) { |
|
1753 fprintf(output, "<URI>%s</URI>\n", dsigRefCtx->uri); |
|
1754 } |
|
1755 if(dsigRefCtx->type != NULL) { |
|
1756 fprintf(output, "<Type>%s</Type>\n", dsigRefCtx->type); |
|
1757 } |
|
1758 |
|
1759 fprintf(output, "<ReferenceTransformCtx>\n"); |
|
1760 xmlSecTransformCtxDebugXmlDump(&(dsigRefCtx->transformCtx), output); |
|
1761 fprintf(output, "</ReferenceTransformCtx>\n"); |
|
1762 |
|
1763 if(dsigRefCtx->digestMethod != NULL) { |
|
1764 fprintf(output, "<DigestMethod>\n"); |
|
1765 xmlSecTransformDebugXmlDump(dsigRefCtx->digestMethod, output); |
|
1766 fprintf(output, "</DigestMethod>\n"); |
|
1767 } |
|
1768 |
|
1769 if((dsigRefCtx->result != NULL) && |
|
1770 (xmlSecBufferGetData(dsigRefCtx->result) != NULL)) { |
|
1771 |
|
1772 fprintf(output, "<Result>"); |
|
1773 fwrite(xmlSecBufferGetData(dsigRefCtx->result), |
|
1774 xmlSecBufferGetSize(dsigRefCtx->result), 1, |
|
1775 output); |
|
1776 fprintf(output, "</Result>\n"); |
|
1777 } |
|
1778 |
|
1779 if((xmlSecDSigReferenceCtxGetPreDigestBuffer(dsigRefCtx) != NULL) && |
|
1780 (xmlSecBufferGetData(xmlSecDSigReferenceCtxGetPreDigestBuffer(dsigRefCtx)) != NULL)) { |
|
1781 |
|
1782 fprintf(output, "<PreDigestData>"); |
|
1783 fwrite(xmlSecBufferGetData(xmlSecDSigReferenceCtxGetPreDigestBuffer(dsigRefCtx)), |
|
1784 xmlSecBufferGetSize(xmlSecDSigReferenceCtxGetPreDigestBuffer(dsigRefCtx)), |
|
1785 1, output); |
|
1786 fprintf(output, "</PreDigestData>\n"); |
|
1787 } |
|
1788 if(dsigRefCtx->dsigCtx->operation == xmlSecTransformOperationSign) { |
|
1789 fprintf(output, "</ReferenceCalculationContext>\n"); |
|
1790 } else { |
|
1791 fprintf(output, "</ReferenceVerificationContext>\n"); |
|
1792 } |
|
1793 } |
|
1794 |
|
1795 |
|
1796 /************************************************************************** |
|
1797 * |
|
1798 * xmlSecDSigReferenceCtxListKlass |
|
1799 * |
|
1800 *************************************************************************/ |
|
1801 static xmlSecPtrListKlass xmlSecDSigReferenceCtxListKlass = { |
|
1802 BAD_CAST "dsig-reference-list", |
|
1803 NULL, /* xmlSecPtrDuplicateItemMethod duplicateItem; */ |
|
1804 (xmlSecPtrDestroyItemMethod)xmlSecDSigReferenceCtxDestroy, /* xmlSecPtrDestroyItemMethod destroyItem; */ |
|
1805 (xmlSecPtrDebugDumpItemMethod)xmlSecDSigReferenceCtxDebugDump, /* xmlSecPtrDebugDumpItemMethod debugDumpItem; */ |
|
1806 (xmlSecPtrDebugDumpItemMethod)xmlSecDSigReferenceCtxDebugXmlDump, /* xmlSecPtrDebugDumpItemMethod debugXmlDumpItem; */ |
|
1807 }; |
|
1808 |
|
1809 /** |
|
1810 * xmlSecDSigReferenceCtxListGetKlass: |
|
1811 * |
|
1812 * The <dsig:Reference/> element processing contexts list klass. |
|
1813 * |
|
1814 * Returns <dsig:Reference/> element processing context list klass. |
|
1815 */ |
|
1816 EXPORT_C |
|
1817 xmlSecPtrListId |
|
1818 xmlSecDSigReferenceCtxListGetKlass(void) { |
|
1819 return(&xmlSecDSigReferenceCtxListKlass); |
|
1820 } |
|
1821 |
|
1822 #endif /* XMLSEC_NO_XMLDSIG */ |
|
1823 |
|
1824 |