/
githubmirror
/
xmlsec
Обзор
Документация
Войти
/
githubmirror
/
xmlsec
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/xmldsig.c
1 625 строк
60 KB
lsh123
(xmlsec-openssl) Added experimental ML-KEM support (#1137)
22 апр 2026, 05:15
Не верифицирован
22 апр 2026, 05:15
941a264
Код
Авторство
О чём код?
/** * XML Security Library (http://www.aleksey.com/xmlsec). * * This is free software; see the Copyright file in the source distribution for precise wording. * * Copyright (C) 2002-2026 Aleksey Sanin <aleksey@aleksey.com>. All Rights Reserved. */ /** * @addtogroup xmlsec_core_xmldsig * @brief XML Digital Signature functions. * [XML Digital Signature](http://www.w3.org/TR/xmldsig-core/) implementation. */ #include "globals.h" #ifndef XMLSEC_NO_XMLDSIG #include <stdlib.h> #include <stdio.h> #include <string.h> #include <libxml/tree.h> #include <libxml/parser.h> #include <xmlsec/xmlsec.h> #include <xmlsec/buffer.h> #include <xmlsec/xmltree.h> #include <xmlsec/keys.h> #include <xmlsec/keysmngr.h> #include <xmlsec/transforms.h> #include <xmlsec/membuf.h> #include <xmlsec/xmldsig.h> #include <xmlsec/errors.h> #include "cast_helpers.h" /****************************************************************************** * * xmlSecDSigCtx * *****************************************************************************/ static int xmlSecDSigCtxProcessSignatureNode (xmlSecDSigCtxPtr dsigCtx, xmlNodePtr node); static int xmlSecDSigCtxProcessSignedInfoNode (xmlSecDSigCtxPtr dsigCtx, xmlNodePtr node, xmlNodePtr * firstReferenceNode); static int xmlSecDSigCtxProcessKeyInfoNode (xmlSecDSigCtxPtr dsigCtx, xmlNodePtr node); static int xmlSecDSigCtxProcessObjectNode (xmlSecDSigCtxPtr dsigCtx, xmlNodePtr node); static int xmlSecDSigCtxProcessManifestNode (xmlSecDSigCtxPtr dsigCtx, xmlNodePtr node); static int xmlSecDSigCtxProcessReferences (xmlSecDSigCtxPtr dsigCtx, xmlNodePtr firstReferenceNode); static void xmlSecDSigCtxMarkAsSucceeded (xmlSecDSigCtxPtr dsigCtx); static void xmlSecDSigCtxMarkAsFailed (xmlSecDSigCtxPtr dsigCtx, xmlSecDSigFailureReason failureReason); /* The ID attribute in XMLDSig is 'Id' */ static const xmlChar* xmlSecDSigIds[] = { xmlSecAttrId, NULL }; /** * @brief Creates a dsig:Signature processing context. * @details Creates <dsig:Signature/> element processing context. * The caller is responsible for destroying returned object by calling * #xmlSecDSigCtxDestroy function. * * @param keysMngr the pointer to keys manager. * @return pointer to newly allocated context object or NULL if an error * occurs. */ xmlSecDSigCtxPtr xmlSecDSigCtxCreate(xmlSecKeysMngrPtr keysMngr) { xmlSecDSigCtxPtr dsigCtx; int ret; dsigCtx = (xmlSecDSigCtxPtr) xmlMalloc(sizeof(xmlSecDSigCtx)); if(dsigCtx == NULL) { xmlSecMallocError(sizeof(xmlSecDSigCtx), NULL); return(NULL); } ret = xmlSecDSigCtxInitialize(dsigCtx, keysMngr); if(ret < 0) { xmlSecInternalError("xmlSecDSigCtxInitialize", NULL); xmlSecDSigCtxDestroy(dsigCtx); return(NULL); } return(dsigCtx); } /** * @brief Destroys a dsig:Signature processing context. * @details Destroy context object created with #xmlSecDSigCtxCreate function. * @param dsigCtx the pointer to <dsig:Signature/> processing context. */ void xmlSecDSigCtxDestroy(xmlSecDSigCtxPtr dsigCtx) { xmlSecAssert(dsigCtx != NULL); xmlSecDSigCtxFinalize(dsigCtx); xmlFree(dsigCtx); } /** * @brief Initializes a dsig:Signature processing context. * @details Initializes <dsig:Signature/> element processing context. * The caller is responsible for cleaning up returned object by calling * #xmlSecDSigCtxFinalize function. * * @param dsigCtx the pointer to <dsig:Signature/> processing context. * @param keysMngr the pointer to keys manager. * @return 0 on success or a negative value if an error occurs. */ int xmlSecDSigCtxInitialize(xmlSecDSigCtxPtr dsigCtx, xmlSecKeysMngrPtr keysMngr) { int ret; xmlSecAssert2(dsigCtx != NULL, -1); memset(dsigCtx, 0, sizeof(xmlSecDSigCtx)); /* initialize key info */ ret = xmlSecKeyInfoCtxInitialize(&(dsigCtx->keyInfoReadCtx), keysMngr); if(ret < 0) { xmlSecInternalError("xmlSecKeyInfoCtxInitialize", NULL); return(-1); } dsigCtx->keyInfoReadCtx.mode = xmlSecKeyInfoModeRead; ret = xmlSecKeyInfoCtxInitialize(&(dsigCtx->keyInfoWriteCtx), keysMngr); if(ret < 0) { xmlSecInternalError("xmlSecKeyInfoCtxInitialize", NULL); return(-1); } dsigCtx->keyInfoWriteCtx.mode = xmlSecKeyInfoModeWrite; /* it's not wise to write private key :) */ dsigCtx->keyInfoWriteCtx.keyReq.keyType = xmlSecKeyDataTypePublic; /* initializes transforms dsigCtx */ ret = xmlSecTransformCtxInitialize(&(dsigCtx->transformCtx)); if(ret < 0) { xmlSecInternalError("xmlSecTransformCtxInitialize", NULL); return(-1); } /* references lists from SignedInfo and Manifest elements */ ret = xmlSecPtrListInitialize(&(dsigCtx->signedInfoReferences), xmlSecDSigReferenceCtxListId); if(ret != 0) { xmlSecInternalError("xmlSecPtrListInitialize", NULL); return(ret); } ret = xmlSecPtrListInitialize(&(dsigCtx->manifestReferences), xmlSecDSigReferenceCtxListId); if(ret != 0) { xmlSecInternalError("xmlSecPtrListInitialize", NULL); return(ret); } dsigCtx->enabledReferenceUris = xmlSecTransformUriTypeAny; return(0); } /** * @brief Cleans up a dsig:Signature processing context. * @details Cleans up @p dsigCtx object initialized with #xmlSecDSigCtxInitialize function. * @param dsigCtx the pointer to <dsig:Signature/> processing context. */ void xmlSecDSigCtxFinalize(xmlSecDSigCtxPtr dsigCtx) { xmlSecAssert(dsigCtx != NULL); xmlSecTransformCtxFinalize(&(dsigCtx->transformCtx)); xmlSecKeyInfoCtxFinalize(&(dsigCtx->keyInfoReadCtx)); xmlSecKeyInfoCtxFinalize(&(dsigCtx->keyInfoWriteCtx)); xmlSecPtrListFinalize(&(dsigCtx->signedInfoReferences)); xmlSecPtrListFinalize(&(dsigCtx->manifestReferences)); if(dsigCtx->enabledReferenceTransforms != NULL) { xmlSecPtrListDestroy(dsigCtx->enabledReferenceTransforms); } if(dsigCtx->signKey != NULL) { xmlSecKeyDestroy(dsigCtx->signKey); } if(dsigCtx->id != NULL) { xmlFree(dsigCtx->id); } memset(dsigCtx, 0, sizeof(xmlSecDSigCtx)); } /** * @brief Enables a transform for dsig:Reference processing. * @details Enables @p transformId for <dsig:Reference/> elements processing. * @param dsigCtx the pointer to <dsig:Signature/> processing context. * @param transformId the transform klass. * @return 0 on success or a negative value if an error occurs. */ int xmlSecDSigCtxEnableReferenceTransform(xmlSecDSigCtxPtr dsigCtx, xmlSecTransformId transformId) { int ret; xmlSecAssert2(dsigCtx != NULL, -1); xmlSecAssert2(dsigCtx->result == NULL, -1); xmlSecAssert2(transformId != xmlSecTransformIdUnknown, -1); if(dsigCtx->enabledReferenceTransforms == NULL) { dsigCtx->enabledReferenceTransforms = xmlSecPtrListCreate(xmlSecTransformIdListId); if(dsigCtx->enabledReferenceTransforms == NULL) { xmlSecInternalError("xmlSecPtrListCreate", NULL); return(-1); } } ret = xmlSecPtrListAdd(dsigCtx->enabledReferenceTransforms, (void*)transformId); if(ret < 0) { xmlSecInternalError("xmlSecPtrListAdd", NULL); return(-1); } return(0); } /** * @brief Enables a transform for dsig:SignedInfo processing. * @details Enables @p transformId for <dsig:SignedInfo/> element processing. * @param dsigCtx the pointer to <dsig:Signature/> processing context. * @param transformId the transform klass. * @return 0 on success or a negative value if an error occurs. */ int xmlSecDSigCtxEnableSignatureTransform(xmlSecDSigCtxPtr dsigCtx, xmlSecTransformId transformId) { xmlSecAssert2(dsigCtx != NULL, -1); xmlSecAssert2(dsigCtx->result == NULL, -1); xmlSecAssert2(transformId != xmlSecTransformIdUnknown, -1); return(xmlSecPtrListAdd(&(dsigCtx->transformCtx.enabledTransforms), (void*)transformId)); } /** * @brief Gets the pre-signature buffer for the SignedInfo element. * @details Gets pointer to the buffer with serialized <dsig:SignedInfo/> element * just before signature claculation (valid if and only if * #XMLSEC_DSIG_FLAGS_STORE_SIGNATURE context flag is set. * * @param dsigCtx the pointer to <dsig:Signature/> processing context. * @return 0 on success or a negative value if an error occurs. */ xmlSecBufferPtr xmlSecDSigCtxGetPreSignBuffer(xmlSecDSigCtxPtr dsigCtx) { xmlSecAssert2(dsigCtx != NULL, NULL); return((dsigCtx->preSignMemBufMethod != NULL) ? xmlSecTransformMemBufGetBuffer(dsigCtx->preSignMemBufMethod) : NULL); } /** * @brief Signs the data as described in @p tmpl node. * @param dsigCtx the pointer to <dsig:Signature/> processing context. * @param tmpl the pointer to <dsig:Signature/> node with signature template. * @return 0 on success or a negative value if an error occurs. */ int xmlSecDSigCtxSign(xmlSecDSigCtxPtr dsigCtx, xmlNodePtr tmpl) { xmlSecByte* outBuf; xmlSecSize outSize; int outLen; int ret; xmlSecAssert2(dsigCtx != NULL, -1); xmlSecAssert2(dsigCtx->result == NULL, -1); xmlSecAssert2(tmpl != NULL, -1); xmlSecAssert2(tmpl->doc != NULL, -1); /* add ids for Signature nodes */ dsigCtx->operation = xmlSecTransformOperationSign; dsigCtx->status = xmlSecDSigStatusUnknown; dsigCtx->keyInfoReadCtx.operation = xmlSecTransformOperationSign; dsigCtx->keyInfoWriteCtx.operation = xmlSecTransformOperationSign; xmlSecAddIDs(tmpl->doc, tmpl, xmlSecDSigIds); /* read signature template */ ret = xmlSecDSigCtxProcessSignatureNode(dsigCtx, tmpl); if(ret < 0) { xmlSecInternalError("xmlSecDSigCtxProcessSignatureNode", NULL); return(-1); } xmlSecAssert2(dsigCtx->signMethod != NULL, -1); xmlSecAssert2(dsigCtx->signValueNode != NULL, -1); /* references processing might change the status */ if(dsigCtx->status != xmlSecDSigStatusUnknown) { return(0); } /* check what we've got */ dsigCtx->result = dsigCtx->transformCtx.result; if((dsigCtx->result == NULL) || (xmlSecBufferGetData(dsigCtx->result) == NULL)) { xmlSecOtherError(XMLSEC_ERRORS_R_INVALID_RESULT, NULL, NULL); return(-1); } /* write signed data to xml */ outBuf = xmlSecBufferGetData(dsigCtx->result); outSize = xmlSecBufferGetSize(dsigCtx->result); XMLSEC_SAFE_CAST_SIZE_TO_INT(outSize, outLen, return(-1), NULL); xmlNodeSetContentLen(dsigCtx->signValueNode, outBuf, outLen); /* set success status and we are done */ xmlSecDSigCtxMarkAsSucceeded(dsigCtx); return(0); } /** * @brief Validates the dsig:Signature in the node. * @details Validates signature in the @p node. The verification result is returned * in @p status member of the @p dsigCtx object. * * @param dsigCtx the pointer to <dsig:Signature/> processing context. * @param node the pointer with <dsig:Signature/> node. * @return 0 on success (check @p status member of @p dsigCtx to get * signature verification result) or a negative value if an error occurs. */ int xmlSecDSigCtxVerify(xmlSecDSigCtxPtr dsigCtx, xmlNodePtr node) { int ret; xmlSecAssert2(dsigCtx != NULL, -1); xmlSecAssert2(node != NULL, -1); xmlSecAssert2(node->doc != NULL, -1); /* add ids for Signature nodes */ dsigCtx->operation = xmlSecTransformOperationVerify; dsigCtx->status = xmlSecDSigStatusUnknown; dsigCtx->keyInfoReadCtx.operation = xmlSecTransformOperationVerify; dsigCtx->keyInfoWriteCtx.operation = xmlSecTransformOperationVerify; xmlSecAddIDs(node->doc, node, xmlSecDSigIds); /* read signature info */ ret = xmlSecDSigCtxProcessSignatureNode(dsigCtx, node); if(ret < 0) { xmlSecInternalError("xmlSecDSigCtxProcessSignatureNode", NULL); return(-1); } xmlSecAssert2(dsigCtx->signMethod != NULL, -1); xmlSecAssert2(dsigCtx->signValueNode != NULL, -1); /* references processing might change the status */ if(dsigCtx->status != xmlSecDSigStatusUnknown) { return(0); } /* verify SignatureValue node content */ ret = xmlSecTransformVerifyNodeContent(dsigCtx->signMethod, dsigCtx->signValueNode, &(dsigCtx->transformCtx)); if(ret < 0) { xmlSecInternalError("xmlSecTransformVerifyNodeContent", NULL); return(-1); } /* set status and we are done */ if(dsigCtx->signMethod->status == xmlSecTransformStatusOk) { xmlSecDSigCtxMarkAsSucceeded(dsigCtx); } else { xmlSecDSigCtxMarkAsFailed(dsigCtx, xmlSecDSigFailureReasonSignature); } return(0); } static void xmlSecDSigCtxMarkAsSucceeded(xmlSecDSigCtxPtr dsigCtx) { xmlSecAssert(dsigCtx != NULL); dsigCtx->status = xmlSecDSigStatusSucceeded; } static void xmlSecDSigCtxMarkAsFailed(xmlSecDSigCtxPtr dsigCtx, xmlSecDSigFailureReason failureReason) { xmlSecAssert(dsigCtx != NULL); dsigCtx->status = xmlSecDSigStatusInvalid; if(dsigCtx->failureReason == xmlSecDSigFailureReasonUnknown) { dsigCtx->failureReason = failureReason; } } /* * xmlSecDSigCtxProcessSignatureNode: * * The Signature element (http://www.w3.org/TR/xmldsig-core/#sec-Signature) * * The Signature element is the root element of an XML Signature. * Implementation MUST generate laxly schema valid [XML-schema] Signature * elements as specified by the following schema: * The way in which the SignedInfo element is presented to the * canonicalization method is dependent on that method. The following * applies to algorithms which process XML as nodes or characters: * * - XML based canonicalization implementations MUST be provided with * a [XPath] node-set originally formed from the document containing * the SignedInfo and currently indicating the SignedInfo, its descendants, * and the attribute and namespace nodes of SignedInfo and its descendant * elements. * * - Text based canonicalization algorithms (such as CRLF and charset * normalization) should be provided with the UTF-8 octets that represent * the well-formed SignedInfo element, from the first character to the * last character of the XML representation, inclusive. This includes * the entire text of the start and end tags of the SignedInfo element * as well as all descendant markup and character data (i.e., the text) * between those tags. Use of text based canonicalization of SignedInfo * is NOT RECOMMENDED. * * ================================= * we do not support any non XML based C14N * * Schema Definition: * * <element name="Signature" type="ds:SignatureType"/> * <complexType name="SignatureType"> * <sequence> * <element ref="ds:SignedInfo"/> * <element ref="ds:SignatureValue"/> * <element ref="ds:KeyInfo" minOccurs="0"/> * <element ref="ds:Object" minOccurs="0" maxOccurs="unbounded"/> * </sequence> <attribute name="Id" type="ID" use="optional"/> * </complexType> * * DTD: * * <!ELEMENT Signature (SignedInfo, SignatureValue, KeyInfo?, Object*) > * <!ATTLIST Signature * xmlns CDATA #FIXED 'http://www.w3.org/2000/09/xmldsig#' * Id ID #IMPLIED > * */ static int xmlSecDSigCtxProcessSignatureNode(xmlSecDSigCtxPtr dsigCtx, xmlNodePtr node) { xmlSecTransformDataType firstType; xmlNodePtr signedInfoNode = NULL; xmlNodePtr keyInfoNode = NULL; xmlNodePtr firstReferenceNode = NULL; xmlNodePtr cur; int ret; xmlSecAssert2(dsigCtx != NULL, -1); xmlSecAssert2((dsigCtx->operation == xmlSecTransformOperationSign) || (dsigCtx->operation == xmlSecTransformOperationVerify), -1); xmlSecAssert2(dsigCtx->status == xmlSecDSigStatusUnknown, -1); xmlSecAssert2(dsigCtx->signValueNode == NULL, -1); xmlSecAssert2(dsigCtx->signMethod == NULL, -1); xmlSecAssert2(dsigCtx->c14nMethod == NULL, -1); xmlSecAssert2(node != NULL, -1); if(!xmlSecCheckNodeName(node, xmlSecNodeSignature, xmlSecDSigNs)) { xmlSecInvalidNodeError(node, xmlSecNodeSignature, NULL); return(-1); } /* setup transforms */ if((dsigCtx->flags & XMLSEC_DSIG_FLAGS_USE_ASN1_SIGNATURE_VALUES) != 0) { dsigCtx->transformCtx.flags |= XMLSEC_TRANSFORMCTX_FLAGS_SUPPORT_ASN1_SIGNATURE_VALUES; } /* read node data */ xmlSecAssert2(dsigCtx->id == NULL, -1); dsigCtx->id = xmlGetProp(node, xmlSecAttrId); /* first node is required SignedInfo */ cur = xmlSecGetNextElementNode(node->children); if((cur == NULL) || (!xmlSecCheckNodeName(cur, xmlSecNodeSignedInfo, xmlSecDSigNs))) { xmlSecInvalidNodeError(cur, xmlSecNodeSignedInfo, NULL); return(-1); } signedInfoNode = cur; cur = xmlSecGetNextElementNode(cur->next); /* next node is required SignatureValue */ if((cur == NULL) || (!xmlSecCheckNodeName(cur, xmlSecNodeSignatureValue, xmlSecDSigNs))) { xmlSecInvalidNodeError(cur, xmlSecNodeSignatureValue, NULL); return(-1); } dsigCtx->signValueNode = cur; cur = xmlSecGetNextElementNode(cur->next); /* next node is optional KeyInfo */ if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeKeyInfo, xmlSecDSigNs))) { keyInfoNode = cur; cur = xmlSecGetNextElementNode(cur->next); } else { keyInfoNode = NULL; } /* next nodes are optional Object nodes */ while((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeObject, xmlSecDSigNs))) { /* read manifests from objects */ if((dsigCtx->flags & XMLSEC_DSIG_FLAGS_IGNORE_MANIFESTS) == 0) { ret = xmlSecDSigCtxProcessObjectNode(dsigCtx, cur); if(ret < 0) { xmlSecInternalError("xmlSecDSigCtxProcessObjectNode", NULL); return(-1); } } cur = xmlSecGetNextElementNode(cur->next); } /* if there is something left than it's an error */ if(cur != NULL) { xmlSecUnexpectedNodeError(cur, NULL); return(-1); } /* now validated all the references and prepare transform */ ret = xmlSecDSigCtxProcessSignedInfoNode(dsigCtx, signedInfoNode, &firstReferenceNode); if(ret < 0) { xmlSecInternalError("xmlSecDSigCtxProcessSignedInfoNode", NULL); return(-1); } /* as the result, we should have sign and c14n methods set */ xmlSecAssert2(dsigCtx->signMethod != NULL, -1); xmlSecAssert2(dsigCtx->c14nMethod != NULL, -1); /* now read key info node */ ret = xmlSecDSigCtxProcessKeyInfoNode(dsigCtx, keyInfoNode); if(ret < 0) { xmlSecInternalError("xmlSecDSigCtxProcessKeyInfoNode", NULL); return(-1); } /* as the result, we should have a key */ xmlSecAssert2(dsigCtx->signKey != NULL, -1); /* now actually process references and calculate digests */ ret = xmlSecDSigCtxProcessReferences(dsigCtx, firstReferenceNode); if(ret < 0) { xmlSecInternalError("xmlSecDSigCtxProcessReferences", NULL); return(-1); } /* references processing might change the status */ if(dsigCtx->status != xmlSecDSigStatusUnknown) { return(0); } /* if we need to write result to xml node then we need base64 encode result */ if(dsigCtx->operation == xmlSecTransformOperationSign) { xmlSecTransformPtr base64Encode; /* we need to add base64 encode transform */ base64Encode = xmlSecTransformCtxCreateAndAppend(&(dsigCtx->transformCtx), xmlSecTransformBase64Id); if(base64Encode == NULL) { xmlSecInternalError("xmlSecTransformCtxCreateAndAppend", NULL); return(-1); } base64Encode->operation = xmlSecTransformOperationEncode; } firstType = xmlSecTransformGetDataType(dsigCtx->transformCtx.first, xmlSecTransformModePush, &(dsigCtx->transformCtx)); if((firstType & xmlSecTransformDataTypeXml) != 0) { xmlSecNodeSetPtr nodeset = NULL; xmlSecAssert2(signedInfoNode != NULL, -1); nodeset = xmlSecNodeSetGetChildren(signedInfoNode->doc, signedInfoNode, 1, 0); if(nodeset == NULL) { xmlSecInternalError("xmlSecNodeSetGetChildren(signedInfoNode)", NULL); return(-1); } /* calculate the signature */ ret = xmlSecTransformCtxXmlExecute(&(dsigCtx->transformCtx), nodeset); if(ret < 0) { xmlSecInternalError("xmlSecTransformCtxXmlExecute", NULL); xmlSecNodeSetDestroy(nodeset); return(-1); } xmlSecNodeSetDestroy(nodeset); } else { xmlSecNotImplementedError("Binary c14n transforms are not supported"); return(-1); } return(0); } /** * @brief Processes the dsig:SignedInfo element. * @details The SignedInfo Element (http://www.w3.org/TR/xmldsig-core/#sec-SignedInfo) * * The structure of SignedInfo includes the canonicalization algorithm, * a result algorithm, and one or more references. The SignedInfo element * may contain an optional ID attribute that will allow it to be referenced by * other signatures and objects. * * SignedInfo does not include explicit result or digest properties (such as * calculation time, cryptographic device serial number, etc.). If an * application needs to associate properties with the result or digest, * it may include such information in a SignatureProperties element within * an Object element. * * Schema Definition: * @code{.xml} * <element name="SignedInfo" type="ds:SignedInfoType"/> * <complexType name="SignedInfoType"> * <sequence> * <element ref="ds:CanonicalizationMethod"/> * <element ref="ds:SignatureMethod"/> * <element ref="ds:Reference" maxOccurs="unbounded"/> * </sequence> * <attribute name="Id" type="ID" use="optional"/> * </complexType> * * DTD: * * <!ELEMENT SignedInfo (CanonicalizationMethod, SignatureMethod, Reference+) > * <!ATTLIST SignedInfo Id ID #IMPLIED> * @endcode */ static int xmlSecDSigCtxProcessSignedInfoNode(xmlSecDSigCtxPtr dsigCtx, xmlNodePtr node, xmlNodePtr * firstReferenceNode) { xmlSecSize refNodesCount = 0; xmlNodePtr cur; xmlSecAssert2(dsigCtx != NULL, -1); xmlSecAssert2(dsigCtx->status == xmlSecDSigStatusUnknown, -1); xmlSecAssert2(dsigCtx->signMethod == NULL, -1); xmlSecAssert2(dsigCtx->c14nMethod == NULL, -1); xmlSecAssert2((dsigCtx->operation == xmlSecTransformOperationSign) || (dsigCtx->operation == xmlSecTransformOperationVerify), -1); xmlSecAssert2(node != NULL, -1); xmlSecAssert2(firstReferenceNode != NULL, -1); xmlSecAssert2((*firstReferenceNode) == NULL, -1); /* first node is required CanonicalizationMethod. */ cur = xmlSecGetNextElementNode(node->children); if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeCanonicalizationMethod, xmlSecDSigNs))) { dsigCtx->c14nMethod = xmlSecTransformCtxNodeRead(&(dsigCtx->transformCtx), cur, xmlSecTransformUsageC14NMethod); if(dsigCtx->c14nMethod == NULL) { xmlSecInternalError2("xmlSecTransformCtxNodeRead", NULL, "node=%s", xmlSecErrorsSafeString(xmlSecNodeGetName(cur))); return(-1); } } else if(dsigCtx->defC14NMethodId != xmlSecTransformIdUnknown) { /* the dsig spec does require CanonicalizationMethod node * to be present but in some case it application might decide to * minimize traffic */ dsigCtx->c14nMethod = xmlSecTransformCtxCreateAndAppend(&(dsigCtx->transformCtx), dsigCtx->defC14NMethodId); if(dsigCtx->c14nMethod == NULL) { xmlSecInternalError("xmlSecTransformCtxCreateAndAppend", NULL); return(-1); } } else { /* if c14n method is not specified in the template and not specified in * the dsig context then it's an error. */ xmlSecInvalidNodeError(cur, xmlSecNodeCanonicalizationMethod, NULL); return(-1); } /* insert membuf if requested */ if((dsigCtx->flags & XMLSEC_DSIG_FLAGS_STORE_SIGNATURE) != 0) { xmlSecAssert2(dsigCtx->preSignMemBufMethod == NULL, -1); dsigCtx->preSignMemBufMethod = xmlSecTransformCtxCreateAndAppend(&(dsigCtx->transformCtx), xmlSecTransformMemBufId); if(dsigCtx->preSignMemBufMethod == NULL) { xmlSecInternalError("xmlSecTransformCtxCreateAndAppend", xmlSecTransformKlassGetName(xmlSecTransformMemBufId)); return(-1); } } /* next node is required SignatureMethod. */ cur = xmlSecGetNextElementNode( ((cur != NULL) ? cur->next : node->children) ); if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeSignatureMethod, xmlSecDSigNs))) { dsigCtx->signMethod = xmlSecTransformCtxNodeRead(&(dsigCtx->transformCtx), cur, xmlSecTransformUsageSignatureMethod); if(dsigCtx->signMethod == NULL) { xmlSecInternalError("xmlSecTransformCtxNodeRead", xmlSecNodeGetName(cur)); return(-1); } cur = xmlSecGetNextElementNode(cur->next); } else if(dsigCtx->defSignMethodId != xmlSecTransformIdUnknown) { /* the dsig spec does require SignatureMethod node * to be present but in some case it application might decide to * minimize traffic */ dsigCtx->signMethod = xmlSecTransformCtxCreateAndAppend(&(dsigCtx->transformCtx), dsigCtx->defSignMethodId); if(dsigCtx->signMethod == NULL) { xmlSecInternalError("xmlSecTransformCtxCreateAndAppend", NULL); return(-1); } } else { /* if sign method is not specified in the template and not specified in * the dsig context then it's an error. */ xmlSecInvalidNodeError(cur, xmlSecNodeSignatureMethod, NULL); return(-1); } dsigCtx->signMethod->operation = dsigCtx->operation; /* read references */ while((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeReference, xmlSecDSigNs))) { /* record first reference node */ if((*firstReferenceNode) == NULL) { (*firstReferenceNode) = cur; } ++refNodesCount; /* go to next */ cur = xmlSecGetNextElementNode(cur->next); } /* check that we have at least one Reference */ if(refNodesCount == 0) { xmlSecOtherError(XMLSEC_ERRORS_R_DSIG_NO_REFERENCES, NULL, NULL); return(-1); } /* if there is something left than it's an error */ if(cur != NULL) { xmlSecUnexpectedNodeError(cur, NULL); return(-1); } /* done */ return(0); } static int xmlSecDSigCtxProcessReferences(xmlSecDSigCtxPtr dsigCtx, xmlNodePtr firstReferenceNode) { xmlSecDSigReferenceCtxPtr dsigRefCtx; xmlNodePtr cur; int ret; xmlSecAssert2(dsigCtx != NULL, -1); xmlSecAssert2(dsigCtx->status == xmlSecDSigStatusUnknown, -1); xmlSecAssert2((dsigCtx->operation == xmlSecTransformOperationSign) || (dsigCtx->operation == xmlSecTransformOperationVerify), -1); xmlSecAssert2(xmlSecPtrListGetSize(&(dsigCtx->signedInfoReferences)) == 0, -1); xmlSecAssert2(firstReferenceNode != NULL, -1); /* process references */ for(cur = firstReferenceNode; (cur != NULL); cur = xmlSecGetNextElementNode(cur->next)) { /* already checked but we trust none */ if(!xmlSecCheckNodeName(cur, xmlSecNodeReference, xmlSecDSigNs)) { xmlSecInvalidNodeError(cur, xmlSecNodeReference, NULL); return(-1); } /* create reference */ dsigRefCtx = xmlSecDSigReferenceCtxCreate(dsigCtx, xmlSecDSigReferenceOriginSignedInfo); if(dsigRefCtx == NULL) { xmlSecInternalError("xmlSecDSigReferenceCtxCreate", NULL); return(-1); } /* add to the list */ ret = xmlSecPtrListAdd(&(dsigCtx->signedInfoReferences), dsigRefCtx); if(ret < 0) { xmlSecInternalError("xmlSecPtrListAdd", NULL); xmlSecDSigReferenceCtxDestroy(dsigRefCtx); return(-1); } /* process */ ret = xmlSecDSigReferenceCtxProcessNode(dsigRefCtx, cur); if(ret < 0) { xmlSecInternalError("xmlSecDSigReferenceCtxProcessNode", xmlSecNodeGetName(cur)); return(-1); } /* bail out if next Reference processing failed */ if(dsigRefCtx->status != xmlSecDSigStatusSucceeded) { xmlSecDSigCtxMarkAsFailed(dsigCtx, xmlSecDSigFailureReasonReference); return(0); } } /* done */ return(0); } static int xmlSecDSigCtxProcessKeyInfoNode(xmlSecDSigCtxPtr dsigCtx, xmlNodePtr node) { int ret; xmlSecAssert2(dsigCtx != NULL, -1); xmlSecAssert2(dsigCtx->signMethod != NULL, -1); /* set key requirements */ ret = xmlSecTransformSetKeyReq(dsigCtx->signMethod, &(dsigCtx->keyInfoReadCtx.keyReq)); if(ret < 0) { xmlSecInternalError("xmlSecTransformSetKeyReq", xmlSecTransformGetName(dsigCtx->signMethod)); return(-1); } /* ignore <dsig:KeyInfo /> if there is the key is already set */ /* todo: throw an error if key is set and node != NULL? */ if((dsigCtx->signKey == NULL) && (dsigCtx->keyInfoReadCtx.keysMngr != NULL) && (dsigCtx->keyInfoReadCtx.keysMngr->getKey != NULL)) { dsigCtx->signKey = (dsigCtx->keyInfoReadCtx.keysMngr->getKey)(node, &(dsigCtx->keyInfoReadCtx)); } /* check that we have exactly what we want */ if((dsigCtx->signKey == NULL) || (!xmlSecKeyMatch(dsigCtx->signKey, NULL, &(dsigCtx->keyInfoReadCtx.keyReq)))) { xmlSecOtherError(XMLSEC_ERRORS_R_KEY_NOT_FOUND, NULL, NULL); xmlSecDSigCtxMarkAsFailed(dsigCtx, xmlSecDSigFailureReasonKeyNotFound); return(-1); } /* set the key to the transform */ ret = xmlSecTransformSetKey(dsigCtx->signMethod, dsigCtx->signKey); if(ret < 0) { xmlSecInternalError("xmlSecTransformSetKey", xmlSecTransformGetName(dsigCtx->signMethod)); return(-1); } /* if we are signing document, update <dsig:KeyInfo/> node */ if((node != NULL) && (dsigCtx->operation == xmlSecTransformOperationSign)) { ret = xmlSecKeyInfoNodeWrite(node, dsigCtx->signKey, &(dsigCtx->keyInfoWriteCtx)); if(ret < 0) { xmlSecInternalError("xmlSecKeyInfoNodeWrite", NULL); return(-1); } } return(0); } /** * @brief Processes a dsig:Object element. * @details The Object Element (http://www.w3.org/TR/xmldsig-core/#sec-Object) * * Object is an optional element that may occur one or more times. When * present, this element may contain any data. The Object element may include * optional MIME type, ID, and encoding attributes. * * Schema Definition: * @code{.xml} * <element name="Object" type="ds:ObjectType"/> * <complexType name="ObjectType" mixed="true"> * <sequence minOccurs="0" maxOccurs="unbounded"> * <any namespace="##any" processContents="lax"/> * </sequence> * <attribute name="Id" type="ID" use="optional"/> * <attribute name="MimeType" type="string" use="optional"/> * <attribute name="Encoding" type="anyURI" use="optional"/> * </complexType> * * DTD: * * <!ELEMENT Object (#PCDATA|Signature|SignatureProperties|Manifest %Object.ANY;)* > * <!ATTLIST Object Id ID #IMPLIED * MimeType CDATA #IMPLIED * Encoding CDATA #IMPLIED > * @endcode */ static int xmlSecDSigCtxProcessObjectNode(xmlSecDSigCtxPtr dsigCtx, xmlNodePtr node) { xmlNodePtr cur; int ret; xmlSecAssert2(dsigCtx != NULL, -1); xmlSecAssert2(dsigCtx->status == xmlSecDSigStatusUnknown, -1); xmlSecAssert2(node != NULL, -1); /* we care about Manifest nodes only; ignore everything else */ cur = xmlSecGetNextElementNode(node->children); while(cur != NULL) { if(xmlSecCheckNodeName(cur, xmlSecNodeManifest, xmlSecDSigNs)) { ret = xmlSecDSigCtxProcessManifestNode(dsigCtx, cur); if(ret < 0){ xmlSecInternalError("xmlSecDSigCtxProcessManifestNode", NULL); return(-1); } } cur = xmlSecGetNextElementNode(cur->next); } return(0); } /** * @brief Processes a dsig:Manifest element. * @details The Manifest Element (http://www.w3.org/TR/xmldsig-core/#sec-Manifest) * * The Manifest element provides a list of References. The difference from * the list in SignedInfo is that it is application defined which, if any, of * the digests are actually checked against the objects referenced and what to * do if the object is inaccessible or the digest compare fails. If a Manifest * is pointed to from SignedInfo, the digest over the Manifest itself will be * checked by the core result validation behavior. The digests within such * a Manifest are checked at the application's discretion. If a Manifest is * referenced from another Manifest, even the overall digest of this two level * deep Manifest might not be checked. * * Schema Definition: * @code{.xml} * <element name="Manifest" type="ds:ManifestType"/> * <complexType name="ManifestType"> * <sequence> * <element ref="ds:Reference" maxOccurs="unbounded"/> * </sequence> * <attribute name="Id" type="ID" use="optional"/> * </complexType> * * DTD: * * <!ELEMENT Manifest (Reference+) > * <!ATTLIST Manifest Id ID #IMPLIED > * @endcode */ static int xmlSecDSigCtxProcessManifestNode(xmlSecDSigCtxPtr dsigCtx, xmlNodePtr node) { xmlSecDSigReferenceCtxPtr dsigRefCtx; xmlNodePtr cur; int ret; xmlSecAssert2(dsigCtx != NULL, -1); xmlSecAssert2(dsigCtx->status == xmlSecDSigStatusUnknown, -1); xmlSecAssert2(node != NULL, -1); /* calculate references */ cur = xmlSecGetNextElementNode(node->children); while((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeReference, xmlSecDSigNs))) { /* create reference */ dsigRefCtx = xmlSecDSigReferenceCtxCreate(dsigCtx, xmlSecDSigReferenceOriginManifest); if(dsigRefCtx == NULL) { xmlSecInternalError("xmlSecDSigReferenceCtxCreate", NULL); return(-1); } /* add to the list */ ret = xmlSecPtrListAdd(&(dsigCtx->manifestReferences), dsigRefCtx); if(ret < 0) { xmlSecInternalError("xmlSecPtrListAdd", NULL); xmlSecDSigReferenceCtxDestroy(dsigRefCtx); return(-1); } /* process */ ret = xmlSecDSigReferenceCtxProcessNode(dsigRefCtx, cur); if(ret < 0) { xmlSecInternalError("xmlSecDSigReferenceCtxProcessNode", xmlSecNodeGetName(cur)); return(-1); } /* we don;t care if Reference processing failed because * it's Manifest node */ cur = xmlSecGetNextElementNode(cur->next); } /* we should have nothing else here */ if(cur != NULL) { xmlSecUnexpectedNodeError(cur, NULL); return(-1); } return(0); } /** * @brief Gets status as a string. * @param status the status. * @return status as a string. */ const char* xmlSecDSigCtxGetStatusString(xmlSecDSigStatus status) { switch(status) { case xmlSecDSigStatusSucceeded: return "OK"; case xmlSecDSigStatusInvalid: return "FAILED"; /* unknown status can only be caused by an error in the operation */ case xmlSecDSigStatusUnknown: default: return "ERROR"; } } /** * @brief Gets failure reason as a string. * @param failureReason the failure reason. * @return failure reason as a string. */ const char* xmlSecDSigCtxGetFailureReasonString(xmlSecDSigFailureReason failureReason) { switch(failureReason) { case xmlSecDSigFailureReasonReference: return "REFERENCE"; case xmlSecDSigFailureReasonSignature: return "SIGNATURE"; case xmlSecDSigFailureReasonKeyNotFound: return "KEY-NOT-FOUND"; case xmlSecDSigFailureReasonUnknown: default: return "UNKNOWN"; } } /** * @brief Prints debug information about the context. * @details Prints the debug information about @p dsigCtx to @p output. * @param dsigCtx the pointer to <dsig:Signature/> processing context. * @param output the pointer to output FILE. */ void xmlSecDSigCtxDebugDump(xmlSecDSigCtxPtr dsigCtx, FILE* output) { xmlSecAssert(dsigCtx != NULL); xmlSecAssert(output != NULL); if(dsigCtx->operation == xmlSecTransformOperationSign) { fprintf(output, "= SIGNATURE CONTEXT\n"); } else { fprintf(output, "= VERIFICATION CONTEXT\n"); } fprintf(output, "== Status: %s\n", xmlSecDSigCtxGetStatusString(dsigCtx->status)); fprintf(output, "== Failure reason: %s\n", xmlSecDSigCtxGetFailureReasonString(dsigCtx->failureReason)); fprintf(output, "== flags: 0x%08x\n", dsigCtx->flags); fprintf(output, "== flags2: 0x%08x\n", dsigCtx->flags2); if(dsigCtx->id != NULL) { fprintf(output, "== Id: \"%s\"\n", dsigCtx->id); } fprintf(output, "== Key Info Read Ctx:\n"); xmlSecKeyInfoCtxDebugDump(&(dsigCtx->keyInfoReadCtx), output); fprintf(output, "== Key Info Write Ctx:\n"); xmlSecKeyInfoCtxDebugDump(&(dsigCtx->keyInfoWriteCtx), output); fprintf(output, "== Signature Transform Ctx:\n"); xmlSecTransformCtxDebugDump(&(dsigCtx->transformCtx), output); if(dsigCtx->signMethod != NULL) { fprintf(output, "== Signature Method:\n"); xmlSecTransformDebugDump(dsigCtx->signMethod, output); } if(dsigCtx->signKey != NULL) { fprintf(output, "== Signature Key:\n"); xmlSecKeyDebugDump(dsigCtx->signKey, output); } fprintf(output, "== SignedInfo References List:\n"); xmlSecPtrListDebugDump(&(dsigCtx->signedInfoReferences), output); fprintf(output, "== Manifest References List:\n"); xmlSecPtrListDebugDump(&(dsigCtx->manifestReferences), output); if((dsigCtx->result != NULL) && (xmlSecBufferGetData(dsigCtx->result) != NULL)) { fprintf(output, "== Result - start buffer:\n"); xmlSecBufferDebugHexDump(dsigCtx->result, output); fprintf(output, "\n== Result - end buffer\n"); } if(((dsigCtx->flags & XMLSEC_DSIG_FLAGS_STORE_SIGNATURE) != 0) && (xmlSecDSigCtxGetPreSignBuffer(dsigCtx) != NULL) && (xmlSecBufferGetData(xmlSecDSigCtxGetPreSignBuffer(dsigCtx)) != NULL)) { fprintf(output, "== PreSigned data - start buffer:\n"); xmlSecBufferDebugHexDump(xmlSecDSigCtxGetPreSignBuffer(dsigCtx), output); fprintf(output, "\n== PreSigned data - end buffer\n"); } } /** * @brief Prints debug information about the context in XML format. * @details Prints the debug information about @p dsigCtx to @p output in XML format. * @param dsigCtx the pointer to <dsig:Signature/> processing context. * @param output the pointer to output FILE. */ void xmlSecDSigCtxDebugXmlDump(xmlSecDSigCtxPtr dsigCtx, FILE* output) { xmlSecAssert(dsigCtx != NULL); xmlSecAssert(output != NULL); if(dsigCtx->operation == xmlSecTransformOperationSign) { fprintf(output, "<SignatureContext"); } else { fprintf(output, "<VerificationContext"); } fprintf(output, " status=\"%s\"", xmlSecDSigCtxGetStatusString(dsigCtx->status)); fprintf(output, " failureReason=\"%s\"", xmlSecDSigCtxGetFailureReasonString(dsigCtx->failureReason)); fprintf(output, ">\n"); fprintf(output, "<Flags>%08x</Flags>\n", dsigCtx->flags); fprintf(output, "<Flags2>%08x</Flags2>\n", dsigCtx->flags2); fprintf(output, "<Id>"); xmlSecPrintXmlString(output, dsigCtx->id); fprintf(output, "</Id>\n"); fprintf(output, "<KeyInfoReadCtx>\n"); xmlSecKeyInfoCtxDebugXmlDump(&(dsigCtx->keyInfoReadCtx), output); fprintf(output, "</KeyInfoReadCtx>\n"); fprintf(output, "<KeyInfoWriteCtx>\n"); xmlSecKeyInfoCtxDebugXmlDump(&(dsigCtx->keyInfoWriteCtx), output); fprintf(output, "</KeyInfoWriteCtx>\n"); fprintf(output, "<SignatureTransformCtx>\n"); xmlSecTransformCtxDebugXmlDump(&(dsigCtx->transformCtx), output); fprintf(output, "</SignatureTransformCtx>\n"); if(dsigCtx->signMethod != NULL) { fprintf(output, "<SignatureMethod>\n"); xmlSecTransformDebugXmlDump(dsigCtx->signMethod, output); fprintf(output, "</SignatureMethod>\n"); } if(dsigCtx->signKey != NULL) { fprintf(output, "<SignatureKey>\n"); xmlSecKeyDebugXmlDump(dsigCtx->signKey, output); fprintf(output, "</SignatureKey>\n"); } fprintf(output, "<SignedInfoReferences>\n"); xmlSecPtrListDebugXmlDump(&(dsigCtx->signedInfoReferences), output); fprintf(output, "</SignedInfoReferences>\n"); fprintf(output, "<ManifestReferences>\n"); xmlSecPtrListDebugXmlDump(&(dsigCtx->manifestReferences), output); fprintf(output, "</ManifestReferences>\n"); if((dsigCtx->result != NULL) && (xmlSecBufferGetData(dsigCtx->result) != NULL)) { fprintf(output, "<Result>\n"); xmlSecBufferDebugHexDump(dsigCtx->result, output); fprintf(output, "\n</Result>\n"); } if(((dsigCtx->flags & XMLSEC_DSIG_FLAGS_STORE_SIGNATURE) != 0) && (xmlSecDSigCtxGetPreSignBuffer(dsigCtx) != NULL) && (xmlSecBufferGetData(xmlSecDSigCtxGetPreSignBuffer(dsigCtx)) != NULL)) { fprintf(output, "<PreSignedData>\n"); xmlSecBufferDebugHexDump(xmlSecDSigCtxGetPreSignBuffer(dsigCtx), output); fprintf(output, "\n</PreSignedData>\n"); } if(dsigCtx->operation == xmlSecTransformOperationSign) { fprintf(output, "</SignatureContext>\n"); } else { fprintf(output, "</VerificationContext>\n"); } } /****************************************************************************** * * xmlSecDSigReferenceCtx * *****************************************************************************/ /** * @brief Creates a dsig:Reference processing context. * @details Creates new <dsig:Reference/> element processing context. Caller is responsible * for destroying the returned context by calling #xmlSecDSigReferenceCtxDestroy * function. * * @param dsigCtx the pointer to parent <dsig:Signature/> node processing context. * @param origin the reference origin (<dsig:SignedInfo/> or <dsig:Manifest/> node). * @return pointer to newly created context or NULL if an error occurs. */ xmlSecDSigReferenceCtxPtr xmlSecDSigReferenceCtxCreate(xmlSecDSigCtxPtr dsigCtx, xmlSecDSigReferenceOrigin origin) { xmlSecDSigReferenceCtxPtr dsigRefCtx; int ret; xmlSecAssert2(dsigCtx != NULL, NULL); dsigRefCtx = (xmlSecDSigReferenceCtxPtr) xmlMalloc(sizeof(xmlSecDSigReferenceCtx)); if(dsigRefCtx == NULL) { xmlSecMallocError(sizeof(xmlSecDSigReferenceCtx), NULL); return(NULL); } ret = xmlSecDSigReferenceCtxInitialize(dsigRefCtx, dsigCtx, origin); if(ret < 0) { xmlSecInternalError("xmlSecDSigReferenceCtxInitialize", NULL); xmlSecDSigReferenceCtxDestroy(dsigRefCtx); return(NULL); } return(dsigRefCtx); } /** * @brief Destroys a dsig:Reference processing context. * @details Destroy context object created with #xmlSecDSigReferenceCtxCreate function. * @param dsigRefCtx the pointer to <dsig:Reference/> element processing context. */ void xmlSecDSigReferenceCtxDestroy(xmlSecDSigReferenceCtxPtr dsigRefCtx) { xmlSecAssert(dsigRefCtx != NULL); xmlSecDSigReferenceCtxFinalize(dsigRefCtx); xmlFree(dsigRefCtx); } /** * @brief Initializes a dsig:Reference processing context. * @details Initializes new <dsig:Reference/> element processing context. Caller is responsible * for cleaning up the returned context by calling #xmlSecDSigReferenceCtxFinalize * function. * * @param dsigRefCtx the pointer to <dsig:Reference/> element processing context. * @param dsigCtx the pointer to parent <dsig:Signature/> node processing context. * @param origin the reference origin (<dsig:SignedInfo/> or <dsig:Manifest/> node). * @return 0 on succes or aa negative value otherwise. */ int xmlSecDSigReferenceCtxInitialize(xmlSecDSigReferenceCtxPtr dsigRefCtx, xmlSecDSigCtxPtr dsigCtx, xmlSecDSigReferenceOrigin origin) { int ret; xmlSecAssert2(dsigCtx != NULL, -1); xmlSecAssert2(dsigRefCtx != NULL, -1); memset(dsigRefCtx, 0, sizeof(xmlSecDSigReferenceCtx)); dsigRefCtx->dsigCtx = dsigCtx; dsigRefCtx->origin = origin; /* initializes transforms dsigRefCtx */ ret = xmlSecTransformCtxInitialize(&(dsigRefCtx->transformCtx)); if(ret < 0) { xmlSecInternalError("xmlSecTransformCtxInitialize", NULL); return(-1); } /* copy enabled transforms */ if(dsigCtx->enabledReferenceTransforms != NULL) { ret = xmlSecPtrListCopy(&(dsigRefCtx->transformCtx.enabledTransforms), dsigCtx->enabledReferenceTransforms); if(ret < 0) { xmlSecInternalError("xmlSecPtrListCopy", NULL); return(-1); } } dsigRefCtx->transformCtx.preExecCallback = dsigCtx->referencePreExecuteCallback; dsigRefCtx->transformCtx.enabledUris = dsigCtx->enabledReferenceUris; dsigRefCtx->transformCtx.userData = dsigCtx->userData; if((dsigCtx->flags & XMLSEC_DSIG_FLAGS_USE_VISA3D_HACK) != 0) { dsigRefCtx->transformCtx.flags |= XMLSEC_TRANSFORMCTX_FLAGS_USE_VISA3D_HACK; } if((dsigCtx->flags & XMLSEC_DSIG_FLAGS_USE_ASN1_SIGNATURE_VALUES) != 0) { dsigRefCtx->transformCtx.flags |= XMLSEC_TRANSFORMCTX_FLAGS_SUPPORT_ASN1_SIGNATURE_VALUES; } return(0); } /** * @brief Cleans up a dsig:Reference processing context. * @details Cleans up context object created with #xmlSecDSigReferenceCtxInitialize function. * @param dsigRefCtx the pointer to <dsig:Reference/> element processing context. */ void xmlSecDSigReferenceCtxFinalize(xmlSecDSigReferenceCtxPtr dsigRefCtx) { xmlSecAssert(dsigRefCtx != NULL); xmlSecTransformCtxFinalize(&(dsigRefCtx->transformCtx)); if(dsigRefCtx->id != NULL) { xmlFree(dsigRefCtx->id); } if(dsigRefCtx->uri != NULL) { xmlFree(dsigRefCtx->uri); } if(dsigRefCtx->type != NULL) { xmlFree(dsigRefCtx->type); } memset(dsigRefCtx, 0, sizeof(xmlSecDSigReferenceCtx)); } /** * @brief Gets the pre-digest buffer for the Reference element. * @details Gets the results of <dsig:Reference/> node processing just before digesting * (valid only if #XMLSEC_DSIG_FLAGS_STORE_SIGNEDINFO_REFERENCES or * #XMLSEC_DSIG_FLAGS_STORE_MANIFEST_REFERENCES flas of signature context * is set). * * @param dsigRefCtx the pointer to <dsig:Reference/> element processing context. * @return pointer to the buffer or NULL if an error occurs. */ xmlSecBufferPtr xmlSecDSigReferenceCtxGetPreDigestBuffer(xmlSecDSigReferenceCtxPtr dsigRefCtx) { xmlSecAssert2(dsigRefCtx != NULL, NULL); return((dsigRefCtx->preDigestMemBufMethod != NULL) ? xmlSecTransformMemBufGetBuffer(dsigRefCtx->preDigestMemBufMethod) : NULL); } /** * @brief Processes a dsig:Reference element. * @details The Reference Element (http://www.w3.org/TR/xmldsig-core/#sec-Reference) * * Reference is an element that may occur one or more times. It specifies * a digest algorithm and digest value, and optionally an identifier of the * object being signed, the type of the object, and/or a list of transforms * to be applied prior to digesting. The identification (URI) and transforms * describe how the digested content (i.e., the input to the digest method) * was created. The Type attribute facilitates the processing of referenced * data. For example, while this specification makes no requirements over * external data, an application may wish to signal that the referent is a * Manifest. An optional ID attribute permits a Reference to be referenced * from elsewhere. * * @param dsigRefCtx the pointer to <dsig:Reference/> element processing context. * @param node the pointer to <dsig:Reference/> node. * @return 0 on succes or aa negative value otherwise. */ int xmlSecDSigReferenceCtxProcessNode(xmlSecDSigReferenceCtxPtr dsigRefCtx, xmlNodePtr node) { xmlSecTransformCtxPtr transformCtx; xmlNodePtr digestValueNode; xmlNodePtr cur; int ret; xmlSecAssert2(dsigRefCtx != NULL, -1); xmlSecAssert2(dsigRefCtx->dsigCtx != NULL, -1); xmlSecAssert2(dsigRefCtx->digestMethod == NULL, -1); xmlSecAssert2(dsigRefCtx->digestMethod == NULL, -1); xmlSecAssert2(dsigRefCtx->preDigestMemBufMethod == NULL, -1); xmlSecAssert2(node != NULL, -1); xmlSecAssert2(node->doc != NULL, -1); transformCtx = &(dsigRefCtx->transformCtx); /* read attributes first */ dsigRefCtx->uri = xmlGetProp(node, xmlSecAttrURI); dsigRefCtx->id = xmlGetProp(node, xmlSecAttrId); dsigRefCtx->type= xmlGetProp(node, xmlSecAttrType); /* set start URI (and check that it is enabled!) */ ret = xmlSecTransformCtxSetUri(transformCtx, dsigRefCtx->uri, node); if(ret < 0) { xmlSecInternalError2("xmlSecTransformCtxSetUri", NULL, "uri=%s", xmlSecErrorsSafeString(dsigRefCtx->uri)); return(-1); } /* first is optional Transforms node */ cur = xmlSecGetNextElementNode(node->children); if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeTransforms, xmlSecDSigNs))) { ret = xmlSecTransformCtxNodesListRead(transformCtx, cur, xmlSecTransformUsageDSigTransform); if(ret < 0) { xmlSecInternalError2("xmlSecTransformCtxNodesListRead", NULL, "node=%s", xmlSecErrorsSafeString(xmlSecNodeGetName(cur))); return(-1); } cur = xmlSecGetNextElementNode(cur->next); } /* insert membuf if requested */ if(((dsigRefCtx->origin == xmlSecDSigReferenceOriginSignedInfo) && ((dsigRefCtx->dsigCtx->flags & XMLSEC_DSIG_FLAGS_STORE_SIGNEDINFO_REFERENCES) != 0)) || ((dsigRefCtx->origin == xmlSecDSigReferenceOriginManifest) && ((dsigRefCtx->dsigCtx->flags & XMLSEC_DSIG_FLAGS_STORE_MANIFEST_REFERENCES) != 0))) { xmlSecAssert2(dsigRefCtx->preDigestMemBufMethod == NULL, -1); dsigRefCtx->preDigestMemBufMethod = xmlSecTransformCtxCreateAndAppend( transformCtx, xmlSecTransformMemBufId); if(dsigRefCtx->preDigestMemBufMethod == NULL) { xmlSecInternalError("xmlSecTransformCtxCreateAndAppend(xmlSecTransformMemBufId)", NULL); return(-1); } } /* next node is required DigestMethod. */ if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeDigestMethod, xmlSecDSigNs))) { dsigRefCtx->digestMethod = xmlSecTransformCtxNodeRead(&(dsigRefCtx->transformCtx), cur, xmlSecTransformUsageDigestMethod); if(dsigRefCtx->digestMethod == NULL) { xmlSecInternalError("xmlSecTransformCtxNodeRead", xmlSecNodeGetName(cur)); return(-1); } cur = xmlSecGetNextElementNode(cur->next); } else if(dsigRefCtx->dsigCtx->defSignMethodId != xmlSecTransformIdUnknown) { /* the dsig spec does require DigestMethod node * to be present but in some case it application might decide to * minimize traffic */ dsigRefCtx->digestMethod = xmlSecTransformCtxCreateAndAppend(&(dsigRefCtx->transformCtx), dsigRefCtx->dsigCtx->defSignMethodId); if(dsigRefCtx->digestMethod == NULL) { xmlSecInternalError("xmlSecTransformCtxCreateAndAppend", NULL); return(-1); } } else { /* if digest method is not specified in the template and not specified in * the dsig context then it's an error. */ xmlSecInvalidNodeError(cur, xmlSecNodeDigestMethod, NULL); return(-1); } dsigRefCtx->digestMethod->operation = dsigRefCtx->dsigCtx->operation; /* last node is required DigestValue */ if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeDigestValue, xmlSecDSigNs))) { digestValueNode = cur; cur = xmlSecGetNextElementNode(cur->next); } else { xmlSecInvalidNodeError(cur, xmlSecNodeDigestValue, NULL); return(-1); } /* if we have something else then it's an error */ if(cur != NULL) { xmlSecUnexpectedNodeError(cur, NULL); return(-1); } /* if we need to write result to xml node then we need base64 encode result */ if(dsigRefCtx->dsigCtx->operation == xmlSecTransformOperationSign) { xmlSecTransformPtr base64Encode; /* we need to add base64 encode transform */ base64Encode = xmlSecTransformCtxCreateAndAppend(transformCtx, xmlSecTransformBase64Id); if(base64Encode == NULL) { xmlSecInternalError("xmlSecTransformCtxCreateAndAppend", NULL); return(-1); } base64Encode->operation = xmlSecTransformOperationEncode; } /* finally get transforms results */ ret = xmlSecTransformCtxExecute(transformCtx, node->doc); if(ret < 0) { xmlSecInternalError("xmlSecTransformCtxExecute", NULL); return(-1); } dsigRefCtx->result = transformCtx->result; if(dsigRefCtx->dsigCtx->operation == xmlSecTransformOperationSign) { xmlSecByte* outBuf; xmlSecSize outSize; int outLen; if((dsigRefCtx->result == NULL) || (xmlSecBufferGetData(dsigRefCtx->result) == NULL)) { xmlSecInternalError("xmlSecTransformCtxExecute", NULL); return(-1); } /* write signed data to xml */ outBuf = xmlSecBufferGetData(dsigRefCtx->result); outSize = xmlSecBufferGetSize(dsigRefCtx->result); XMLSEC_SAFE_CAST_SIZE_TO_INT(outSize, outLen, return(-1), NULL); xmlNodeSetContentLen(digestValueNode, outBuf, outLen); /* set success status and we are done */ dsigRefCtx->status = xmlSecDSigStatusSucceeded; } else { /* verify SignatureValue node content */ ret = xmlSecTransformVerifyNodeContent(dsigRefCtx->digestMethod, digestValueNode, transformCtx); if(ret < 0) { xmlSecInternalError("xmlSecTransformVerifyNodeContent", NULL); return(-1); } /* set status and we are done */ if(dsigRefCtx->digestMethod->status == xmlSecTransformStatusOk) { dsigRefCtx->status = xmlSecDSigStatusSucceeded; } else { dsigRefCtx->status = xmlSecDSigStatusInvalid; } } return(0); } /** * @brief Prints debug information about the reference context. * @details Prints debug information about @p dsigRefCtx to @p output. * @param dsigRefCtx the pointer to <dsig:Reference/> element processing context. * @param output the pointer to output FILE. */ void xmlSecDSigReferenceCtxDebugDump(xmlSecDSigReferenceCtxPtr dsigRefCtx, FILE* output) { xmlSecAssert(dsigRefCtx != NULL); xmlSecAssert(dsigRefCtx->dsigCtx != NULL); xmlSecAssert(output != NULL); if(dsigRefCtx->dsigCtx->operation == xmlSecTransformOperationSign) { fprintf(output, "= REFERENCE CALCULATION CONTEXT\n"); } else { fprintf(output, "= REFERENCE VERIFICATION CONTEXT\n"); } fprintf(output, "== Status: %s\n", xmlSecDSigCtxGetStatusString(dsigRefCtx->status)); if(dsigRefCtx->id != NULL) { fprintf(output, "== Id: \"%s\"\n", dsigRefCtx->id); } if(dsigRefCtx->uri != NULL) { fprintf(output, "== URI: \"%s\"\n", dsigRefCtx->uri); } if(dsigRefCtx->type != NULL) { fprintf(output, "== Type: \"%s\"\n", dsigRefCtx->type); } fprintf(output, "== Reference Transform Ctx:\n"); xmlSecTransformCtxDebugDump(&(dsigRefCtx->transformCtx), output); if(dsigRefCtx->digestMethod != NULL) { fprintf(output, "== Digest Method:\n"); xmlSecTransformDebugDump(dsigRefCtx->digestMethod, output); } if((xmlSecDSigReferenceCtxGetPreDigestBuffer(dsigRefCtx) != NULL) && (xmlSecBufferGetData(xmlSecDSigReferenceCtxGetPreDigestBuffer(dsigRefCtx)) != NULL)) { fprintf(output, "== PreDigest data - start buffer:\n"); xmlSecBufferDebugHexDump(xmlSecDSigReferenceCtxGetPreDigestBuffer(dsigRefCtx), output); fprintf(output, "\n== PreDigest data - end buffer\n"); } if((dsigRefCtx->result != NULL) && (xmlSecBufferGetData(dsigRefCtx->result) != NULL)) { fprintf(output, "== Result - start buffer:\n"); xmlSecBufferDebugHexDump(dsigRefCtx->result, output); fprintf(output, "\n== Result - end buffer\n"); } } /** * @brief Prints debug information about the reference context in XML format. * @details Prints debug information about @p dsigRefCtx to @p output in output format. * @param dsigRefCtx the pointer to <dsig:Reference/> element processing context. * @param output the pointer to output FILE. */ void xmlSecDSigReferenceCtxDebugXmlDump(xmlSecDSigReferenceCtxPtr dsigRefCtx, FILE* output) { xmlSecAssert(dsigRefCtx != NULL); xmlSecAssert(dsigRefCtx->dsigCtx != NULL); xmlSecAssert(output != NULL); if(dsigRefCtx->dsigCtx->operation == xmlSecTransformOperationSign) { fprintf(output, "<ReferenceCalculationContext "); } else { fprintf(output, "<ReferenceVerificationContext "); } fprintf(output, " status=\"%s\"", xmlSecDSigCtxGetStatusString(dsigRefCtx->status)); fprintf(output, ">\n"); fprintf(output, "<Id>"); xmlSecPrintXmlString(output, dsigRefCtx->id); fprintf(output, "</Id>\n"); fprintf(output, "<URI>"); xmlSecPrintXmlString(output, dsigRefCtx->uri); fprintf(output, "</URI>\n"); fprintf(output, "<Type>"); xmlSecPrintXmlString(output, dsigRefCtx->type); fprintf(output, "</Type>\n"); fprintf(output, "<ReferenceTransformCtx>\n"); xmlSecTransformCtxDebugXmlDump(&(dsigRefCtx->transformCtx), output); fprintf(output, "</ReferenceTransformCtx>\n"); if(dsigRefCtx->digestMethod != NULL) { fprintf(output, "<DigestMethod>\n"); xmlSecTransformDebugXmlDump(dsigRefCtx->digestMethod, output); fprintf(output, "</DigestMethod>\n"); } if((dsigRefCtx->result != NULL) && (xmlSecBufferGetData(dsigRefCtx->result) != NULL)) { fprintf(output, "<Result>"); xmlSecBufferDebugHexDump(dsigRefCtx->result, output); fprintf(output, "</Result>\n"); } if((xmlSecDSigReferenceCtxGetPreDigestBuffer(dsigRefCtx) != NULL) && (xmlSecBufferGetData(xmlSecDSigReferenceCtxGetPreDigestBuffer(dsigRefCtx)) != NULL)) { fprintf(output, "<PreDigestData>"); xmlSecBufferDebugHexDump(xmlSecDSigReferenceCtxGetPreDigestBuffer(dsigRefCtx), output); fprintf(output, "</PreDigestData>\n"); } if(dsigRefCtx->dsigCtx->operation == xmlSecTransformOperationSign) { fprintf(output, "</ReferenceCalculationContext>\n"); } else { fprintf(output, "</ReferenceVerificationContext>\n"); } } /****************************************************************************** * * xmlSecDSigReferenceCtxListKlass * *****************************************************************************/ static xmlSecPtrListKlass xmlSecDSigReferenceCtxListKlass = { BAD_CAST "dsig-reference-list", NULL, /* xmlSecPtrDuplicateItemMethod duplicateItem; */ (xmlSecPtrDestroyItemMethod)xmlSecDSigReferenceCtxDestroy, /* xmlSecPtrDestroyItemMethod destroyItem; */ (xmlSecPtrDebugDumpItemMethod)xmlSecDSigReferenceCtxDebugDump, /* xmlSecPtrDebugDumpItemMethod debugDumpItem; */ (xmlSecPtrDebugDumpItemMethod)xmlSecDSigReferenceCtxDebugXmlDump, /* xmlSecPtrDebugDumpItemMethod debugXmlDumpItem; */ }; /** * @brief Gets the dsig:Reference processing contexts list klass. * @details The <dsig:Reference/> element processing contexts list klass. * * @return <dsig:Reference/> element processing context list klass. */ xmlSecPtrListId xmlSecDSigReferenceCtxListGetKlass(void) { return(&xmlSecDSigReferenceCtxListKlass); } #endif /* XMLSEC_NO_XMLDSIG */