blob: 71f6926b81e62b9b9a48d50655f8cb80461e75ce [file] [log] [blame]
/**
* 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/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 (URI[j] != '\'') {
tmp[i++] = URI[j++];
}
tmp[i] = XERCES_CPP_NAMESPACE_QUALIFIER chNull;
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));
}
}
// --------------------------------------------------------------------------------
// 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;
}