/** 
 * XML Security Library (http://www.aleksey.com/xmlsec).
 *
 * <dsig:KeyInfo/> element processing 
 * (http://www.w3.org/TR/xmlSec-core/#sec-KeyInfo:
 *
 * The KeyInfo Element
 *
 * KeyInfo is an optional element that enables the recipient(s) to obtain 
 * the key needed to validate the signature.  KeyInfo may contain keys, 
 * names, certificates and other public key management information, such as 
 * in-band key distribution or key agreement data. 
 * 
 *  Schema Definition:
 *
 *  <element name="KeyInfo" type="ds:KeyInfoType"/> 
 *  <complexType name="KeyInfoType" mixed="true">
 *    <choice maxOccurs="unbounded"> 
 *       <element ref="ds:KeyName"/> 
 *       <element ref="ds:KeyValue"/> 
 *       <element ref="ds:RetrievalMethod"/> 
 *       <element ref="ds:X509Data"/> 
 *       <element ref="ds:PGPData"/> 
 *       <element ref="ds:SPKIData"/>
 *       <element ref="ds:MgmtData"/>
 *       <any processContents="lax" namespace="##other"/>
 *       <!-- (1,1) elements from (0,unbounded) namespaces -->
 *    </choice>
 *    <attribute name="Id" type="ID" use="optional"/>
 *  </complexType>
 *    
 * DTD:
 *    
 * <!ELEMENT KeyInfo (#PCDATA|KeyName|KeyValue|RetrievalMethod|
 *                    X509Data|PGPData|SPKIData|MgmtData %KeyInfo.ANY;)* >      
 * <!ATTLIST KeyInfo  Id  ID   #IMPLIED >
 *  
 *
 * This is free software; see Copyright file in the source
 * distribution for preciese wording.
 * 
 * Copyright (C) 2002-2003 Aleksey Sanin <aleksey@aleksey.com>
 */
#include "globals.h"

#include <stdlib.h>
#include <string.h>
 
#include <libxml/tree.h>

#include <xmlsec/xmlsec.h>
#include <xmlsec/xmltree.h>
#include <xmlsec/base64.h>
#include <xmlsec/keys.h>
#include <xmlsec/keysmngr.h>
#include <xmlsec/transforms.h>
#include <xmlsec/xmlenc.h>
#include <xmlsec/keyinfo.h>
#include <xmlsec/errors.h>


/**************************************************************************
 *
 * Hi level functions
 *
 *************************************************************************/
/**
 * xmlSecKeyInfoNodeRead:
 * @keyInfoNode: 	the pointer to <dsig:KeyInfo/> node.
 * @key:		the pointer to result key object.
 * @keyInfoCtx:		the pointer to <dsig:KeyInfo/> element processing context.
 *
 * Parses the <dsig:KeyInfo/> element @keyInfoNode, extracts the key data
 * and stores into @key.
 *
 * Returns: 0 on success or -1 if an error occurs.
 */
int
xmlSecKeyInfoNodeRead(xmlNodePtr keyInfoNode, xmlSecKeyPtr key, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    const xmlChar* nodeName;
    const xmlChar* nodeNs;
    xmlSecKeyDataId dataId;
    xmlNodePtr cur;
    int ret;
    
    xmlSecAssert2(keyInfoNode != NULL, -1);
    xmlSecAssert2(key != NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);
    xmlSecAssert2(keyInfoCtx->mode == xmlSecKeyInfoModeRead, -1);

    for(cur = xmlSecGetNextElementNode(keyInfoNode->children); 
	(cur != NULL) && 
	(((keyInfoCtx->flags & XMLSEC_KEYINFO_FLAGS_DONT_STOP_ON_KEY_FOUND) != 0) || 
	 (xmlSecKeyIsValid(key) == 0) || 
	 (xmlSecKeyMatch(key, NULL, &(keyInfoCtx->keyReq)) == 0));
	cur = xmlSecGetNextElementNode(cur->next)) {
    
	/* find data id */
	nodeName = cur->name;
	nodeNs = xmlSecGetNodeNsHref(cur);
	
	/* use global list only if we don't have a local one */
	if(xmlSecPtrListGetSize(&(keyInfoCtx->enabledKeyData)) > 0) {
	    dataId = xmlSecKeyDataIdListFindByNode(&(keyInfoCtx->enabledKeyData),
			    nodeName, nodeNs, xmlSecKeyDataUsageKeyInfoNodeRead);
	} else {	
    	    dataId = xmlSecKeyDataIdListFindByNode(xmlSecKeyDataIdsGet(),
			    nodeName, nodeNs, xmlSecKeyDataUsageKeyInfoNodeRead);
	}
	if(dataId != xmlSecKeyDataIdUnknown) {
	    /* read data node */
	    ret = xmlSecKeyDataXmlRead(dataId, key, cur, keyInfoCtx);
	    if(ret < 0) {
		xmlSecError(XMLSEC_ERRORS_HERE,
			    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(dataId)),
			    "xmlSecKeyDataXmlRead",
			    XMLSEC_ERRORS_R_XMLSEC_FAILED,
			    "node=%s", 
			    xmlSecErrorsSafeString(xmlSecNodeGetName(cur)));
		return(-1);
	    }
	} else if((keyInfoCtx->flags & XMLSEC_KEYINFO_FLAGS_STOP_ON_UNKNOWN_CHILD) != 0) {
	    /* there is a laxi schema validation but application may
	     * desire to disable unknown nodes*/
	    xmlSecError(XMLSEC_ERRORS_HERE,
			NULL,
			xmlSecErrorsSafeString(xmlSecNodeGetName(cur)),
			XMLSEC_ERRORS_R_INVALID_NODE,
			XMLSEC_ERRORS_NO_MESSAGE);
	    return(-1);
	}
    }
    
    return(0);    
}

/**
 * xmlSecKeyInfoNodeWrite:
 * @keyInfoNode: 	the pointer to <dsig:KeyInfo/> node.
 * @key:		the pointer to key object.
 * @keyInfoCtx:		the pointer to <dsig:KeyInfo/> element processing context.
 *
 * Writes the @key into the <dsig:KeyInfo/> element template @keyInfoNode.
 *
 * Returns: 0 on success or -1 if an error occurs.
 */
int 
xmlSecKeyInfoNodeWrite(xmlNodePtr keyInfoNode, xmlSecKeyPtr key, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    const xmlChar* nodeName;
    const xmlChar* nodeNs;
    xmlSecKeyDataId dataId;
    xmlNodePtr cur;
    int ret;
    
    xmlSecAssert2(keyInfoNode != NULL, -1);
    xmlSecAssert2(key != NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);
    xmlSecAssert2(keyInfoCtx->mode == xmlSecKeyInfoModeWrite, -1);

    for(cur = xmlSecGetNextElementNode(keyInfoNode->children); 
	cur != NULL;
	cur = xmlSecGetNextElementNode(cur->next)) {
    
	/* find data id */
	nodeName = cur->name;
	nodeNs = xmlSecGetNodeNsHref(cur);

	/* use global list only if we don't have a local one */
	if(xmlSecPtrListGetSize(&(keyInfoCtx->enabledKeyData)) > 0) {
        	dataId = xmlSecKeyDataIdListFindByNode(&(keyInfoCtx->enabledKeyData),
			    nodeName, nodeNs, 
			    xmlSecKeyDataUsageKeyInfoNodeWrite);
	} else {
        	dataId = xmlSecKeyDataIdListFindByNode(xmlSecKeyDataIdsGet(),
			    nodeName, nodeNs, 
			    xmlSecKeyDataUsageKeyInfoNodeWrite);
	}
	if(dataId != xmlSecKeyDataIdUnknown) {
	    ret = xmlSecKeyDataXmlWrite(dataId, key, cur, keyInfoCtx);
	    if(ret < 0) {
		xmlSecError(XMLSEC_ERRORS_HERE,
			    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(dataId)),
			    "xmlSecKeyDataXmlWrite",
			    XMLSEC_ERRORS_R_XMLSEC_FAILED,
			    "node=%s", 
			    xmlSecErrorsSafeString(xmlSecNodeGetName(cur)));
		return(-1);
	    }
	} else if((keyInfoCtx->flags & XMLSEC_KEYINFO_FLAGS_STOP_ON_UNKNOWN_CHILD) != 0) {
	    /* laxi schema validation but application can disable it*/
	    xmlSecError(XMLSEC_ERRORS_HERE,
			NULL,
			xmlSecErrorsSafeString(xmlSecNodeGetName(cur)),
			XMLSEC_ERRORS_R_INVALID_NODE,
			XMLSEC_ERRORS_NO_MESSAGE);
	    return(-1);
	}
    }
    
    return(0);
} 

/**************************************************************************
 *
 * KeyInfo context
 *
 *************************************************************************/
/**
 * xmlSecKeyInfoCtxCreate:
 * @keysMngr: 		the pointer to keys manager (may be NULL).
 *
 * Allocates and initializes <dsig:KeyInfo/> element processing context.
 * Caller is responsible for freeing it by calling #xmlSecKeyInfoCtxDestroy 
 * function.
 *
 * Returns: pointer to newly allocated object or NULL if an error occurs.
 */
xmlSecKeyInfoCtxPtr 
xmlSecKeyInfoCtxCreate(xmlSecKeysMngrPtr keysMngr) {
    xmlSecKeyInfoCtxPtr keyInfoCtx;
    int ret;
    
    /* Allocate a new xmlSecKeyInfoCtx and fill the fields. */
    keyInfoCtx = (xmlSecKeyInfoCtxPtr)xmlMalloc(sizeof(xmlSecKeyInfoCtx));
    if(keyInfoCtx == NULL) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    NULL,
		    XMLSEC_ERRORS_R_MALLOC_FAILED,
		    "size=%d", sizeof(xmlSecKeyInfoCtx)); 
	return(NULL);
    }
    
    ret = xmlSecKeyInfoCtxInitialize(keyInfoCtx, keysMngr);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecKeyInfoCtxInitialize",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	xmlSecKeyInfoCtxDestroy(keyInfoCtx);
	return(NULL);
    }
    
    return(keyInfoCtx);
}

/** 
 * xmlSecKeyInfoCtxDestroy:
 * @keyInfoCtx:		the pointer to <dsig:KeyInfo/> element processing context.
 *
 * Destroys @keyInfoCtx object created with #xmlSecKeyInfoCtxCreate function.
 */
void 
xmlSecKeyInfoCtxDestroy(xmlSecKeyInfoCtxPtr keyInfoCtx) {
    xmlSecAssert(keyInfoCtx != NULL);
    
    xmlSecKeyInfoCtxFinalize(keyInfoCtx);
    xmlFree(keyInfoCtx);
}

/** 
 * xmlSecKeyInfoCtxInitialize:
 * @keyInfoCtx:		the pointer to <dsig:KeyInfo/> element processing context.
 * @keysMngr: 		the pointer to keys manager (may be NULL).
 *
 * Initializes <dsig:KeyInfo/> element processing context. Caller is 
 * responsible for cleaning it up by #xmlSecKeyInfoCtxFinalize function.
 * 
 * Returns: 0 on success and a negative value if an error occurs.
 */
int 
xmlSecKeyInfoCtxInitialize(xmlSecKeyInfoCtxPtr keyInfoCtx, xmlSecKeysMngrPtr keysMngr) {
    int ret;
    
    xmlSecAssert2(keyInfoCtx != NULL, -1);
    
    memset(keyInfoCtx, 0, sizeof(xmlSecKeyInfoCtx));
    keyInfoCtx->keysMngr = keysMngr;
    keyInfoCtx->base64LineSize = xmlSecBase64GetDefaultLineSize();    
    ret = xmlSecPtrListInitialize(&(keyInfoCtx->enabledKeyData), xmlSecKeyDataIdListId);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecPtrListInitialize",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }

    keyInfoCtx->maxRetrievalMethodLevel = 1;
    ret = xmlSecTransformCtxInitialize(&(keyInfoCtx->retrievalMethodCtx));
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecTransformCtxInitialize",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }

#ifndef XMLSEC_NO_XMLENC
    keyInfoCtx->maxEncryptedKeyLevel = 1;
#endif /* XMLSEC_NO_XMLENC */

#ifndef XMLSEC_NO_X509
    keyInfoCtx->certsVerificationDepth= 9;
#endif /* XMLSEC_NO_X509 */

    ret = xmlSecKeyReqInitialize(&(keyInfoCtx->keyReq));
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecKeyReqInitialize",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }
        
    return(0);
}

/** 
 * xmlSecKeyInfoCtxFinalize:
 * @keyInfoCtx:		the pointer to <dsig:KeyInfo/> element processing context.
 *
 * Cleans up the @keyInfoCtx initialized with #xmlSecKeyInfoCtxInitialize
 * function.
 */
void 
xmlSecKeyInfoCtxFinalize(xmlSecKeyInfoCtxPtr keyInfoCtx) {
    xmlSecAssert(keyInfoCtx != NULL);
    
    xmlSecPtrListFinalize(&(keyInfoCtx->enabledKeyData));
    xmlSecTransformCtxFinalize(&(keyInfoCtx->retrievalMethodCtx));
    xmlSecKeyReqFinalize(&(keyInfoCtx->keyReq));

#ifndef XMLSEC_NO_XMLENC
    if(keyInfoCtx->encCtx != NULL) {
        xmlSecEncCtxDestroy(keyInfoCtx->encCtx);
    }
#endif /* XMLSEC_NO_XMLENC */

    memset(keyInfoCtx, 0, sizeof(xmlSecKeyInfoCtx));
}

/** 
 * xmlSecKeyInfoCtxReset:
 * @keyInfoCtx:		the pointer to <dsig:KeyInfo/> element processing context.
 * 
 * Resets the @keyInfoCtx state. User settings are not changed.
 */
void 
xmlSecKeyInfoCtxReset(xmlSecKeyInfoCtxPtr keyInfoCtx) {
    xmlSecAssert(keyInfoCtx != NULL);
    
    xmlSecTransformCtxReset(&(keyInfoCtx->retrievalMethodCtx));
    keyInfoCtx->curRetrievalMethodLevel = 0;

#ifndef XMLSEC_NO_XMLENC
    if(keyInfoCtx->encCtx != NULL) { 	   
        xmlSecEncCtxReset(keyInfoCtx->encCtx);
    }
    keyInfoCtx->curEncryptedKeyLevel = 0;
#endif /* XMLSEC_NO_XMLENC */
    
    xmlSecKeyReqReset(&(keyInfoCtx->keyReq));
}

/** 
 * xmlSecKeyInfoCtxCreateEncCtx:
 * @keyInfoCtx:		the pointer to <dsig:KeyInfo/> element processing context.
 * 
 * Creates encryption context form processing <enc:EncryptedKey/> child
 * of <dsig:KeyInfo/> element.
 * 
 * Returns: 0 on success and a negative value if an error occurs.
 */
int 
xmlSecKeyInfoCtxCreateEncCtx(xmlSecKeyInfoCtxPtr keyInfoCtx) {
#ifndef XMLSEC_NO_XMLENC
    xmlSecEncCtxPtr tmp;
    int ret;
    
    xmlSecAssert2(keyInfoCtx != NULL, -1);
    xmlSecAssert2(keyInfoCtx->encCtx == NULL, -1);

    /* we have to use tmp variable to avoid a recursive loop */ 
    tmp = xmlSecEncCtxCreate(keyInfoCtx->keysMngr);
    if(tmp == NULL) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecEncCtxCreate",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }
    tmp->mode = xmlEncCtxModeEncryptedKey;
        
    /* copy user preferences from our current ctx */
    switch(keyInfoCtx->mode) {
	case xmlSecKeyInfoModeRead:
	    ret = xmlSecKeyInfoCtxCopyUserPref(&(tmp->keyInfoReadCtx), keyInfoCtx);
	    if(ret < 0) {
		xmlSecError(XMLSEC_ERRORS_HERE,
			    NULL,
			    "xmlSecKeyInfoCtxCopyUserPref",
			    XMLSEC_ERRORS_R_XMLSEC_FAILED,
			    XMLSEC_ERRORS_NO_MESSAGE);
		xmlSecEncCtxDestroy(tmp);
		return(-1);
	    }    
	    break;
	case xmlSecKeyInfoModeWrite:
	    ret = xmlSecKeyInfoCtxCopyUserPref(&(tmp->keyInfoWriteCtx), keyInfoCtx);
	    if(ret < 0) {
		xmlSecError(XMLSEC_ERRORS_HERE,
			    NULL,
			    "xmlSecKeyInfoCtxCopyUserPref",
			    XMLSEC_ERRORS_R_XMLSEC_FAILED,
			    XMLSEC_ERRORS_NO_MESSAGE);
		xmlSecEncCtxDestroy(tmp);
		return(-1);
	    }
	    break;
    }    
    keyInfoCtx->encCtx = tmp;
        
    return(0);
#else /* XMLSEC_NO_XMLENC */    
    xmlSecError(XMLSEC_ERRORS_HERE,
		NULL,
		"xml encryption",
		XMLSEC_ERRORS_R_DISABLED,
		XMLSEC_ERRORS_NO_MESSAGE);
    return(-1);
#endif /* XMLSEC_NO_XMLENC */    
}

/** 
 * xmlSecKeyInfoCtxCopyUserPref:
 * @dst:		the pointer to destination context object.
 * @src:		the pointer to source context object.
 *
 * Copies user preferences from @src context to @dst context.
 *  
 * Returns: 0 on success and a negative value if an error occurs.
 */
int 
xmlSecKeyInfoCtxCopyUserPref(xmlSecKeyInfoCtxPtr dst, xmlSecKeyInfoCtxPtr src) {
    int ret;
    
    xmlSecAssert2(dst != NULL, -1);
    xmlSecAssert2(src != NULL, -1);
    
    dst->userData 	= src->userData;
    dst->flags		= src->flags;
    dst->flags2		= src->flags2;
    dst->keysMngr	= src->keysMngr;
    dst->mode		= src->mode;
    dst->base64LineSize	= src->base64LineSize;

    ret = xmlSecPtrListCopy(&(dst->enabledKeyData), &(src->enabledKeyData));
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecPtrListCopy",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    "enabledKeyData");    
	return(-1);
    }
    
    /* <dsig:RetrievalMethod/> */
    dst->maxRetrievalMethodLevel= src->maxRetrievalMethodLevel;
    ret = xmlSecTransformCtxCopyUserPref(&(dst->retrievalMethodCtx), 
					 &(src->retrievalMethodCtx));
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecTransformCtxCopyUserPref",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    "enabledKeyData");    
	return(-1);
    }

    /* <enc:EncryptedContext /> */    
#ifndef XMLSEC_NO_XMLENC
    xmlSecAssert2(dst->encCtx == NULL, -1);
    if(src->encCtx != NULL) { 	   	
	dst->encCtx = xmlSecEncCtxCreate(dst->keysMngr);
	if(dst->encCtx == NULL) {
	    xmlSecError(XMLSEC_ERRORS_HERE,
		        NULL,
			"xmlSecEncCtxCreate",
		        XMLSEC_ERRORS_R_XMLSEC_FAILED,
		        XMLSEC_ERRORS_NO_MESSAGE);
	    return(-1);
	}
	
	dst->encCtx->mode = xmlEncCtxModeEncryptedKey;
        ret = xmlSecEncCtxCopyUserPref(dst->encCtx, src->encCtx);
	if(ret < 0) {
	    xmlSecError(XMLSEC_ERRORS_HERE,
		        NULL,
			"xmlSecEncCtxCopyUserPref",
		        XMLSEC_ERRORS_R_XMLSEC_FAILED,
		        XMLSEC_ERRORS_NO_MESSAGE);
	    return(-1);
	}
    }
    dst->maxEncryptedKeyLevel	= src->maxEncryptedKeyLevel;
#endif /* XMLSEC_NO_XMLENC */

    /* <dsig:X509Data /> */    
#ifndef XMLSEC_NO_X509
    dst->certsVerificationTime	= src->certsVerificationTime;
    dst->certsVerificationDepth	= src->certsVerificationDepth;
#endif /* XMLSEC_NO_X509 */
    
    return(0);
}

/** 
 * xmlSecKeyInfoCtxDebugDump:
 * @keyInfoCtx:		the pointer to <dsig:KeyInfo/> element processing context.
 * @output:		the output file pointer.
 *
 * Prints user settings and current context state to @output.
 */
void 
xmlSecKeyInfoCtxDebugDump(xmlSecKeyInfoCtxPtr keyInfoCtx, FILE* output) {
    xmlSecAssert(keyInfoCtx != NULL);
    xmlSecAssert(output != NULL);

    switch(keyInfoCtx->mode) {
	case xmlSecKeyInfoModeRead:
	    fprintf(output, "= KEY INFO READ CONTEXT\n");
	    break;
	case xmlSecKeyInfoModeWrite:
	    fprintf(output, "= KEY INFO WRITE CONTEXT\n");
	    break;
    }
    
    fprintf(output, "== flags: 0x%08x\n", keyInfoCtx->flags);
    fprintf(output, "== flags2: 0x%08x\n", keyInfoCtx->flags2);
    if(xmlSecPtrListGetSize(&(keyInfoCtx->enabledKeyData)) > 0) {
	fprintf(output, "== enabled key data: ");
	xmlSecKeyDataIdListDebugDump(&(keyInfoCtx->enabledKeyData), output);
    } else {
	fprintf(output, "== enabled key data: all\n");
    }
    fprintf(output, "== RetrievalMethod level (cur/max): %d/%d\n",
	    keyInfoCtx->curRetrievalMethodLevel, 
	    keyInfoCtx->maxRetrievalMethodLevel);
    xmlSecTransformCtxDebugDump(&(keyInfoCtx->retrievalMethodCtx), output);
    
#ifndef XMLSEC_NO_XMLENC
    fprintf(output, "== EncryptedKey level (cur/max): %d/%d\n",
	    keyInfoCtx->curEncryptedKeyLevel, 
	    keyInfoCtx->maxEncryptedKeyLevel);
    if(keyInfoCtx->encCtx != NULL) {
	xmlSecEncCtxDebugDump(keyInfoCtx->encCtx, output);
    }
#endif /* XMLSEC_NO_XMLENC */

    xmlSecKeyReqDebugDump(&(keyInfoCtx->keyReq), output);
}

/** 
 * xmlSecKeyInfoCtxDebugXmlDump:
 * @keyInfoCtx:		the pointer to <dsig:KeyInfo/> element processing context.
 * @output:		the output file pointer.
 *
 * Prints user settings and current context state in XML format to @output. 
 */
void 
xmlSecKeyInfoCtxDebugXmlDump(xmlSecKeyInfoCtxPtr keyInfoCtx, FILE* output) {
    xmlSecAssert(keyInfoCtx != NULL);
    xmlSecAssert(output != NULL);

    switch(keyInfoCtx->mode) {
	case xmlSecKeyInfoModeRead:
	    fprintf(output, "<KeyInfoReadContext>\n");
	    break;
	case xmlSecKeyInfoModeWrite:
	    fprintf(output, "<KeyInfoWriteContext>\n");
	    break;
    }
    	    
    fprintf(output, "<Flags>%08x</Flags>\n", keyInfoCtx->flags);
    fprintf(output, "<Flags2>%08x</Flags2>\n", keyInfoCtx->flags2);
    if(xmlSecPtrListGetSize(&(keyInfoCtx->enabledKeyData)) > 0) {
	fprintf(output, "<EnabledKeyData>\n");
	xmlSecKeyDataIdListDebugXmlDump(&(keyInfoCtx->enabledKeyData), output);
	fprintf(output, "</EnabledKeyData>\n");
    } else {
	fprintf(output, "<EnabledKeyData>all</EnabledKeyData>\n");
    }

    fprintf(output, "<RetrievalMethodLevel cur=\"%d\" max=\"%d\" />\n",
	    keyInfoCtx->curRetrievalMethodLevel, 
	    keyInfoCtx->maxRetrievalMethodLevel);
    xmlSecTransformCtxDebugXmlDump(&(keyInfoCtx->retrievalMethodCtx), output);

#ifndef XMLSEC_NO_XMLENC
    fprintf(output, "<EncryptedKeyLevel cur=\"%d\" max=\"%d\" />\n",
	    keyInfoCtx->curEncryptedKeyLevel, 
	    keyInfoCtx->maxEncryptedKeyLevel);
    if(keyInfoCtx->encCtx != NULL) {
	xmlSecEncCtxDebugXmlDump(keyInfoCtx->encCtx, output);
    }
#endif /* XMLSEC_NO_XMLENC */
    
    xmlSecKeyReqDebugXmlDump(&(keyInfoCtx->keyReq), output);
    switch(keyInfoCtx->mode) {
	case xmlSecKeyInfoModeRead:
	    fprintf(output, "</KeyInfoReadContext>\n");
	    break;
	case xmlSecKeyInfoModeWrite:
	    fprintf(output, "</KeyInfoWriteContext>\n");
	    break;
    }
}

/**************************************************************************
 *
 * <dsig:KeyName/> processing
 *
 *************************************************************************/
static int			xmlSecKeyDataNameXmlRead	(xmlSecKeyDataId id,
								 xmlSecKeyPtr key,
								 xmlNodePtr node,
								 xmlSecKeyInfoCtxPtr keyInfoCtx);
static int			xmlSecKeyDataNameXmlWrite	(xmlSecKeyDataId id,
								 xmlSecKeyPtr key,
								 xmlNodePtr node,
								 xmlSecKeyInfoCtxPtr keyInfoCtx);

static xmlSecKeyDataKlass xmlSecKeyDataNameKlass = {
    sizeof(xmlSecKeyDataKlass),
    sizeof(xmlSecKeyData),

    /* data */
    xmlSecNameKeyName,
    xmlSecKeyDataUsageKeyInfoNode, 		/* xmlSecKeyDataUsage usage; */
    NULL,					/* const xmlChar* href; */
    xmlSecNodeKeyName,				/* const xmlChar* dataNodeName; */
    xmlSecDSigNs,				/* const xmlChar* dataNodeNs; */
    
    /* constructors/destructor */
    NULL,					/* xmlSecKeyDataInitializeMethod initialize; */
    NULL,					/* xmlSecKeyDataDuplicateMethod duplicate; */
    NULL,					/* xmlSecKeyDataFinalizeMethod finalize; */
    NULL,					/* xmlSecKeyDataGenerateMethod generate; */
    
    /* get info */
    NULL,					/* xmlSecKeyDataGetTypeMethod getType; */
    NULL,					/* xmlSecKeyDataGetSizeMethod getSize; */
    NULL,					/* xmlSecKeyDataGetIdentifier getIdentifier; */    

    /* read/write */
    xmlSecKeyDataNameXmlRead,			/* xmlSecKeyDataXmlReadMethod xmlRead; */
    xmlSecKeyDataNameXmlWrite,   		/* xmlSecKeyDataXmlWriteMethod xmlWrite; */
    NULL,					/* xmlSecKeyDataBinReadMethod binRead; */
    NULL,					/* xmlSecKeyDataBinWriteMethod binWrite; */

    /* debug */
    NULL,					/* xmlSecKeyDataDebugDumpMethod debugDump; */
    NULL,					/* xmlSecKeyDataDebugDumpMethod debugXmlDump; */

    /* reserved for the future */
    NULL,					/* void* reserved0; */
    NULL,					/* void* reserved1; */
};

/** 
 * xmlSecKeyDataNameGetKlass:
 *
 * The <dsig:KeyName/> element key data klass 
 * (http://www.w3.org/TR/xmldsig-core/#sec-KeyName):
 *
 * The KeyName element contains a string value (in which white space is 
 * significant) which may be used by the signer to communicate a key 
 * identifier to the recipient. Typically, KeyName contains an identifier 
 * related to the key pair used to sign the message, but it may contain 
 * other protocol-related information that indirectly identifies a key pair. 
 * (Common uses of KeyName include simple string names for keys, a key index, 
 * a distinguished name (DN), an email address, etc.) 
 *
 * Returns: the <dsig:KeyName/> element processing key data klass.
 */
xmlSecKeyDataId 
xmlSecKeyDataNameGetKlass(void) {
    return(&xmlSecKeyDataNameKlass);
}

static int 
xmlSecKeyDataNameXmlRead(xmlSecKeyDataId id, xmlSecKeyPtr key, xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    const xmlChar* oldName;
    xmlChar* newName;
    int ret;

    xmlSecAssert2(id == xmlSecKeyDataNameId, -1);
    xmlSecAssert2(key != NULL, -1);
    xmlSecAssert2(node != NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);
    xmlSecAssert2(keyInfoCtx->mode == xmlSecKeyInfoModeRead, -1);

    oldName = xmlSecKeyGetName(key);
    newName = xmlNodeGetContent(node);
    if(newName == NULL) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
		    xmlSecErrorsSafeString(xmlSecNodeGetName(node)),
		    XMLSEC_ERRORS_R_INVALID_NODE_CONTENT,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }
    /* TODO: do we need to decode the name? */
    
    /* compare name values */
    if((oldName != NULL) && !xmlStrEqual(oldName, newName)) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
		    "key name is already specified",
		    XMLSEC_ERRORS_R_INVALID_KEY_DATA,
		    XMLSEC_ERRORS_NO_MESSAGE);
	xmlFree(newName);
	return(-1);
    }

    /* try to find key in the manager */
    if((xmlSecKeyGetValue(key) == NULL) && (keyInfoCtx->keysMngr != NULL)) {
	xmlSecKeyPtr tmpKey;

	tmpKey = xmlSecKeysMngrFindKey(keyInfoCtx->keysMngr, newName, keyInfoCtx);
	if(tmpKey != NULL) {
	    /* erase any current information in the key */
	    xmlSecKeyEmpty(key);

	    /* TODO: since we will destroy tmpKey anyway, we can easily
	     * just re-assign key data values. It'll save use some memory
	     * malloc/free 
	     */
	     
	    /* and copy what we've found */
	    ret = xmlSecKeyCopy(key, tmpKey);
	    if(ret < 0) {
		xmlSecError(XMLSEC_ERRORS_HERE,
			    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
			    "xmlSecKeyCopy",
			    XMLSEC_ERRORS_R_XMLSEC_FAILED,
			    XMLSEC_ERRORS_NO_MESSAGE); 
		xmlSecKeyDestroy(tmpKey);
		xmlFree(newName);
		return(-1);
	    }
	    xmlSecKeyDestroy(tmpKey);
	}
    }		
    
    /* finally set key name if it is not there */
    if(xmlSecKeyGetName(key) == NULL) {
	xmlSecKeySetName(key, newName);
    }
    xmlFree(newName);
    return(0);
}

static int 
xmlSecKeyDataNameXmlWrite(xmlSecKeyDataId id, xmlSecKeyPtr key, xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    const xmlChar* name;

    xmlSecAssert2(id == xmlSecKeyDataNameId, -1);
    xmlSecAssert2(key != NULL, -1);
    xmlSecAssert2(node != NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);
    xmlSecAssert2(keyInfoCtx->mode == xmlSecKeyInfoModeWrite, -1);

    name = xmlSecKeyGetName(key);
    if(name != NULL) {
	    xmlSecNodeEncodeAndSetContent(node, name);
    }
    return(0);
}

/**************************************************************************
 *
 * <dsig:KeyValue/> processing
 *
 *************************************************************************/
static int			xmlSecKeyDataValueXmlRead	(xmlSecKeyDataId id,
								 xmlSecKeyPtr key,
								 xmlNodePtr node,
								 xmlSecKeyInfoCtxPtr keyInfoCtx);
static int			xmlSecKeyDataValueXmlWrite	(xmlSecKeyDataId id,
								 xmlSecKeyPtr key,
								 xmlNodePtr node,
								 xmlSecKeyInfoCtxPtr keyInfoCtx);

static xmlSecKeyDataKlass xmlSecKeyDataValueKlass = {
    sizeof(xmlSecKeyDataKlass),
    sizeof(xmlSecKeyData),

    /* data */
    xmlSecNameKeyValue,
    xmlSecKeyDataUsageKeyInfoNode, 		/* xmlSecKeyDataUsage usage; */
    NULL,					/* const xmlChar* href; */
    xmlSecNodeKeyValue,				/* const xmlChar* dataNodeName; */
    xmlSecDSigNs,				/* const xmlChar* dataNodeNs; */
    
    /* constructors/destructor */
    NULL,					/* xmlSecKeyDataInitializeMethod initialize; */
    NULL,					/* xmlSecKeyDataDuplicateMethod duplicate; */
    NULL,					/* xmlSecKeyDataFinalizeMethod finalize; */
    NULL,					/* xmlSecKeyDataGenerateMethod generate; */
    
    /* get info */
    NULL,					/* xmlSecKeyDataGetTypeMethod getType; */
    NULL,					/* xmlSecKeyDataGetSizeMethod getSize; */
    NULL,					/* xmlSecKeyDataGetIdentifier getIdentifier; */    

    /* read/write */
    xmlSecKeyDataValueXmlRead,			/* xmlSecKeyDataXmlReadMethod xmlRead; */
    xmlSecKeyDataValueXmlWrite, 	  	/* xmlSecKeyDataXmlWriteMethod xmlWrite; */
    NULL,					/* xmlSecKeyDataBinReadMethod binRead; */
    NULL,					/* xmlSecKeyDataBinWriteMethod binWrite; */

    /* debug */
    NULL,					/* xmlSecKeyDataDebugDumpMethod debugDump; */
    NULL,					/* xmlSecKeyDataDebugDumpMethod debugXmlDump; */

    /* reserved for the future */
    NULL,					/* void* reserved0; */
    NULL,					/* void* reserved1; */
};

/** 
 * xmlSecKeyDataValueGetKlass:
 *
 * The <dsig:KeyValue/> element key data klass 
 * (http://www.w3.org/TR/xmldsig-core/#sec-KeyValue):
 *
 * The KeyValue element contains a single public key that may be useful in 
 * validating the signature. 
 * 
 * Returns: the <dsig:KeyValue/> element processing key data klass.
 */
xmlSecKeyDataId 
xmlSecKeyDataValueGetKlass(void) {
    return(&xmlSecKeyDataValueKlass);
}

static int 
xmlSecKeyDataValueXmlRead(xmlSecKeyDataId id, xmlSecKeyPtr key, xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    const xmlChar* nodeName;
    const xmlChar* nodeNs;
    xmlSecKeyDataId dataId;
    xmlNodePtr cur;
    int ret;

    xmlSecAssert2(id == xmlSecKeyDataValueId, -1);
    xmlSecAssert2(key != NULL, -1);
    xmlSecAssert2(node != NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);
    xmlSecAssert2(keyInfoCtx->mode == xmlSecKeyInfoModeRead, -1);

    cur = xmlSecGetNextElementNode(node->children);
    if(cur == NULL) {
	/* just an empty node */
	return(0);
    }

    /* find data id */
    nodeName = cur->name;
    nodeNs = xmlSecGetNodeNsHref(cur);

    /* use global list only if we don't have a local one */
    if(xmlSecPtrListGetSize(&(keyInfoCtx->enabledKeyData)) > 0) {
	dataId = xmlSecKeyDataIdListFindByNode(&(keyInfoCtx->enabledKeyData),
			    nodeName, nodeNs, xmlSecKeyDataUsageKeyValueNodeRead);
    } else {	
    	dataId = xmlSecKeyDataIdListFindByNode(xmlSecKeyDataIdsGet(),
			    nodeName, nodeNs, xmlSecKeyDataUsageKeyValueNodeRead);
    }
    if(dataId != xmlSecKeyDataIdUnknown) {
	/* read data node */
	ret = xmlSecKeyDataXmlRead(dataId, key, cur, keyInfoCtx);
	if(ret < 0) {
	    xmlSecError(XMLSEC_ERRORS_HERE,
			xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
			"xmlSecKeyDataXmlRead",
			XMLSEC_ERRORS_R_XMLSEC_FAILED,
			"node=%s",
			xmlSecErrorsSafeString(xmlSecNodeGetName(cur)));
	    return(-1);
	}
    } else if((keyInfoCtx->flags & XMLSEC_KEYINFO_FLAGS_KEYVALUE_STOP_ON_UNKNOWN_CHILD) != 0) {
	/* laxi schema validation but application can disable it */
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
		    xmlSecErrorsSafeString(xmlSecNodeGetName(cur)),
		    XMLSEC_ERRORS_R_INVALID_NODE,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);	
    }

    /* <dsig:KeyValue/> might have only one node */
    cur = xmlSecGetNextElementNode(cur->next);  
    if(cur != NULL) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
		    xmlSecErrorsSafeString(xmlSecNodeGetName(cur)),
		    XMLSEC_ERRORS_R_UNEXPECTED_NODE,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }
    
    return(0);
}

static int 
xmlSecKeyDataValueXmlWrite(xmlSecKeyDataId id, xmlSecKeyPtr key, xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    const xmlChar* nodeName;
    const xmlChar* nodeNs;  
    xmlNodePtr cur;
    int ret;
    
    xmlSecAssert2(id == xmlSecKeyDataValueId, -1);
    xmlSecAssert2(key != NULL, -1);
    xmlSecAssert2(node != NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);
    xmlSecAssert2(keyInfoCtx->mode == xmlSecKeyInfoModeWrite, -1);

    if(!xmlSecKeyDataIsValid(key->value) || 
       !xmlSecKeyDataCheckUsage(key->value, xmlSecKeyDataUsageKeyValueNodeWrite)){
	/* nothing to write */
	return(0);
    }
    if((xmlSecPtrListGetSize(&(keyInfoCtx->enabledKeyData)) > 0) && 
        (xmlSecKeyDataIdListFind(&(keyInfoCtx->enabledKeyData), id) != 1)) {

	/* we are not enabled to write out key data with this id */
	return(0);
    }
    if(xmlSecKeyReqMatchKey(&(keyInfoCtx->keyReq), key) != 1) {
	/* we are not allowed to write out this key */
	return(0);
    }

    nodeName = key->value->id->dataNodeName;
    nodeNs = key->value->id->dataNodeNs;
    xmlSecAssert2(nodeName != NULL, -1);
    
    /* remove all existing key value */
    xmlNodeSetContent(node, NULL);
    
    /* create key node */
    cur = xmlSecAddChild(node, nodeName, nodeNs);
    if(cur == NULL) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
		    "xmlSecAddChild",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    "node=%s",
		    xmlSecErrorsSafeString(xmlSecNodeGetName(node)));
	return(-1);	
    }

    ret = xmlSecKeyDataXmlWrite(key->value->id, key, cur, keyInfoCtx);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
		    "xmlSecKeyDataXmlWrite",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    "node=%s",
		    xmlSecErrorsSafeString(xmlSecNodeGetName(cur)));
	return(-1);	
    }

    return(0);
}

/**************************************************************************
 *
 * <dsig:RetrievalMethod/> processing
 *
 *************************************************************************/
static int			xmlSecKeyDataRetrievalMethodXmlRead(xmlSecKeyDataId id,
								 xmlSecKeyPtr key,
								 xmlNodePtr node,
								 xmlSecKeyInfoCtxPtr keyInfoCtx);
static int			xmlSecKeyDataRetrievalMethodXmlWrite(xmlSecKeyDataId id,
								 xmlSecKeyPtr key,
								 xmlNodePtr node,
								 xmlSecKeyInfoCtxPtr keyInfoCtx);



static xmlSecKeyDataKlass xmlSecKeyDataRetrievalMethodKlass = {
    sizeof(xmlSecKeyDataKlass),
    sizeof(xmlSecKeyData),

    /* data */
    xmlSecNameRetrievalMethod,
    xmlSecKeyDataUsageKeyInfoNode, 		/* xmlSecKeyDataUsage usage; */
    NULL,					/* const xmlChar* href; */
    xmlSecNodeRetrievalMethod,			/* const xmlChar* dataNodeName; */
    xmlSecDSigNs,				/* const xmlChar* dataNodeNs; */
    
    /* constructors/destructor */
    NULL,					/* xmlSecKeyDataInitializeMethod initialize; */
    NULL,					/* xmlSecKeyDataDuplicateMethod duplicate; */
    NULL,					/* xmlSecKeyDataFinalizeMethod finalize; */
    NULL,					/* xmlSecKeyDataGenerateMethod generate; */
    
    /* get info */
    NULL,					/* xmlSecKeyDataGetTypeMethod getType; */
    NULL,					/* xmlSecKeyDataGetSizeMethod getSize; */
    NULL,					/* xmlSecKeyDataGetIdentifier getIdentifier; */    

    /* read/write */
    xmlSecKeyDataRetrievalMethodXmlRead,	/* xmlSecKeyDataXmlReadMethod xmlRead; */
    xmlSecKeyDataRetrievalMethodXmlWrite,   	/* xmlSecKeyDataXmlWriteMethod xmlWrite; */
    NULL,					/* xmlSecKeyDataBinReadMethod binRead; */
    NULL,					/* xmlSecKeyDataBinWriteMethod binWrite; */

    /* debug */
    NULL,					/* xmlSecKeyDataDebugDumpMethod debugDump; */
    NULL,					/* xmlSecKeyDataDebugDumpMethod debugXmlDump; */

    /* reserved for the future */
    NULL,					/* void* reserved0; */
    NULL,					/* void* reserved1; */
};

static int			xmlSecKeyDataRetrievalMethodReadXmlResult(xmlSecKeyDataId typeId,
								 xmlSecKeyPtr key,
								 const xmlChar* buffer,
								 xmlSecSize bufferSize,
								 xmlSecKeyInfoCtxPtr keyInfoCtx);

/** 
 * xmlSecKeyDataRetrievalMethodGetKlass:
 *
 * The <dsig:RetrievalMethod/> element key data klass 
 * (http://www.w3.org/TR/xmldsig-core/#sec-RetrievalMethod):
 * A RetrievalMethod element within KeyInfo is used to convey a reference to 
 * KeyInfo information that is stored at another location. For example, 
 * several signatures in a document might use a key verified by an X.509v3 
 * certificate chain appearing once in the document or remotely outside the 
 * document; each signature's KeyInfo can reference this chain using a single 
 * RetrievalMethod element instead of including the entire chain with a 
 * sequence of X509Certificate elements.
 *
 * RetrievalMethod uses the same syntax and dereferencing behavior as 
 * Reference's URI and The Reference Processing Model.
 * 
 * Returns: the <dsig:RetrievalMethod/> element processing key data klass.
 */
xmlSecKeyDataId 
xmlSecKeyDataRetrievalMethodGetKlass(void) {
    return(&xmlSecKeyDataRetrievalMethodKlass);
}

static int 
xmlSecKeyDataRetrievalMethodXmlRead(xmlSecKeyDataId id, xmlSecKeyPtr key, xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    xmlSecKeyDataId dataId = xmlSecKeyDataIdUnknown;
    xmlChar *retrType = NULL;
    xmlChar *uri = NULL;
    xmlNodePtr cur;
    int res = -1;
    int ret;
    
    xmlSecAssert2(id == xmlSecKeyDataRetrievalMethodId, -1);
    xmlSecAssert2(key != NULL, -1);
    xmlSecAssert2(node != NULL, -1);
    xmlSecAssert2(node->doc != NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);
    xmlSecAssert2(keyInfoCtx->mode == xmlSecKeyInfoModeRead, -1);

    /* check retrieval level */
    if(keyInfoCtx->curRetrievalMethodLevel >= keyInfoCtx->maxRetrievalMethodLevel) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
		    NULL,
		    XMLSEC_ERRORS_R_MAX_RETRIEVALS_LEVEL,
		    "cur=%d;max=%d", 
		    keyInfoCtx->curRetrievalMethodLevel,
		    keyInfoCtx->maxRetrievalMethodLevel);
	goto done;
    }
    ++keyInfoCtx->curRetrievalMethodLevel;

    retrType = xmlGetProp(node, xmlSecAttrType);
    if(retrType != NULL) {
	/* use global list only if we don't have a local one */
	if(xmlSecPtrListGetSize(&(keyInfoCtx->enabledKeyData)) > 0) {
	    dataId = xmlSecKeyDataIdListFindByHref(&(keyInfoCtx->enabledKeyData),
			    retrType, xmlSecKeyDataUsageRetrievalMethodNode);
	} else {	
    	    dataId = xmlSecKeyDataIdListFindByHref(xmlSecKeyDataIdsGet(),
			    retrType, xmlSecKeyDataUsageRetrievalMethodNode);
	}
    }

    /* laxi schema validation but aplication can disable it */
    if(dataId == xmlSecKeyDataIdUnknown) {
	if((keyInfoCtx->flags & XMLSEC_KEYINFO_FLAGS_RETRMETHOD_STOP_ON_UNKNOWN_HREF) != 0) {
	    xmlSecError(XMLSEC_ERRORS_HERE,
		        xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
			xmlSecErrorsSafeString(xmlSecAttrType),
			XMLSEC_ERRORS_R_INVALID_NODE_ATTRIBUTE,
			"value=%s", xmlSecErrorsSafeString(retrType));
	} else {
	    res = 0;
	}
	goto done;
    }

    /* destroy prev retrieval method context */
    xmlSecTransformCtxReset(&(keyInfoCtx->retrievalMethodCtx));

    /* set start URI and check that it is enabled */
    uri = xmlGetProp(node, xmlSecAttrURI);
    ret = xmlSecTransformCtxSetUri(&(keyInfoCtx->retrievalMethodCtx), uri, node);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
		    "xmlSecTransformCtxSetUri",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    "uri=%s",
		    xmlSecErrorsSafeString(uri));
	goto done;
    }

    /* the only one node is optional Transforms node */
    cur = xmlSecGetNextElementNode(node->children);
    if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeTransforms, xmlSecDSigNs))) {
	ret = xmlSecTransformCtxNodesListRead(&(keyInfoCtx->retrievalMethodCtx), 
					    cur, xmlSecTransformUsageDSigTransform);
	if(ret < 0) {
	    xmlSecError(XMLSEC_ERRORS_HERE,
			xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
			"xmlSecTransformCtxNodesListRead",
			XMLSEC_ERRORS_R_XMLSEC_FAILED,
			"node=%s",
			xmlSecErrorsSafeString(xmlSecNodeGetName(cur)));
	    goto done;
	}	
        cur = xmlSecGetNextElementNode(cur->next);
    }

    if(cur != NULL) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
		    xmlSecErrorsSafeString(xmlSecNodeGetName(cur)),
		    XMLSEC_ERRORS_R_UNEXPECTED_NODE,
		    XMLSEC_ERRORS_NO_MESSAGE);
	goto done;
    }

    /* finally get transforms results */
    ret = xmlSecTransformCtxExecute(&(keyInfoCtx->retrievalMethodCtx), node->doc);
    if((ret < 0) || 
       (keyInfoCtx->retrievalMethodCtx.result == NULL) ||
       (xmlSecBufferGetData(keyInfoCtx->retrievalMethodCtx.result) == NULL)) {

	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
		    "xmlSecTransformCtxExecute",
	    	    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	goto done;
    }


    /* assume that the data is in XML if we could not find id */    
    if((dataId == xmlSecKeyDataIdUnknown) || 
       ((dataId->usage & xmlSecKeyDataUsageRetrievalMethodNodeXml) != 0)) {

	ret = xmlSecKeyDataRetrievalMethodReadXmlResult(dataId, key,
		    xmlSecBufferGetData(keyInfoCtx->retrievalMethodCtx.result),
                    xmlSecBufferGetSize(keyInfoCtx->retrievalMethodCtx.result),
		    keyInfoCtx);
	if(ret < 0) {
	    xmlSecError(XMLSEC_ERRORS_HERE,
			xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
			"xmlSecKeyDataRetrievalMethodReadXmlResult",
	    		XMLSEC_ERRORS_R_XMLSEC_FAILED,
			XMLSEC_ERRORS_NO_MESSAGE);
	    goto done;
	}    
    } else {
	ret = xmlSecKeyDataBinRead(dataId, key, 
		    xmlSecBufferGetData(keyInfoCtx->retrievalMethodCtx.result),
                    xmlSecBufferGetSize(keyInfoCtx->retrievalMethodCtx.result),
		    keyInfoCtx);
	if(ret < 0) {
	    xmlSecError(XMLSEC_ERRORS_HERE,
			xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
			"xmlSecKeyDataBinRead",
	    		XMLSEC_ERRORS_R_XMLSEC_FAILED,
			XMLSEC_ERRORS_NO_MESSAGE);
	    goto done;
	}    
    }
    --keyInfoCtx->curRetrievalMethodLevel;
    
    res = 0;    
done:
    if(uri != NULL) {
	xmlFree(uri);
    }
    if(retrType != NULL) {
	xmlFree(retrType);
    }
    return(res);
}

static int 
xmlSecKeyDataRetrievalMethodXmlWrite(xmlSecKeyDataId id, xmlSecKeyPtr key, xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    xmlSecAssert2(id == xmlSecKeyDataRetrievalMethodId, -1);
    xmlSecAssert2(key != NULL, -1);
    xmlSecAssert2(node != NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);
    xmlSecAssert2(keyInfoCtx->mode == xmlSecKeyInfoModeWrite, -1);

    /* just do nothing */
    return(0);
}

static int
xmlSecKeyDataRetrievalMethodReadXmlResult(xmlSecKeyDataId typeId, xmlSecKeyPtr key,
					  const xmlChar* buffer, xmlSecSize bufferSize,
					  xmlSecKeyInfoCtxPtr keyInfoCtx) {
    xmlDocPtr doc;
    xmlNodePtr cur;
    const xmlChar* nodeName;
    const xmlChar* nodeNs;
    xmlSecKeyDataId dataId;
    int ret;
    
    xmlSecAssert2(key != NULL, -1);
    xmlSecAssert2(buffer != NULL, -1);
    xmlSecAssert2(bufferSize > 0, -1); 
    xmlSecAssert2(keyInfoCtx != NULL, -1);
    xmlSecAssert2(keyInfoCtx->mode == xmlSecKeyInfoModeRead, -1);

    doc = xmlRecoverMemory((const char*)buffer, bufferSize);
    if(doc == NULL) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(typeId)),
		    "xmlRecoverMemory",
		    XMLSEC_ERRORS_R_XML_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }
	
    cur = xmlDocGetRootElement(doc);
    if(cur == NULL) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(typeId)),
		    "xmlDocGetRootElement",
		    XMLSEC_ERRORS_R_XML_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	xmlFreeDoc(doc);
	return(-1);	
    }

    nodeName = cur->name;
    nodeNs = xmlSecGetNodeNsHref(cur);

    /* use global list only if we don't have a local one */
    if(xmlSecPtrListGetSize(&(keyInfoCtx->enabledKeyData)) > 0) {
	dataId = xmlSecKeyDataIdListFindByNode(&(keyInfoCtx->enabledKeyData),
			    nodeName, nodeNs, xmlSecKeyDataUsageRetrievalMethodNodeXml);
    } else {	
    	dataId = xmlSecKeyDataIdListFindByNode(xmlSecKeyDataIdsGet(),
			    nodeName, nodeNs, xmlSecKeyDataUsageRetrievalMethodNodeXml);
    }
    if(dataId == xmlSecKeyDataIdUnknown) {
	xmlFreeDoc(doc);

	/* laxi schema validation but application can disable it */
	if((keyInfoCtx->flags & XMLSEC_KEYINFO_FLAGS_KEYVALUE_STOP_ON_UNKNOWN_CHILD) != 0) {
	    xmlSecError(XMLSEC_ERRORS_HERE,
			xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(typeId)),
			xmlSecErrorsSafeString(xmlSecNodeGetName(cur)),
			XMLSEC_ERRORS_R_INVALID_NODE,
			XMLSEC_ERRORS_NO_MESSAGE);
	    return(-1);
	}
	return(0);
    } else if((typeId != xmlSecKeyDataIdUnknown) && (typeId != dataId) &&
	      ((keyInfoCtx->flags & XMLSEC_KEYINFO_FLAGS_RETRMETHOD_STOP_ON_MISMATCH_HREF) != 0)) {
	
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(typeId)),
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(dataId)),		    
		    XMLSEC_ERRORS_R_MAX_RETRIEVAL_TYPE_MISMATCH,
		    XMLSEC_ERRORS_NO_MESSAGE);
	xmlFreeDoc(doc);
	return(-1);
    }

    /* read data node */
    ret = xmlSecKeyDataXmlRead(dataId, key, cur, keyInfoCtx);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(typeId)),
		    "xmlSecKeyDataXmlRead",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    "node=%s",
		    xmlSecErrorsSafeString(xmlSecNodeGetName(cur)));
	xmlFreeDoc(doc);
	return(-1);
    }
    
    xmlFreeDoc(doc);
    return(0);
}


#ifndef XMLSEC_NO_XMLENC
/**************************************************************************
 *
 * <enc:EncryptedKey/> processing
 *
 *************************************************************************/
static int	xmlSecKeyDataEncryptedKeyXmlRead	(xmlSecKeyDataId id,
							 xmlSecKeyPtr key,
							 xmlNodePtr node,
							 xmlSecKeyInfoCtxPtr keyInfoCtx);
static int	xmlSecKeyDataEncryptedKeyXmlWrite	(xmlSecKeyDataId id,
							 xmlSecKeyPtr key,
							 xmlNodePtr node,
							 xmlSecKeyInfoCtxPtr keyInfoCtx);



static xmlSecKeyDataKlass xmlSecKeyDataEncryptedKeyKlass = {
    sizeof(xmlSecKeyDataKlass),
    sizeof(xmlSecKeyData),

    /* data */
    xmlSecNameEncryptedKey,
    xmlSecKeyDataUsageKeyInfoNode | xmlSecKeyDataUsageRetrievalMethodNodeXml, 		
						/* xmlSecKeyDataUsage usage; */
    xmlSecHrefEncryptedKey,			/* const xmlChar* href; */
    xmlSecNodeEncryptedKey,			/* const xmlChar* dataNodeName; */
    xmlSecEncNs,				/* const xmlChar* dataNodeNs; */
    
    /* constructors/destructor */
    NULL,					/* xmlSecKeyDataInitializeMethod initialize; */
    NULL,					/* xmlSecKeyDataDuplicateMethod duplicate; */
    NULL,					/* xmlSecKeyDataFinalizeMethod finalize; */
    NULL,					/* xmlSecKeyDataGenerateMethod generate; */
    
    /* get info */
    NULL,					/* xmlSecKeyDataGetTypeMethod getType; */
    NULL,					/* xmlSecKeyDataGetSizeMethod getSize; */
    NULL,					/* xmlSecKeyDataGetIdentifier getIdentifier; */    

    /* read/write */
    xmlSecKeyDataEncryptedKeyXmlRead,		/* xmlSecKeyDataXmlReadMethod xmlRead; */
    xmlSecKeyDataEncryptedKeyXmlWrite,   	/* xmlSecKeyDataXmlWriteMethod xmlWrite; */
    NULL,					/* xmlSecKeyDataBinReadMethod binRead; */
    NULL,					/* xmlSecKeyDataBinWriteMethod binWrite; */

    /* debug */
    NULL,					/* xmlSecKeyDataDebugDumpMethod debugDump; */
    NULL,					/* xmlSecKeyDataDebugDumpMethod debugXmlDump; */

    /* reserved for the future */
    NULL,					/* void* reserved0; */
    NULL,					/* void* reserved1; */
};

/** 
 * xmlSecKeyDataEncryptedKeyGetKlass:
 *
 * The <enc:EncryptedKey/> element key data klass 
 * (http://www.w3.org/TR/xmlenc-core/#sec-EncryptedKey):
 *
 * The EncryptedKey element is used to transport encryption keys from 
 * the originator to a known recipient(s). It may be used as a stand-alone 
 * XML document, be placed within an application document, or appear inside 
 * an EncryptedData element as a child of a ds:KeyInfo element. The key value 
 * is always encrypted to the recipient(s). When EncryptedKey is decrypted the 
 * resulting octets are made available to the EncryptionMethod algorithm 
 * without any additional processing.
 * 
 * Returns: the <enc:EncryptedKey/> element processing key data klass.
 */
xmlSecKeyDataId 
xmlSecKeyDataEncryptedKeyGetKlass(void) {
    return(&xmlSecKeyDataEncryptedKeyKlass);
}

static int 
xmlSecKeyDataEncryptedKeyXmlRead(xmlSecKeyDataId id, xmlSecKeyPtr key, xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    xmlSecBufferPtr result;
    int ret;

    xmlSecAssert2(id == xmlSecKeyDataEncryptedKeyId, -1);
    xmlSecAssert2(key != NULL, -1);
    xmlSecAssert2(node != NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);
    xmlSecAssert2(keyInfoCtx->mode == xmlSecKeyInfoModeRead, -1);

    /* check the enc level */    
    if(keyInfoCtx->curEncryptedKeyLevel >= keyInfoCtx->maxEncryptedKeyLevel) {
        xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
		    NULL,
		    XMLSEC_ERRORS_R_MAX_ENCKEY_LEVEL,
		    "cur=%d;max=%d", 
		    keyInfoCtx->curEncryptedKeyLevel,
		    keyInfoCtx->maxEncryptedKeyLevel);
	return(-1);
    }
    ++keyInfoCtx->curEncryptedKeyLevel;

    /* init Enc context */    
    if(keyInfoCtx->encCtx != NULL) {
	xmlSecEncCtxReset(keyInfoCtx->encCtx);
    } else {
	ret = xmlSecKeyInfoCtxCreateEncCtx(keyInfoCtx);
	if(ret < 0) {
	    xmlSecError(XMLSEC_ERRORS_HERE,
			xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
		        "xmlSecKeyInfoCtxCreateEncCtx",
		        XMLSEC_ERRORS_R_XMLSEC_FAILED,
			XMLSEC_ERRORS_NO_MESSAGE);
	    return(-1);		
        }
    }
    xmlSecAssert2(keyInfoCtx->encCtx != NULL, -1);
    
    result = xmlSecEncCtxDecryptToBuffer(keyInfoCtx->encCtx, node);
    if((result == NULL) || (xmlSecBufferGetData(result) == NULL)) {
	/* We might have multiple EncryptedKey elements, encrypted 
	 * for different receipints but application can enforce
	 * correct enc key.
	 */
        if((keyInfoCtx->flags & XMLSEC_KEYINFO_FLAGS_ENCKEY_DONT_STOP_ON_FAILED_DECRYPTION) != 0) {
	    xmlSecError(XMLSEC_ERRORS_HERE,
			xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
			"xmlSecEncCtxDecryptToBuffer",
			XMLSEC_ERRORS_R_XMLSEC_FAILED,
			XMLSEC_ERRORS_NO_MESSAGE);
	    return(-1);
	}
	return(0);
    }
	 
    ret = xmlSecKeyDataBinRead(keyInfoCtx->keyReq.keyId, key,
			   xmlSecBufferGetData(result),
			   xmlSecBufferGetSize(result),
			   keyInfoCtx);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
		    "xmlSecKeyDataBinRead",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }			   
    --keyInfoCtx->curEncryptedKeyLevel;

    return(0);
}

static int 
xmlSecKeyDataEncryptedKeyXmlWrite(xmlSecKeyDataId id, xmlSecKeyPtr key, xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    xmlSecKeyInfoCtx keyInfoCtx2;
    xmlSecByte *keyBuf = NULL;
    xmlSecSize keySize = 0;
    int res = -1;
    int ret;

    xmlSecAssert2(id == xmlSecKeyDataEncryptedKeyId, -1);
    xmlSecAssert2(key != NULL, -1);
    xmlSecAssert2(xmlSecKeyIsValid(key), -1);
    xmlSecAssert2(node != NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);
    xmlSecAssert2(keyInfoCtx->mode == xmlSecKeyInfoModeWrite, -1);
    
    /* dump key to a binary buffer */
    ret = xmlSecKeyInfoCtxInitialize(&keyInfoCtx2, NULL);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
		    "xmlSecKeyInfoCtxInitialize",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	goto done;
    }
    
    ret = xmlSecKeyInfoCtxCopyUserPref(&keyInfoCtx2, keyInfoCtx);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
		    "xmlSecKeyInfoCtxCopyUserPref",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	xmlSecKeyInfoCtxFinalize(&keyInfoCtx2);
	goto done;
    }

    keyInfoCtx2.keyReq.keyType = xmlSecKeyDataTypeAny;
    ret = xmlSecKeyDataBinWrite(key->value->id, key, &keyBuf, &keySize, &keyInfoCtx2);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
		    "xmlSecKeyDataBinWrite",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	xmlSecKeyInfoCtxFinalize(&keyInfoCtx2);
	goto done;
    }
    xmlSecKeyInfoCtxFinalize(&keyInfoCtx2);
    
    /* init Enc context */    
    if(keyInfoCtx->encCtx != NULL) {
	xmlSecEncCtxReset(keyInfoCtx->encCtx);
    } else {
	ret = xmlSecKeyInfoCtxCreateEncCtx(keyInfoCtx);
	if(ret < 0) {
	    xmlSecError(XMLSEC_ERRORS_HERE,
		        xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
			"xmlSecKeyInfoCtxCreateEncCtx",
			XMLSEC_ERRORS_R_XMLSEC_FAILED,
		        XMLSEC_ERRORS_NO_MESSAGE);
    	    goto done;	
	}
    }
    xmlSecAssert2(keyInfoCtx->encCtx != NULL, -1);

    ret = xmlSecEncCtxBinaryEncrypt(keyInfoCtx->encCtx, node, keyBuf, keySize);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(id)),
		    "xmlSecEncCtxBinaryEncrypt",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	goto done;	
    }
    
    res = 0;
done:
    if(keyBuf != NULL) {
	memset(keyBuf, 0, keySize);
	xmlFree(keyBuf); keyBuf = NULL;
    }
    return(res);
}

#endif /* XMLSEC_NO_XMLENC */

