/**
 * 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: DSIGReference.cpp 1496703 2013-06-26 00:58:41Z scantor $
 *
 */

// 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/TXFMSHA1.hpp>
#include <xsec/transformers/TXFMMD5.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/XSECDOMUtils.hpp>
#include <xsec/utils/XSECBinTXFMInputStream.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_referenceNode = dom;
	mp_env = env;

	// Should throw an exception if the node is not a REFERENCE element

	XSECnew(mp_formatter, XSECSafeBufferFormatter("UTF-8",XMLFormatter::NoEscapes,
												XMLFormatter::UnRep_CharRef));

	mp_preHash = NULL;
	mp_manifestList = NULL;
	me_hashMethod = HASH_NONE;
	mp_transformsNode = NULL;
	mp_transformList = NULL;
	mp_URI = NULL;
	m_isManifest = false;
	mp_algorithmURI = NULL;
	m_loaded = false;

}

DSIGReference::DSIGReference(const XSECEnv * env) {

	mp_env = env;
	mp_referenceNode = NULL;
	mp_transformsNode = NULL;
	mp_transformList = NULL;

	XSECnew(mp_formatter, XSECSafeBufferFormatter("UTF-8",XMLFormatter::NoEscapes,
											XMLFormatter::UnRep_CharRef));

	mp_preHash = NULL;		// By default no "special" transform
	mp_manifestList = NULL;
	me_hashMethod = HASH_NONE;
	mp_URI = NULL;
	m_isManifest = false;
	mp_algorithmURI = NULL;
	m_loaded = false;

};

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(canonicalizationMethod cm) {

	DOMElement *txfmElt;
	DSIGTransformC14n * txfm;

	XSECnew(txfm, DSIGTransformC14n(mp_env));
	txfmElt = txfm->createBlankTransform(mp_env->getParentDocument());
	txfm->setCanonicalizationMethod(cm);

	addTransform(txfm, txfmElt);

	return txfm;

}

DSIGTransformC14n * DSIGReference::appendCanonicalizationTransform(
		const XMLCh * canonicalizationAlgorithmURI) {

	canonicalizationMethod cm;
	XSECmapURIToCanonicalizationMethod(canonicalizationAlgorithmURI, cm);

	return appendCanonicalizationTransform(cm);

}

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, hashMethod hm, char * type) {

	// Deprecated - use the algorithm URI based method instead

	safeBuffer hURI;

	if (hashMethod2URI(hURI, hm) == false) {
		throw XSECException(XSECException::UnknownSignatureAlgorithm,
			"DSIGReference::createBlankReference - Hash method unknown");
	}

	return createBlankReference(URI, hURI.sbStrToXMLCh(), MAKE_UNICODE_STRING(type));

}

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;

	XSECmapURIToHashMethod(hashAlgorithmURI, me_hashMethod);

	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() {

	return m_isManifest;

}

// --------------------------------------------------------------------------------
//           getURI
// --------------------------------------------------------------------------------

const XMLCh * DSIGReference::getURI() {

	return mp_URI;

}

// --------------------------------------------------------------------------------
//           getManifestReferenceList
// --------------------------------------------------------------------------------

DSIGReferenceList * DSIGReference::getManifestReferenceList() {

	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) {

			xsecsize_t len = XMLString::stringLen(&URI[14]);

			XMLCh* tmp = new XMLCh[len + 1];
			ArrayJanitor<XMLCh> j_tmp(tmp);

			xsecsize_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();

	// Determine hashing algorithm
	XSECmapURIToHashMethod(mp_algorithmURI, me_hashMethod);

	// 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(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(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 (NetAccessorException e) {

			res = false;

			errStr.sbXMLChCat("Error accessing network URI=\"");
			errStr.sbXMLChCat(r->getURI());
			errStr.sbXMLChCat("\".  Reference failed to verify\n");

		}
		catch (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(void) {

	// First determine the hash value
	XMLByte calculatedHashVal[CRYPTO_MAX_HASH_SIZE];	// The hash that we determined
	unsigned int calculatedHashLen;
	XMLByte base64Hash [CRYPTO_MAX_HASH_SIZE * 2];
	unsigned int base64HashLen;

	calculatedHashLen = calculateHash(calculatedHashVal, CRYPTO_MAX_HASH_SIZE);

	// Calculate the base64 value

	XSECCryptoBase64 *	b64 = XSECPlatformUtils::g_cryptoProvider->base64();

	if (!b64) {

		throw XSECException(XSECException::CryptoProviderError,
				"Error requesting Base64 object from Crypto Provider");

	}

	Janitor<XSECCryptoBase64> j_b64(b64);

	b64->encodeInit();
	base64HashLen = b64->encode(calculatedHashVal,
								calculatedHashLen,
								base64Hash,
								CRYPTO_MAX_HASH_SIZE * 2);
	base64HashLen += b64->encodeFinish(&base64Hash[base64HashLen],
										(CRYPTO_MAX_HASH_SIZE * 2) - base64HashLen);

	// 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;

	if (mp_hashValueNode == 0) {

		throw XSECException(XSECException::NotLoaded,
			"setHash() called in DSIGReference before load()");

	}

	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));
	}

}

void DSIGReference::setExternalHash(XMLByte *hash)
{

	XMLByte base64Hash [CRYPTO_MAX_HASH_SIZE * 2];
	unsigned int base64HashLen;
	// Calculate the base64 value

	XSECCryptoBase64 *	b64 = XSECPlatformUtils::g_cryptoProvider->base64();

	if (!b64) {

		throw XSECException(XSECException::CryptoProviderError,
				"Error requesting Base64 object from Crypto Provider");

	}

	Janitor<XSECCryptoBase64> j_b64(b64);

	b64->encodeInit();
	base64HashLen = b64->encode(hash,
								32,  //SHA-256 Hash assumed
								base64Hash,
								CRYPTO_MAX_HASH_SIZE * 2);
	base64HashLen += b64->encodeFinish(&base64Hash[base64HashLen],
										(CRYPTO_MAX_HASH_SIZE * 2) - base64HashLen);

	// 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;

	if (mp_hashValueNode == 0) {

		throw XSECException(XSECException::NotLoaded,
			"setHash() called in DSIGReference before load()");

	}

	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));
	}
}




// --------------------------------------------------------------------------------
//           Create hash
// --------------------------------------------------------------------------------

unsigned int DSIGReference::calculateHash(XMLByte *toFill, unsigned int maxToFill) {

	// 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

	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) {

	// 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() {

	// 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

	XMLByte calculatedHashVal[CRYPTO_MAX_HASH_SIZE];		// The hash that we determined
	XMLByte readHashVal[CRYPTO_MAX_HASH_SIZE];			// The hash in the element

	unsigned int calculatedHashSize, i;

	if ((calculatedHashSize = calculateHash(calculatedHashVal, CRYPTO_MAX_HASH_SIZE)) == 0)
		return false;

	if (readHash(readHashVal, CRYPTO_MAX_HASH_SIZE) != calculatedHashSize)
		return false;

	for (i = 0; i < calculatedHashSize; ++i) {
		if (calculatedHashVal[i] != readHashVal[i])
			return false;

	}

	// Got through with flying colours!
	return true;

}





