xsec/dsig/DSIGReference.cpp (763 lines of code) (raw):

/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * XSEC * * DSIG_Reference := Class for handling a DSIG reference element * * $Id$ * */ // XSEC includes #include <xsec/dsig/DSIGReference.hpp> #include <xsec/transformers/TXFMChain.hpp> #include <xsec/transformers/TXFMURL.hpp> #include <xsec/transformers/TXFMDocObject.hpp> #include <xsec/transformers/TXFMOutputFile.hpp> #include <xsec/transformers/TXFMHash.hpp> #include <xsec/transformers/TXFMBase64.hpp> #include <xsec/transformers/TXFMSB.hpp> #include <xsec/transformers/TXFMXPath.hpp> #include <xsec/transformers/TXFMC14n.hpp> #include <xsec/transformers/TXFMXSL.hpp> #include <xsec/transformers/TXFMEnvelope.hpp> #include <xsec/dsig/DSIGConstants.hpp> #include <xsec/dsig/DSIGSignature.hpp> #include <xsec/dsig/DSIGTransformList.hpp> #include <xsec/dsig/DSIGTransformBase64.hpp> #include <xsec/dsig/DSIGTransformEnvelope.hpp> #include <xsec/dsig/DSIGTransformXPath.hpp> #include <xsec/dsig/DSIGTransformXPathFilter.hpp> #include <xsec/dsig/DSIGTransformXSL.hpp> #include <xsec/dsig/DSIGTransformC14n.hpp> #include <xsec/framework/XSECError.hpp> #include <xsec/framework/XSECEnv.hpp> #include <xsec/framework/XSECAlgorithmHandler.hpp> #include <xsec/framework/XSECAlgorithmMapper.hpp> #include <xsec/utils/XSECPlatformUtils.hpp> #include <xsec/utils/XSECBinTXFMInputStream.hpp> #include "../utils/XSECDOMUtils.hpp" // Xerces #include <xercesc/util/XMLNetAccessor.hpp> #include <xercesc/util/XMLUniDefs.hpp> #include <xercesc/util/Janitor.hpp> XERCES_CPP_NAMESPACE_USE #include <iostream> // -------------------------------------------------------------------------------- // Some useful strings // -------------------------------------------------------------------------------- static const XMLCh s_unicodeStrURI[] = { XERCES_CPP_NAMESPACE_QUALIFIER chLatin_U, XERCES_CPP_NAMESPACE_QUALIFIER chLatin_R, XERCES_CPP_NAMESPACE_QUALIFIER chLatin_I, XERCES_CPP_NAMESPACE_QUALIFIER chNull }; static const XMLCh s_unicodeStrxpointer[] = { XERCES_CPP_NAMESPACE_QUALIFIER chLatin_x, XERCES_CPP_NAMESPACE_QUALIFIER chLatin_p, XERCES_CPP_NAMESPACE_QUALIFIER chLatin_o, XERCES_CPP_NAMESPACE_QUALIFIER chLatin_i, XERCES_CPP_NAMESPACE_QUALIFIER chLatin_n, XERCES_CPP_NAMESPACE_QUALIFIER chLatin_t, XERCES_CPP_NAMESPACE_QUALIFIER chLatin_e, XERCES_CPP_NAMESPACE_QUALIFIER chLatin_r, XERCES_CPP_NAMESPACE_QUALIFIER chNull }; static const XMLCh s_unicodeStrRootNode[] = { XERCES_CPP_NAMESPACE_QUALIFIER chOpenParen, XERCES_CPP_NAMESPACE_QUALIFIER chForwardSlash, XERCES_CPP_NAMESPACE_QUALIFIER chCloseParen, XERCES_CPP_NAMESPACE_QUALIFIER chNull }; // -------------------------------------------------------------------------------- // Constructors and Destructors // -------------------------------------------------------------------------------- DSIGReference::DSIGReference(const XSECEnv * env, DOMNode *dom) : mp_formatter(NULL), mp_referenceNode(dom), mp_preHash(NULL), mp_manifestList(NULL), mp_URI(NULL), m_isManifest(false), mp_transformsNode(NULL), mp_hashValueNode(NULL), mp_env(env), mp_transformList(NULL), mp_algorithmURI(NULL), m_loaded(false) { // Should throw an exception if the node is not a REFERENCE element XSECnew(mp_formatter, XSECSafeBufferFormatter("UTF-8",XMLFormatter::NoEscapes, XMLFormatter::UnRep_CharRef)); } DSIGReference::DSIGReference(const XSECEnv * env) : mp_formatter(NULL), mp_referenceNode(NULL), mp_preHash(NULL), mp_manifestList(NULL), mp_URI(NULL), m_isManifest(false), mp_transformsNode(NULL), mp_hashValueNode(NULL), mp_env(env), mp_transformList(NULL), mp_algorithmURI(NULL), m_loaded(false) { XSECnew(mp_formatter, XSECSafeBufferFormatter("UTF-8",XMLFormatter::NoEscapes, XMLFormatter::UnRep_CharRef)); }; DSIGReference::~DSIGReference() { // Destroy any associated transforms if (mp_transformList != NULL) { delete mp_transformList; mp_transformList = NULL; } if (mp_formatter != NULL) delete mp_formatter; if (mp_manifestList != NULL) delete mp_manifestList; }; // -------------------------------------------------------------------------------- // Creation of Transforms // -------------------------------------------------------------------------------- void DSIGReference::createTransformList(void) { // Creates the transforms list safeBuffer str; const XMLCh * prefix; DOMDocument *doc = mp_env->getParentDocument(); prefix = mp_env->getDSIGNSPrefix(); if (mp_transformsNode == NULL) { // Need to create a transforms node makeQName(str, prefix, "Transforms"); mp_transformsNode = doc->createElementNS(DSIGConstants::s_unicodeStrURIDSIG, str.rawXMLChBuffer()); mp_referenceNode->insertBefore(mp_transformsNode, mp_referenceNode->getFirstChild()); if (mp_env->getPrettyPrintFlag() == true) mp_referenceNode->insertBefore(doc->createTextNode(DSIGConstants::s_unicodeStrNL), mp_transformsNode); mp_env->doPrettyPrint(mp_transformsNode); // Create the list XSECnew(mp_transformList, DSIGTransformList()); } } void DSIGReference::addTransform(DSIGTransform * txfm, DOMElement * txfmElt) { if (mp_transformList == NULL) createTransformList(); mp_transformsNode->appendChild(txfmElt); mp_env->doPrettyPrint(mp_transformsNode); mp_transformList->addTransform(txfm); } DSIGTransformEnvelope * DSIGReference::appendEnvelopedSignatureTransform() { DOMElement *txfmElt; DSIGTransformEnvelope * txfm; XSECnew(txfm, DSIGTransformEnvelope(mp_env)); txfmElt = txfm->createBlankTransform(mp_env->getParentDocument()); addTransform(txfm, txfmElt); return txfm; } DSIGTransformBase64 * DSIGReference::appendBase64Transform() { DOMElement *txfmElt; DSIGTransformBase64 * txfm; XSECnew(txfm, DSIGTransformBase64(mp_env)); txfmElt = txfm->createBlankTransform(mp_env->getParentDocument()); addTransform(txfm, txfmElt); return txfm; } DSIGTransformXSL * DSIGReference::appendXSLTransform(DOMNode * stylesheet) { DOMElement *txfmElt; DSIGTransformXSL * txfm; XSECnew(txfm, DSIGTransformXSL(mp_env)); txfmElt = txfm->createBlankTransform(mp_env->getParentDocument()); txfm->setStylesheet(stylesheet); addTransform(txfm, txfmElt); return txfm; } DSIGTransformC14n * DSIGReference::appendCanonicalizationTransform( const XMLCh * canonicalizationAlgorithmURI) { DOMElement *txfmElt; DSIGTransformC14n * txfm; XSECnew(txfm, DSIGTransformC14n(mp_env)); txfmElt = txfm->createBlankTransform(mp_env->getParentDocument()); txfm->setCanonicalizationMethod(canonicalizationAlgorithmURI); addTransform(txfm, txfmElt); return txfm; } DSIGTransformXPath * DSIGReference::appendXPathTransform(const char * expr) { DOMElement *txfmElt; DSIGTransformXPath * txfm; XSECnew(txfm, DSIGTransformXPath(mp_env)); txfmElt = txfm->createBlankTransform(mp_env->getParentDocument()); txfm->setExpression(expr); addTransform(txfm, txfmElt); return txfm; } DSIGTransformXPathFilter * DSIGReference::appendXPathFilterTransform(void) { DOMElement *txfmElt; DSIGTransformXPathFilter * txfm; XSECnew(txfm, DSIGTransformXPathFilter(mp_env)); txfmElt = txfm->createBlankTransform(mp_env->getParentDocument()); addTransform(txfm, txfmElt); mp_env->doPrettyPrint(txfmElt); return txfm; } // -------------------------------------------------------------------------------- // Creation of blanks // -------------------------------------------------------------------------------- DOMElement *DSIGReference::createBlankReference(const XMLCh * URI, const XMLCh * hashAlgorithmURI, const XMLCh * type) { // Reset this Reference just in case m_isManifest = false; mp_preHash = NULL; mp_manifestList = NULL; mp_transformsNode = NULL; mp_transformList = NULL; safeBuffer str; DOMDocument *doc = mp_env->getParentDocument(); const XMLCh * prefix = mp_env->getDSIGNSPrefix(); makeQName(str, prefix, "Reference"); DOMElement *ret = doc->createElementNS(DSIGConstants::s_unicodeStrURIDSIG, str.rawXMLChBuffer()); mp_referenceNode = ret; // Set type if (type != NULL) ret->setAttributeNS(NULL, MAKE_UNICODE_STRING("Type"), type); // Set URI if (URI != NULL) { ret->setAttributeNS(NULL, s_unicodeStrURI, URI); mp_URI = ret->getAttributeNS(NULL, s_unicodeStrURI); // Used later on as a pointer } else { // Anonymous reference mp_URI = NULL; } // Create hash and hashValue nodes makeQName(str, prefix, "DigestMethod"); DOMElement *digestMethod = doc->createElementNS(DSIGConstants::s_unicodeStrURIDSIG, str.rawXMLChBuffer()); mp_env->doPrettyPrint(ret); ret->appendChild(digestMethod); mp_env->doPrettyPrint(ret); digestMethod->setAttributeNS(NULL, DSIGConstants::s_unicodeStrAlgorithm, hashAlgorithmURI); // Retrieve the attribute value for later use mp_algorithmURI = digestMethod->getAttributeNS(NULL, DSIGConstants::s_unicodeStrAlgorithm); // DigestValue makeQName(str, prefix, "DigestValue"); mp_hashValueNode = doc->createElementNS(DSIGConstants::s_unicodeStrURIDSIG, str.rawXMLChBuffer()); ret->appendChild(mp_hashValueNode); mp_env->doPrettyPrint(ret); mp_hashValueNode->appendChild(doc->createTextNode(MAKE_UNICODE_STRING("Not yet calculated"))); m_loaded = true; return ret; } // -------------------------------------------------------------------------------- // setPreHashTransform - to set a transform to be used pre-hash // -------------------------------------------------------------------------------- void DSIGReference::setPreHashTXFM(TXFMBase * t) { mp_preHash = t; } // setId // -------------------------------------------------------------------------------- void DSIGReference::setId(const XMLCh *id) { if (mp_referenceNode) ((DOMElement*)mp_referenceNode)->setAttributeNS(NULL, MAKE_UNICODE_STRING("Id"), id); } // -------------------------------------------------------------------------------- // setType // -------------------------------------------------------------------------------- void DSIGReference::setType(const XMLCh *type) { if (mp_referenceNode) ((DOMElement*)mp_referenceNode)->setAttributeNS(NULL, MAKE_UNICODE_STRING("Type"), type); } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // isManifest // -------------------------------------------------------------------------------- bool DSIGReference::isManifest() const { return m_isManifest; } // -------------------------------------------------------------------------------- // getURI // -------------------------------------------------------------------------------- const XMLCh * DSIGReference::getURI() const { return mp_URI; } // -------------------------------------------------------------------------------- // getManifestReferenceList // -------------------------------------------------------------------------------- DSIGReferenceList * DSIGReference::getManifestReferenceList() const { return mp_manifestList; } // -------------------------------------------------------------------------------- // getURIBaseTransform // -------------------------------------------------------------------------------- TXFMBase * DSIGReference::getURIBaseTXFM(DOMDocument * doc, const XMLCh * URI, const XSECEnv * env) { // Determine if this is a full URL or a pointer to a URL if (URI == NULL || (URI[0] != 0 && URI[0] != XERCES_CPP_NAMESPACE_QUALIFIER chPound)) { TXFMURL * retTransform; // Have a URL! XSECnew(retTransform, TXFMURL(doc, env->getURIResolver())); try { ((TXFMURL *) retTransform)->setInput(URI); } catch (...) { delete retTransform; throw; } return retTransform; } // Have a fragment URI from the local document TXFMDocObject * to; XSECnew(to, TXFMDocObject(doc)); Janitor<TXFMDocObject> j_to(to); to->setEnv(env); // Find out what sort of object pointer this is. if (URI[0] == 0) { // empty pointer - use the document itself to->setInput(doc); to->stripComments(); } else if (XMLString::compareNString(&URI[1], s_unicodeStrxpointer, 8) == 0) { // Have an xpointer if (strEquals(s_unicodeStrRootNode, &URI[9]) == true) { // Root node to->setInput(doc); } else if (URI[9] == XERCES_CPP_NAMESPACE_QUALIFIER chOpenParen && URI[10] == XERCES_CPP_NAMESPACE_QUALIFIER chLatin_i && URI[11] == XERCES_CPP_NAMESPACE_QUALIFIER chLatin_d && URI[12] == XERCES_CPP_NAMESPACE_QUALIFIER chOpenParen && URI[13] == XERCES_CPP_NAMESPACE_QUALIFIER chSingleQuote) { XMLSize_t len = XMLString::stringLen(&URI[14]); XMLCh* tmp = new XMLCh[len + 1]; ArrayJanitor<XMLCh> j_tmp(tmp); XMLSize_t j = 14, i = 0; // Have an ID while (i < len && URI[j] != '\'') { tmp[i++] = URI[j++]; } tmp[i] = XERCES_CPP_NAMESPACE_QUALIFIER chNull; if (URI[j] != '\'') { throw XSECException(XSECException::UnsupportedXpointerExpr); } to->setInput(doc, tmp); } else { throw XSECException(XSECException::UnsupportedXpointerExpr); } // Keep comments in these situations to->activateComments(); } else { to->setInput(doc, &URI[1]); // Remove comments to->stripComments(); } j_to.release(); return to; } // -------------------------------------------------------------------------------- // load // -------------------------------------------------------------------------------- void DSIGReference::load(void) { // Load reference info from XML DOMNamedNodeMap *atts = mp_referenceNode->getAttributes(); DOMNode *tmpElt; const XMLCh * name; safeBuffer sbName; if (atts != 0) { XMLSize_t size = atts->getLength(); for (XMLSize_t i = 0; i < size; ++i) { name = atts->item(i)->getNodeName(); sbName << (*mp_formatter << atts->item(i)->getNodeName()); if (strEquals(name, s_unicodeStrURI)) { mp_URI = atts->item(i)->getNodeValue(); } else if (strEquals(name, "Type")) { // Check if a manifest, otherwise ignore for now if (strEquals(atts->item(i)->getNodeValue(), DSIGConstants::s_unicodeStrURIMANIFEST)) m_isManifest = true; } else if (strEquals(name, "Id")) { // For now ignore } else if (sbName.sbStrncmp("xmlns", 5) == 0) { // Ignore name spaces } else { //safeBuffer tmp, error; //error << (*mp_formatter << name); //tmp.sbStrcpyIn("Unknown attribute in <Reference> Element : "); //tmp.sbStrcatIn(error); throw XSECException(XSECException::UnknownDSIGAttribute, "Unknown attribute in <Reference> Element"); } } } // Now check for Transforms tmpElt = mp_referenceNode->getFirstChild(); while (tmpElt != 0 && (tmpElt->getNodeType() != DOMNode::ELEMENT_NODE)) { if (tmpElt->getNodeType() == DOMNode::ENTITY_REFERENCE_NODE) { throw XSECException(XSECException::ExpectedDSIGChildNotFound, "EntityReference nodes in <Reference> are unsupported."); } // Skip text and comments tmpElt = tmpElt->getNextSibling(); } if (tmpElt == 0) { throw XSECException(XSECException::ExpectedDSIGChildNotFound, "Expected <Transforms> or <DigestMethod> within <Reference>"); } if (strEquals(getDSIGLocalName(tmpElt), "Transforms")) { // Store node for later use mp_transformsNode = tmpElt; // Load the transforms mp_transformList = loadTransforms(tmpElt, mp_formatter, mp_env); // Find next node tmpElt = tmpElt->getNextSibling(); while (tmpElt != 0 && (tmpElt->getNodeType() != DOMNode::ELEMENT_NODE)) { if (tmpElt->getNodeType() == DOMNode::ENTITY_REFERENCE_NODE) { throw XSECException(XSECException::ExpectedDSIGChildNotFound, "EntityReference nodes in <Reference> are unsupported."); } tmpElt = tmpElt->getNextSibling(); } } /* if tmpElt node type = transforms */ else { mp_transformList = NULL; } if (tmpElt == NULL || !strEquals(getDSIGLocalName(tmpElt), "DigestMethod")) { throw XSECException(XSECException::ExpectedDSIGChildNotFound, "Expected <DigestMethod> element"); } // Determine what the digest method actually is atts = tmpElt->getAttributes(); unsigned int i; for (i = 0; i < atts->getLength() && !strEquals(atts->item(i)->getNodeName(), DSIGConstants::s_unicodeStrAlgorithm); ++i); if (i == atts->getLength()) { throw XSECException(XSECException::ExpectedDSIGChildNotFound, "Expected 'Algorithm' attribute in <DigestMethod>"); } mp_algorithmURI = atts->item(i)->getNodeValue(); // Find the hash value node tmpElt = tmpElt->getNextSibling(); while (tmpElt != 0 && (tmpElt->getNodeType() != DOMNode::ELEMENT_NODE || !strEquals(getDSIGLocalName(tmpElt), "DigestValue"))) { if (tmpElt->getNodeType() == DOMNode::ENTITY_REFERENCE_NODE) { throw XSECException(XSECException::ExpectedDSIGChildNotFound, "EntityReference nodes in <Reference> are unsupported."); } tmpElt = tmpElt->getNextSibling(); } if (tmpElt == 0) { throw XSECException(XSECException::ExpectedDSIGChildNotFound, "Expected <DigestValue> within <Reference>"); } mp_hashValueNode = tmpElt; // If we are a manifest, then we need to load the manifest references if (m_isManifest) { // Find the manifest node - we cheat and use a transform TXFMBase * docObject; DOMNode * manifestNode, * referenceNode; docObject = getURIBaseTXFM(mp_referenceNode->getOwnerDocument(), mp_URI, mp_env); manifestNode = docObject->getFragmentNode(); delete docObject; // Now search downwards to find a <Manifest> if (manifestNode == 0 || manifestNode->getNodeType() != DOMNode::ELEMENT_NODE || (!strEquals(getDSIGLocalName(manifestNode), "Object") && !strEquals(getDSIGLocalName(manifestNode), "Manifest"))) { throw XSECException(XSECException::ExpectedDSIGChildNotFound, "Expected <Manifest> or <Object> URI for Manifest Type <Reference>"); } if (strEquals(getDSIGLocalName(manifestNode), "Object")) { // Find Manifest child manifestNode = manifestNode->getFirstChild(); while (manifestNode != 0 && manifestNode->getNodeType() != DOMNode::ELEMENT_NODE) { if (manifestNode->getNodeType() == DOMNode::ENTITY_REFERENCE_NODE) { throw XSECException(XSECException::ExpectedDSIGChildNotFound, "EntityReference nodes in <Reference> are unsupported."); } manifestNode = manifestNode->getNextSibling(); } if (manifestNode == 0 || !strEquals(getDSIGLocalName(manifestNode), "Manifest")) throw XSECException(XSECException::ExpectedDSIGChildNotFound, "Expected <Manifest> as child of <Object> for Manifest Type <Reference>"); } // Now have the manifest node, find the first reference and load! referenceNode = manifestNode->getFirstChild(); while (referenceNode != 0 && (referenceNode->getNodeType() != DOMNode::ELEMENT_NODE || !strEquals(getDSIGLocalName(referenceNode), "Reference"))) { if (referenceNode->getNodeType() == DOMNode::ENTITY_REFERENCE_NODE) { throw XSECException(XSECException::ExpectedDSIGChildNotFound, "EntityReference nodes in <Reference> are unsupported."); } referenceNode = referenceNode->getNextSibling(); } if (referenceNode == 0) throw XSECException(XSECException::ExpectedDSIGChildNotFound, "Expected <Reference> as child of <Manifest>"); // Have reference node, so lets create a list! mp_manifestList = DSIGReference::loadReferenceListFromXML(mp_env, referenceNode); } /* m_isManifest */ m_loaded = true; } // -------------------------------------------------------------------------------- // createReferenceListFromXML // -------------------------------------------------------------------------------- DSIGReferenceList *DSIGReference::loadReferenceListFromXML(const XSECEnv * env, DOMNode *firstReference) { // Have the first reference element in the document, // so want to find and load them all DOMNode *tmpRef = firstReference; DSIGReferenceList * refList; DSIGReference * r; XSECnew(refList, DSIGReferenceList()); Janitor<DSIGReferenceList> j_refList(refList); while (tmpRef != 0) { // Must be an element node if (tmpRef->getNodeType() != DOMNode::ELEMENT_NODE || !strEquals(getDSIGLocalName(tmpRef), "Reference")) { throw XSECException(XSECException::ExpectedDSIGChildNotFound, "Expected <Reference> as child of <SignedInfo>"); } XSECnew(r, DSIGReference(env, tmpRef)); refList->addReference(r); // Load the reference before moving on r->load(); // Find next element Node tmpRef = tmpRef->getNextSibling(); while (tmpRef != 0 && tmpRef->getNodeType() != DOMNode::ELEMENT_NODE) { if (tmpRef->getNodeType() == DOMNode::ENTITY_REFERENCE_NODE) { throw XSECException(XSECException::ExpectedDSIGChildNotFound, "EntityReference nodes in <Reference> are unsupported."); } tmpRef = tmpRef->getNextSibling(); } } j_refList.release(); return refList; } // -------------------------------------------------------------------------------- // Get the Canonicalised BYTE_STREAM of the reference data (and TXFMS) // -------------------------------------------------------------------------------- XSECBinTXFMInputStream * DSIGReference::makeBinInputStream(void) const { // First set up for input TXFMChain * txfmChain; TXFMBase * currentTxfm; if (m_loaded == false) { throw XSECException(XSECException::NotLoaded, "calculateHash() called in DSIGReference before load()"); } // Find base transform currentTxfm = getURIBaseTXFM(mp_referenceNode->getOwnerDocument(), mp_URI, mp_env); // Set up the transform chain txfmChain = createTXFMChainFromList(currentTxfm, mp_transformList); Janitor<TXFMChain> j_txfmChain(txfmChain); DOMDocument *d = mp_referenceNode->getOwnerDocument(); // All transforms done. If necessary, change the type from nodes to bytes if (txfmChain->getLastTxfm()->getOutputType() == TXFMBase::DOM_NODES) { TXFMC14n * c14n; XSECnew(c14n, TXFMC14n(d)); txfmChain->appendTxfm(c14n); } // Now create the InputStream XSECBinTXFMInputStream * ret; ret = new XSECBinTXFMInputStream(txfmChain); // Probs with MSVC++ mean no XSECnew j_txfmChain.release(); // Now owned by "ret" return ret; } // -------------------------------------------------------------------------------- // Hash a reference list // -------------------------------------------------------------------------------- void DSIGReference::hashReferenceList(const DSIGReferenceList *lst, bool interlocking) { // Run through a list of hashes and checkHash for each one DSIGReference * r; int i = (int) lst->getSize(); safeBuffer errStr; errStr.sbXMLChIn(DSIGConstants::s_unicodeStrEmpty); // Run a VERY naieve process at the moment that assumes the list will "settle" // after N iterations through the list. This will settle any inter-locking references // Where a hash in a later calculated reference could impact an already calculated hash // in a previous references // // If interlocking is set to false, assume there are no interacting <Reference> nodes do { for (int j = 0; j < i; ++j) { r = lst->item(j); // If this is a manifest we need to set all the references in the manifest as well if (r->isManifest()) hashReferenceList(r->getManifestReferenceList()); // Re-ordered as per suggestion by Peter Gubis to make it more likely // that hashes are correct on first pass when manifests are involved r->setHash(); } } while (interlocking && !DSIGReference::verifyReferenceList(lst, errStr) && i-- >= 0); } // -------------------------------------------------------------------------------- // Verify reference list // -------------------------------------------------------------------------------- bool DSIGReference::verifyReferenceList(const DSIGReferenceList * lst, safeBuffer &errStr) { // Run through a list of hashes and checkHash for each one XSEC_USING_XERCES(NetAccessorException); DSIGReference * r; bool res = true; int size = (lst ? (int) lst->getSize() : 0); for (int i = 0; i < size; ++i) { r = lst->item(i); try { if (!r->checkHash()) { // Failed errStr.sbXMLChCat("Reference URI=\""); errStr.sbXMLChCat(r->getURI()); errStr.sbXMLChCat("\" failed to verify\n"); res = false; } } catch (const NetAccessorException&) { res = false; errStr.sbXMLChCat("Error accessing network URI=\""); errStr.sbXMLChCat(r->getURI()); errStr.sbXMLChCat("\". Reference failed to verify\n"); } catch (const XSECException& e) { if (e.getType() != XSECException::HTTPURIInputStreamError) throw; res = false; errStr.sbXMLChCat("Error accessing network URI=\""); errStr.sbXMLChCat(r->getURI()); errStr.sbXMLChCat("\". Reference failed to verify\n"); } // if a manifest, check the manifest list if (r->isManifest()) res = res & verifyReferenceList(r->getManifestReferenceList(), errStr); } return res; } // -------------------------------------------------------------------------------- // processTransforms // -------------------------------------------------------------------------------- TXFMChain * DSIGReference::createTXFMChainFromList(TXFMBase * input, DSIGTransformList * lst) { TXFMChain * ret; XSECnew(ret, TXFMChain(input)); if (lst == NULL) return ret; Janitor<TXFMChain> j_ret(ret); DSIGTransformList::TransformListVectorType::size_type size, i; size = lst->getSize(); if (size > 0) { // Iterate through the list for (i = 0; i < size; ++i) { lst->item(i)->appendTransformer(ret); } } j_ret.release(); return ret; } // -------------------------------------------------------------------------------- // loadTransforms // -------------------------------------------------------------------------------- DSIGTransformList * DSIGReference::loadTransforms( DOMNode *transformsNode, XSECSafeBufferFormatter * formatter, const XSECEnv * env) { // This is defined as a static function, not because it makes use of any static variables // in the DSIGReference class, but to neatly link it to the other users if (transformsNode == 0 || (!strEquals(getDSIGLocalName(transformsNode), "Transforms") && !strEquals(getXENCLocalName(transformsNode), "Transforms"))) { throw XSECException(XSECException::ExpectedDSIGChildNotFound, "Expected <Transforms> in function DSIGReference::processTransforms"); } // Create the list DSIGTransformList * lst; XSECnew(lst, DSIGTransformList()); Janitor<DSIGTransformList> j_lst(lst); // Find First transform DOMNode * transforms = transformsNode->getFirstChild(); while (transforms != NULL && transforms->getNodeType() != DOMNode::ELEMENT_NODE) transforms = transforms->getNextSibling(); while (transforms != NULL) { // Process each transform in turn if (!strEquals(getDSIGLocalName(transforms), "Transform")) { // Not what we expected to see! safeBuffer tmp, error; error << (*formatter << getDSIGLocalName(transforms)); tmp.sbStrcpyIn("Unknown attribute in <Transforms> - Expected <Transform> found "); tmp.sbStrcatIn(error); tmp.sbStrcatIn(">."); throw XSECException(XSECException::ExpectedDSIGChildNotFound, error.rawCharBuffer()); } DOMNamedNodeMap * transformAtts = transforms->getAttributes(); unsigned int i; for (i = 0; i < transformAtts->getLength() && !strEquals(transformAtts->item(i)->getNodeName(), DSIGConstants::s_unicodeStrAlgorithm); ++i); if (i == transformAtts->getLength()) { throw XSECException(XSECException::ExpectedDSIGChildNotFound, "Algorithm attribute not found in <Transform> element"); } safeBuffer algorithm; algorithm << (*formatter << transformAtts->item(i)->getNodeValue()); // Determine what the transform is if (algorithm.sbStrcmp(URI_ID_BASE64) == 0) { DSIGTransformBase64 * b; XSECnew(b, DSIGTransformBase64(env, transforms)); lst->addTransform(b); b->load(); } else if (algorithm.sbStrcmp(URI_ID_XPATH) == 0) { DSIGTransformXPath * x; XSECnew(x, DSIGTransformXPath(env, transforms)); lst->addTransform(x); x->load(); } else if (algorithm.sbStrcmp(URI_ID_XPF) == 0) { DSIGTransformXPathFilter * xpf; XSECnew(xpf, DSIGTransformXPathFilter(env, transforms)); lst->addTransform(xpf); xpf->load(); } else if (algorithm.sbStrcmp(URI_ID_ENVELOPE) == 0) { DSIGTransformEnvelope * e; XSECnew(e, DSIGTransformEnvelope(env, transforms)); lst->addTransform(e); e->load(); } else if (algorithm.sbStrcmp(URI_ID_XSLT) == 0) { DSIGTransformXSL * x; XSECnew(x, DSIGTransformXSL(env, transforms)); lst->addTransform(x); x->load(); } else if (algorithm.sbStrcmp(URI_ID_C14N_COM) == 0 || algorithm.sbStrcmp(URI_ID_C14N_NOC) == 0 || algorithm.sbStrcmp(URI_ID_C14N11_COM) == 0 || algorithm.sbStrcmp(URI_ID_C14N11_NOC) == 0 || algorithm.sbStrcmp(URI_ID_EXC_C14N_COM) == 0 || algorithm.sbStrcmp(URI_ID_EXC_C14N_NOC) == 0) { DSIGTransformC14n * c; XSECnew(c, DSIGTransformC14n(env, transforms)); lst->addTransform(c); c->load(); } else { // Not what we expected to see! safeBuffer tmp; tmp.sbStrcpyIn("Unknown transform : "); tmp.sbStrcatIn(algorithm); tmp.sbStrcatIn(" found."); throw XSECException(XSECException::UnknownTransform, tmp.rawCharBuffer()); } // Now find next element transforms = transforms->getNextSibling(); while (transforms != NULL && transforms->getNodeType() != DOMNode::ELEMENT_NODE) transforms = transforms->getNextSibling(); } /* while (transforms != NULL) */ j_lst.release(); return lst; } // -------------------------------------------------------------------------------- // Set hash // -------------------------------------------------------------------------------- void DSIGReference::setHash() { // Set up if (mp_hashValueNode == 0) { throw XSECException(XSECException::NotLoaded, "setHash() called in DSIGReference before load()"); } unsigned int maxHashSize = XSECPlatformUtils::g_cryptoProvider->getMaxHashSize(); XSECCryptoBase64 * b64 = XSECPlatformUtils::g_cryptoProvider->base64(); if (!b64) { throw XSECException(XSECException::CryptoProviderError, "Error requesting Base64 object from Crypto Provider"); } Janitor<XSECCryptoBase64> j_b64(b64); // First determine the hash value XMLByte* calculatedHashVal = new XMLByte[maxHashSize]; XMLByte* base64Hash = new XMLByte[maxHashSize * 2]; unsigned int base64HashLen = 0; try { unsigned int calculatedHashLen = calculateHash(calculatedHashVal, maxHashSize); // Calculate the base64 value b64->encodeInit(); base64HashLen = b64->encode(calculatedHashVal, calculatedHashLen, base64Hash, maxHashSize * 2); base64HashLen += b64->encodeFinish(&base64Hash[base64HashLen], (maxHashSize * 2) - base64HashLen); } catch (...) { delete[] calculatedHashVal; delete[] base64Hash; throw; } delete[] calculatedHashVal; // Ensure the string is terminated if (base64Hash[base64HashLen-1] == '\n') base64Hash[base64HashLen-1] = '\0'; else base64Hash[base64HashLen] = '\0'; // Now find the correct text node to re-set DOMNode* tmpElt = mp_hashValueNode->getFirstChild(); while (tmpElt != NULL && tmpElt->getNodeType() != DOMNode::TEXT_NODE) tmpElt = tmpElt->getNextSibling(); if (tmpElt == NULL) { // Need to create the underlying TEXT_NODE DOMDocument *doc = mp_referenceNode->getOwnerDocument(); tmpElt = doc->createTextNode(MAKE_UNICODE_STRING((char *) base64Hash)); mp_hashValueNode->appendChild(tmpElt); } else { tmpElt->setNodeValue(MAKE_UNICODE_STRING((char *) base64Hash)); } delete[] base64Hash; } // -------------------------------------------------------------------------------- // Create hash // -------------------------------------------------------------------------------- unsigned int DSIGReference::calculateHash(XMLByte *toFill, unsigned int maxToFill) const { // Determine the hash value of the element // First set up for input TXFMBase * currentTxfm; TXFMChain * chain; unsigned int size; if (m_loaded == false) { throw XSECException(XSECException::NotLoaded, "calculateHash() called in DSIGReference before load()"); } // Find base transform currentTxfm = getURIBaseTXFM(mp_referenceNode->getOwnerDocument(), mp_URI, mp_env); // Now build the transforms list // Note this passes ownership of currentTxfm to the function, so it is the // responsibility of createTXFMChain to ensure it gets deleted if this throws. chain = createTXFMChainFromList(currentTxfm, mp_transformList); Janitor<TXFMChain> j_chain(chain); DOMDocument *d = mp_referenceNode->getOwnerDocument(); // All transforms done. If necessary, change the type from nodes to bytes if (chain->getLastTxfm()->getOutputType() == TXFMBase::DOM_NODES) { TXFMC14n * c14n; XSECnew(c14n, TXFMC14n(d)); chain->appendTxfm(c14n); } // Check to see if there is a final "application" transform prior to the hash if (mp_preHash != NULL) { chain->appendTxfm(mp_preHash); mp_preHash = NULL; // Can't be re-used } // Check for debugging sink for the data TXFMBase* sink = XSECPlatformUtils::GetReferenceLoggingSink(d); if (sink) chain->appendTxfm(sink); // Get the mapping for the hash transform const XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(mp_algorithmURI); if (handler == NULL) { throw XSECException(XSECException::SigVfyError, "Hash method unknown in DSIGReference::calculateHash()"); } if (!handler->appendHashTxfm(chain, mp_algorithmURI)) { throw XSECException(XSECException::SigVfyError, "Unexpected error in handler whilst appending Hash transform"); } // Now we have the hashing transform, run it. size = chain->getLastTxfm()->readBytes(toFill, maxToFill); // Clean out document if necessary chain->getLastTxfm()->deleteExpandedNameSpaces(); return size; } // -------------------------------------------------------------------------------- // Read hash // -------------------------------------------------------------------------------- unsigned int DSIGReference::readHash(XMLByte *toFill, unsigned int maxToFill) const { // Determine the hash value stored in the reference // First set up for input unsigned int size; DOMNode *tmpElt; //const XMLCh * stringHash; TXFMBase * nextInput; DOMDocument *d = mp_referenceNode->getOwnerDocument(); safeBuffer b64HashVal; // Find the hash value tmpElt = mp_referenceNode->getFirstChild(); while (tmpElt != 0 && !strEquals(getDSIGLocalName(tmpElt), "DigestValue")) tmpElt = tmpElt->getNextSibling(); if (tmpElt == NULL) // ERROR return 0; // Now read the DOMString of the hash tmpElt = tmpElt->getFirstChild(); while (tmpElt != NULL && tmpElt->getNodeType() != DOMNode::TEXT_NODE) tmpElt = tmpElt->getNextSibling(); if (tmpElt == NULL) // Something wrong with the underlying XML if no text was found throw XSECException(XSECException::NoHashFoundInDigestValue); b64HashVal << (*mp_formatter << tmpElt->getNodeValue()); // Now have the value of the string - create a transform around it XSECnew(nextInput, TXFMSB(d)); ((TXFMSB *) nextInput)->setInput(b64HashVal); // Create a transform chain (really as a janitor for the entire list) TXFMChain * chain; XSECnew(chain, TXFMChain(nextInput)); Janitor<TXFMChain> j_chain(chain); // Now create the base64 transform XSECnew(nextInput, TXFMBase64(d)); chain->appendTxfm(nextInput); // Now get the value size = chain->getLastTxfm()->readBytes(toFill, maxToFill); // Clear any documentat modifications chain->getLastTxfm()->deleteExpandedNameSpaces(); return size; } // -------------------------------------------------------------------------------- // Check a reference // -------------------------------------------------------------------------------- bool DSIGReference::checkHash() const { // Determine the hash value of the element and check if matches that stored in the // DigestValue part of the element // First set up for input unsigned int maxHashSize = XSECPlatformUtils::g_cryptoProvider->getMaxHashSize(); XMLByte* calculatedHashVal = new XMLByte[maxHashSize]; // The hash that we determined unsigned int calculatedHashSize; try { if ((calculatedHashSize = calculateHash(calculatedHashVal, maxHashSize)) == 0) return false; } catch (...) { delete[] calculatedHashVal; throw; } XMLByte* readHashVal = new XMLByte [maxHashSize]; // The hash in the element if (readHash(readHashVal, maxHashSize) != calculatedHashSize) { delete[] calculatedHashVal; delete[] readHashVal; return false; } for (unsigned int i = 0; i < calculatedHashSize; ++i) { if (calculatedHashVal[i] != readHashVal[i]) { delete[] calculatedHashVal; delete[] readHashVal; return false; } } delete[] calculatedHashVal; delete[] readHashVal; // Got through with flying colours! return true; }