/** 
 * XML Security Library (http://www.aleksey.com/xmlsec).
 *
 * "XML Encryption" implementation
 *  http://www.w3.org/TR/xmlenc-core
 * 
 * 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"

#ifndef XMLSEC_NO_XMLENC
 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <libxml/tree.h>
#include <libxml/parser.h> 

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

static int 	xmlSecEncCtxEncDataNodeRead		(xmlSecEncCtxPtr encCtx, 
							 xmlNodePtr node);
static int 	xmlSecEncCtxEncDataNodeWrite		(xmlSecEncCtxPtr encCtx);
static int 	xmlSecEncCtxCipherDataNodeRead		(xmlSecEncCtxPtr encCtx, 
							 xmlNodePtr node);
static int 	xmlSecEncCtxCipherReferenceNodeRead	(xmlSecEncCtxPtr encCtx, 
							 xmlNodePtr node);

/* The ID attribute in XMLEnc is 'Id' */
static const xmlChar*		xmlSecEncIds[] = { BAD_CAST "Id", NULL };


/**
 * xmlSecEncCtxCreate:
 * @keysMngr: 		the pointer to keys manager.
 *
 * Creates <enc:EncryptedData/> element processing context.
 * The caller is responsible for destroying returend object by calling 
 * #xmlSecEncCtxDestroy function.
 *
 * Returns: pointer to newly allocated context object or NULL if an error
 * occurs.
 */
xmlSecEncCtxPtr	
xmlSecEncCtxCreate(xmlSecKeysMngrPtr keysMngr) {
    xmlSecEncCtxPtr encCtx;
    int ret;
    
    encCtx = (xmlSecEncCtxPtr) xmlMalloc(sizeof(xmlSecEncCtx));
    if(encCtx == NULL) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    NULL,
		    XMLSEC_ERRORS_R_MALLOC_FAILED,
		    "sizeof(xmlSecEncCtx)=%d", 
		    sizeof(xmlSecEncCtx));
	return(NULL);
    }
    
    ret = xmlSecEncCtxInitialize(encCtx, keysMngr);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecEncCtxInitialize",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	xmlSecEncCtxDestroy(encCtx);
	return(NULL);   
    }
    return(encCtx);    
}

/**
 * xmlSecEncCtxDestroy:
 * @encCtx:		the pointer to <enc:EncryptedData/> processing context.
 *
 * Destroy context object created with #xmlSecEncCtxCreate function.
 */
void  
xmlSecEncCtxDestroy(xmlSecEncCtxPtr encCtx) {
    xmlSecAssert(encCtx != NULL);
    
    xmlSecEncCtxFinalize(encCtx);
    xmlFree(encCtx);
}

/**
 * xmlSecEncCtxInitialize:
 * @encCtx:		the pointer to <enc:EncryptedData/> processing context.
 * @keysMngr: 		the pointer to keys manager.
 *
 * Initializes <enc:EncryptedData/> element processing context.
 * The caller is responsible for cleaing up returend object by calling 
 * #xmlSecEncCtxFinalize function.
 *
 * Returns: 0 on success or a negative value if an error occurs.
 */
int 
xmlSecEncCtxInitialize(xmlSecEncCtxPtr encCtx, xmlSecKeysMngrPtr keysMngr) {
    int ret;
    
    xmlSecAssert2(encCtx != NULL, -1);
    
    memset(encCtx, 0, sizeof(xmlSecEncCtx));

    /* initialize key info */
    ret = xmlSecKeyInfoCtxInitialize(&(encCtx->keyInfoReadCtx), keysMngr);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecKeyInfoCtxInitialize",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);   
    }
    encCtx->keyInfoReadCtx.mode = xmlSecKeyInfoModeRead;
    
    ret = xmlSecKeyInfoCtxInitialize(&(encCtx->keyInfoWriteCtx), keysMngr);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecKeyInfoCtxInitialize",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);   
    }
    encCtx->keyInfoWriteCtx.mode = xmlSecKeyInfoModeWrite;
    /* it's not wise to write private key :) */
    encCtx->keyInfoWriteCtx.keyReq.keyType = xmlSecKeyDataTypePublic;

    /* initializes transforms encCtx */
    ret = xmlSecTransformCtxInitialize(&(encCtx->transformCtx));
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecTransformCtxInitialize",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);   
    }

    return(0);
}

/**
 * xmlSecEncCtxFinalize:
 * @encCtx:		the pointer to <enc:EncryptedData/> processing context.
 *
 * Cleans up @encCtx object.
 */
void 
xmlSecEncCtxFinalize(xmlSecEncCtxPtr encCtx) {
    xmlSecAssert(encCtx != NULL);

    xmlSecEncCtxReset(encCtx);
    
    xmlSecTransformCtxFinalize(&(encCtx->transformCtx));
    xmlSecKeyInfoCtxFinalize(&(encCtx->keyInfoReadCtx));
    xmlSecKeyInfoCtxFinalize(&(encCtx->keyInfoWriteCtx));

    memset(encCtx, 0, sizeof(xmlSecEncCtx));
}

/**
 * xmlSecEncCtxReset:
 * @encCtx:		the pointer to <enc:EncryptedData/> processing context.
 *
 * Resets @encCtx object, user settings are not touched.
 */
void 
xmlSecEncCtxReset(xmlSecEncCtxPtr encCtx) {
    xmlSecAssert(encCtx != NULL);
    
    xmlSecTransformCtxReset(&(encCtx->transformCtx));
    xmlSecKeyInfoCtxReset(&(encCtx->keyInfoReadCtx));
    xmlSecKeyInfoCtxReset(&(encCtx->keyInfoWriteCtx));

    encCtx->operation 		= xmlSecTransformOperationNone;
    encCtx->result		= NULL;
    encCtx->resultBase64Encoded = 0;
    encCtx->resultReplaced	= 0;
    encCtx->encMethod		= NULL;
    
    if (encCtx->replacedNodeList != NULL) { 
	  	xmlFreeNodeList(encCtx->replacedNodeList);
    	encCtx->replacedNodeList = NULL;
    }
    
    if(encCtx->encKey != NULL) {
	    xmlSecKeyDestroy(encCtx->encKey);
	    encCtx->encKey = NULL;
    }
    
    if(encCtx->id != NULL) {
	    xmlFree(encCtx->id);
	    encCtx->id = NULL;
    }	

    if(encCtx->type != NULL) {
	    xmlFree(encCtx->type);
	    encCtx->type = NULL;
    }

    if(encCtx->mimeType != NULL) {
	    xmlFree(encCtx->mimeType);
	    encCtx->mimeType = NULL;
    }

    if(encCtx->encoding != NULL) {
	    xmlFree(encCtx->encoding);
	    encCtx->encoding = NULL;
    }	

    if(encCtx->recipient != NULL) {
	    xmlFree(encCtx->recipient);
	    encCtx->recipient = NULL;
    }

    if(encCtx->carriedKeyName != NULL) {
	    xmlFree(encCtx->carriedKeyName);
	    encCtx->carriedKeyName = NULL;
    }
    
    encCtx->encDataNode = encCtx->encMethodNode = 
	encCtx->keyInfoNode = encCtx->cipherValueNode = NULL;
}

/**
 * xmlSecEncCtxCopyUserPref:
 * @dst:		the pointer to destination context.
 * @src:		the pointer to source context.
 * 
 * Copies user preference from @src context to @dst.
 *
 * Returns: 0 on success or a negative value if an error occurs.
 */
int 
xmlSecEncCtxCopyUserPref(xmlSecEncCtxPtr dst, xmlSecEncCtxPtr src) {
    int ret;
    
    xmlSecAssert2(dst != NULL, -1);
    xmlSecAssert2(src != NULL, -1);

    dst->userData 	= src->userData;
    dst->flags		= src->flags;
    dst->flags2		= src->flags2;
    dst->defEncMethodId = src->defEncMethodId;
    dst->mode 		= src->mode;
    
    ret = xmlSecTransformCtxCopyUserPref(&(dst->transformCtx), &(src->transformCtx));
    if(ret < 0) {
    	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecTransformCtxCopyUserPref",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }

    ret = xmlSecKeyInfoCtxCopyUserPref(&(dst->keyInfoReadCtx), &(src->keyInfoReadCtx));
    if(ret < 0) {
    	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecKeyInfoCtxCopyUserPref",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }

    ret = xmlSecKeyInfoCtxCopyUserPref(&(dst->keyInfoWriteCtx), &(src->keyInfoWriteCtx));
    if(ret < 0) {
    	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecKeyInfoCtxCopyUserPref",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }

    return(0);
} 

/**
 * xmlSecEncCtxBinaryEncrypt:
 * @encCtx:		the pointer to <enc:EncryptedData/> processing context.
 * @tmpl:		the pointer to <enc:EncryptedData/> template node.
 * @data:		the pointer for binary buffer.
 * @dataSize:		the @data buffer size.
 *
 * Encrypts @data according to template @tmpl.
 *
 * Returns: 0 on success or a negative value if an error occurs.
 */
int 
xmlSecEncCtxBinaryEncrypt(xmlSecEncCtxPtr encCtx, xmlNodePtr tmpl, 
			  const xmlSecByte* data, xmlSecSize dataSize) {
    int ret;
    
    xmlSecAssert2(encCtx != NULL, -1);
    xmlSecAssert2(encCtx->result == NULL, -1);
    xmlSecAssert2(tmpl != NULL, -1);
    xmlSecAssert2(data != NULL, -1);

    /* initialize context and add ID atributes to the list of known ids */    
    encCtx->operation = xmlSecTransformOperationEncrypt;
    xmlSecAddIDs(tmpl->doc, tmpl, xmlSecEncIds);

    /* read the template and set encryption method, key, etc. */
    ret = xmlSecEncCtxEncDataNodeRead(encCtx, tmpl);
    if(ret < 0) {
    	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecEncCtxEncDataNodeRead",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }

    ret = xmlSecTransformCtxBinaryExecute(&(encCtx->transformCtx), data, dataSize);
    if(ret < 0) {
    	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecTransformCtxBinaryExecute",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    "dataSize=%d",
		    dataSize);
	return(-1);
    }

    encCtx->result = encCtx->transformCtx.result;
    xmlSecAssert2(encCtx->result != NULL, -1);
    
    ret = xmlSecEncCtxEncDataNodeWrite(encCtx);
    if(ret < 0) {
    	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecEncCtxEncDataNodeWrite",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }
    return(0);    
}

/**
 * xmlSecEncCtxXmlEncrypt:
 * @encCtx:		the pointer to <enc:EncryptedData/> processing context.
 * @tmpl:		the pointer to <enc:EncryptedData/> template node.
 * @node:		the pointer to node for encryption.
 *
 * Encrypts @node according to template @tmpl. If requested, @node is replaced
 * with result <enc:EncryptedData/> node.
 *
 * Returns: 0 on success or a negative value if an error occurs.
 */
int 
xmlSecEncCtxXmlEncrypt(xmlSecEncCtxPtr encCtx, xmlNodePtr tmpl, xmlNodePtr node) {
    xmlOutputBufferPtr output;
    int ret;
    
    xmlSecAssert2(encCtx != NULL, -1);
    xmlSecAssert2(encCtx->result == NULL, -1);
    xmlSecAssert2(tmpl != NULL, -1);
    xmlSecAssert2(node != NULL, -1);
    xmlSecAssert2(node->doc != NULL, -1);

    /* initialize context and add ID atributes to the list of known ids */    
    encCtx->operation = xmlSecTransformOperationEncrypt;
    xmlSecAddIDs(tmpl->doc, tmpl, xmlSecEncIds);

    /* read the template and set encryption method, key, etc. */
    ret = xmlSecEncCtxEncDataNodeRead(encCtx, tmpl);
    if(ret < 0) {
    	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecEncCtxEncDataNodeRead",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }

    ret = xmlSecTransformCtxPrepare(&(encCtx->transformCtx), xmlSecTransformDataTypeBin);
    if(ret < 0) {
    	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecTransformCtxPrepare",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    "type=bin");
	return(-1);
    }
    
    xmlSecAssert2(encCtx->transformCtx.first != NULL, -1);
    output = xmlSecTransformCreateOutputBuffer(encCtx->transformCtx.first, 
						&(encCtx->transformCtx));
    if(output == NULL) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecTransformGetName(encCtx->transformCtx.first)),
		    "xmlSecTransformCreateOutputBuffer",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }

    /* push data thru */
    if((encCtx->type != NULL) && xmlStrEqual(encCtx->type, xmlSecTypeEncElement)) {
	/* get the content of the node */
	xmlNodeDumpOutput(output, node->doc, node, 0, 0, NULL);
    } else if((encCtx->type != NULL) && xmlStrEqual(encCtx->type, xmlSecTypeEncContent)) {
	xmlNodePtr cur;

	/* get the content of the nodes childs */
	for(cur = node->children; cur != NULL; cur = cur->next) {
	    xmlNodeDumpOutput(output, node->doc, cur, 0, 0, NULL);
	}
    } else {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    NULL,
		    XMLSEC_ERRORS_R_INVALID_TYPE,
		    "type=%s", 
		    xmlSecErrorsSafeString(encCtx->type));
	xmlOutputBufferClose(output);
	return(-1);	    	
    }
    
    /* close the buffer and flush everything */
    ret = xmlOutputBufferClose(output);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlOutputBufferClose",
		    XMLSEC_ERRORS_R_XML_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }

    encCtx->result = encCtx->transformCtx.result;
    xmlSecAssert2(encCtx->result != NULL, -1);
    
    ret = xmlSecEncCtxEncDataNodeWrite(encCtx);
    if(ret < 0) {
    	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecEncCtxEncDataNodeWrite",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
    	return(-1);
    }
    
    /* now we need to update our original document */
    if((encCtx->type != NULL) && xmlStrEqual(encCtx->type, xmlSecTypeEncElement)) {
        /* check if we need to return the replaced node */
        if((encCtx->flags & XMLSEC_ENC_RETURN_REPLACED_NODE) != 0) {
            ret = xmlSecReplaceNodeAndReturn(node, tmpl, &(encCtx->replacedNodeList));
            if(ret < 0) {
                xmlSecError(XMLSEC_ERRORS_HERE,
                NULL,
                "xmlSecReplaceNode",
                XMLSEC_ERRORS_R_XMLSEC_FAILED,
                "node=%s",
                xmlSecErrorsSafeString(xmlSecNodeGetName(node)));
                return(-1);
            }
        } else {
            ret = xmlSecReplaceNode(node, tmpl);
            if(ret < 0) {
                xmlSecError(XMLSEC_ERRORS_HERE,
		            NULL,
		            "xmlSecReplaceNode",
		            XMLSEC_ERRORS_R_XMLSEC_FAILED,
		            "node=%s",
		            xmlSecErrorsSafeString(xmlSecNodeGetName(node)));
                return(-1);
            }
        }

	    encCtx->resultReplaced = 1;			       
    } else if((encCtx->type != NULL) && xmlStrEqual(encCtx->type, xmlSecTypeEncContent)) {
        /* check if we need to return the replaced node */
        if((encCtx->flags & XMLSEC_ENC_RETURN_REPLACED_NODE) != 0) {        
            ret = xmlSecReplaceContentAndReturn(node, tmpl, &(encCtx->replacedNodeList));
            if(ret < 0) {
                xmlSecError(XMLSEC_ERRORS_HERE,
	                NULL,
	                "xmlSecReplaceContentAndReturn",
	                XMLSEC_ERRORS_R_XMLSEC_FAILED,
	                "node=%s",
	                xmlSecErrorsSafeString(xmlSecNodeGetName(node)));
                return(-1);
            }
        } else {
            ret = xmlSecReplaceContent(node, tmpl);
            if(ret < 0) {
                xmlSecError(XMLSEC_ERRORS_HERE,
	                NULL,
	                "xmlSecReplaceContent",
	                XMLSEC_ERRORS_R_XMLSEC_FAILED,
	                "node=%s",
	                xmlSecErrorsSafeString(xmlSecNodeGetName(node)));
                return(-1);
            }
        }

        encCtx->resultReplaced = 1;			       
    } else {
	    /* we should've catached this error before */
	    xmlSecError(XMLSEC_ERRORS_HERE,
		        NULL,
		        NULL,
		        XMLSEC_ERRORS_R_INVALID_TYPE,
		        "type=%s", 
		        xmlSecErrorsSafeString(encCtx->type));
	    return(-1);	    	
    }
    return(0);    
}

/**
 * xmlSecEncCtxUriEncrypt:
 * @encCtx:		the pointer to <enc:EncryptedData/> processing context.
 * @tmpl:		the pointer to <enc:EncryptedData/> template node.
 * @uri:		the URI.
 *
 * Encrypts data from @uri according to template @tmpl.
 *
 * Returns: 0 on success or a negative value if an error occurs.
 */
int 
xmlSecEncCtxUriEncrypt(xmlSecEncCtxPtr encCtx, xmlNodePtr tmpl, const xmlChar *uri) {
    int ret;
    
    xmlSecAssert2(encCtx != NULL, -1);
    xmlSecAssert2(encCtx->result == NULL, -1);
    xmlSecAssert2(tmpl != NULL, -1);
    xmlSecAssert2(uri != NULL, -1);

    /* initialize context and add ID atributes to the list of known ids */    
    encCtx->operation = xmlSecTransformOperationEncrypt;
    xmlSecAddIDs(tmpl->doc, tmpl, xmlSecEncIds);

    /* we need to add input uri transform first */
    ret = xmlSecTransformCtxSetUri(&(encCtx->transformCtx), uri, tmpl);
    if(ret < 0) {
    	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecTransformCtxSetUri",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    "uri=%s",
		    xmlSecErrorsSafeString(uri));
	return(-1);
    }

    /* read the template and set encryption method, key, etc. */
    ret = xmlSecEncCtxEncDataNodeRead(encCtx, tmpl);
    if(ret < 0) {
    	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecEncCtxEncDataNodeRead",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }

    /* encrypt the data */
    ret = xmlSecTransformCtxExecute(&(encCtx->transformCtx), tmpl->doc);
    if(ret < 0) {
    	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecTransformCtxExecute",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }
        
    encCtx->result = encCtx->transformCtx.result;
    xmlSecAssert2(encCtx->result != NULL, -1);
    
    ret = xmlSecEncCtxEncDataNodeWrite(encCtx);
    if(ret < 0) {
    	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecEncCtxEncDataNodeWrite",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }    
    
    return(0);
}

/**
 * xmlSecEncCtxDecrypt:
 * @encCtx:		the pointer to <enc:EncryptedData/> processing context.
 * @node:		the pointer to <enc:EncryptedData/> node.
 *
 * Decrypts @node and if necessary replaces @node with decrypted data.
 *
 * Returns: 0 on success or a negative value if an error occurs.
 */
int 
xmlSecEncCtxDecrypt(xmlSecEncCtxPtr encCtx, xmlNodePtr node) {
    xmlSecBufferPtr buffer;
    int ret;
    
    xmlSecAssert2(encCtx != NULL, -1);
    xmlSecAssert2(node != NULL, -1);
    
    /* decrypt */
    buffer = xmlSecEncCtxDecryptToBuffer(encCtx, node);
    if(buffer == NULL) {
    	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecEncCtxDecryptToBuffer",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }
    
    /* replace original node if requested */
    if((encCtx->type != NULL) && xmlStrEqual(encCtx->type, xmlSecTypeEncElement)) {
        /* check if we need to return the replaced node */
        if((encCtx->flags & XMLSEC_ENC_RETURN_REPLACED_NODE) != 0) {
	        ret = xmlSecReplaceNodeBufferAndReturn(node, xmlSecBufferGetData(buffer),  xmlSecBufferGetSize(buffer), &(encCtx->replacedNodeList));
	        if(ret < 0) {
	            xmlSecError(XMLSEC_ERRORS_HERE,
			        NULL,
			        "xmlSecReplaceNodeBufferAndReturn",
			        XMLSEC_ERRORS_R_XMLSEC_FAILED,
			        "node=%s",
			        xmlSecErrorsSafeString(xmlSecNodeGetName(node)));
	            return(-1);	    	
	        }
        } else {
	        ret = xmlSecReplaceNodeBuffer(node, xmlSecBufferGetData(buffer),  xmlSecBufferGetSize(buffer));
	        if(ret < 0) {
	            xmlSecError(XMLSEC_ERRORS_HERE,
			        NULL,
			        "xmlSecReplaceNodeBuffer",
			        XMLSEC_ERRORS_R_XMLSEC_FAILED,
			        "node=%s",
			        xmlSecErrorsSafeString(xmlSecNodeGetName(node)));
	            return(-1);	    	
	        }
        }

        encCtx->resultReplaced = 1;			       
    } else if((encCtx->type != NULL) && xmlStrEqual(encCtx->type, xmlSecTypeEncContent)) {
        /* replace the node with the buffer */

        /* check if we need to return the replaced node */
        if((encCtx->flags & XMLSEC_ENC_RETURN_REPLACED_NODE) != 0) {
	        ret = xmlSecReplaceNodeBufferAndReturn(node, xmlSecBufferGetData(buffer), xmlSecBufferGetSize(buffer), &(encCtx->replacedNodeList));
	        if(ret < 0) {
	            xmlSecError(XMLSEC_ERRORS_HERE,
			        NULL,
			        "xmlSecReplaceNodeBufferAndReturn",
			        XMLSEC_ERRORS_R_XMLSEC_FAILED,
			        "node=%s",
			        xmlSecErrorsSafeString(xmlSecNodeGetName(node)));
	            return(-1);	    	
	        }	
        } else {
            ret = xmlSecReplaceNodeBuffer(node, xmlSecBufferGetData(buffer), xmlSecBufferGetSize(buffer));
	        if(ret < 0) {
	            xmlSecError(XMLSEC_ERRORS_HERE,
			        NULL,
			        "xmlSecReplaceNodeBuffer",
			        XMLSEC_ERRORS_R_XMLSEC_FAILED,
			        "node=%s",
			        xmlSecErrorsSafeString(xmlSecNodeGetName(node)));
	            return(-1);	    	
	        }	  
        }
    	encCtx->resultReplaced = 1;			       
    }

    return(0);
}

/**
 * xmlSecEncCtxDecryptToBuffer:
 * @encCtx:		the pointer to <enc:EncryptedData/> processing context.
 * @node:		the pointer to <enc:EncryptedData/> node.
 * 
 * Decrypts @node data to the @encCtx buffer.
 *
 * Returns: 0 on success or a negative value if an error occurs.
 */
xmlSecBufferPtr
xmlSecEncCtxDecryptToBuffer(xmlSecEncCtxPtr encCtx, xmlNodePtr node) {
    int ret;
    
    xmlSecAssert2(encCtx != NULL, NULL);
    xmlSecAssert2(encCtx->result == NULL, NULL);
    xmlSecAssert2(node != NULL, NULL);

    /* initialize context and add ID atributes to the list of known ids */    
    encCtx->operation = xmlSecTransformOperationDecrypt;
    xmlSecAddIDs(node->doc, node, xmlSecEncIds);

    ret = xmlSecEncCtxEncDataNodeRead(encCtx, node);
    if(ret < 0) {
    	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecEncCtxEncDataNodeRead",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(NULL);
    }

    /* decrypt the data */
    if(encCtx->cipherValueNode != NULL) {
        xmlChar* data = NULL;
        xmlSecSize dataSize = 0;

	data = xmlNodeGetContent(encCtx->cipherValueNode);
	if(data == NULL) {
	    xmlSecError(XMLSEC_ERRORS_HERE,
			NULL,
			xmlSecErrorsSafeString(xmlSecNodeGetName(encCtx->cipherValueNode)),
			XMLSEC_ERRORS_R_INVALID_NODE_CONTENT,
			XMLSEC_ERRORS_NO_MESSAGE);
	    return(NULL);
	}	
	dataSize = xmlStrlen(data);

        ret = xmlSecTransformCtxBinaryExecute(&(encCtx->transformCtx), data, dataSize);
	if(ret < 0) {
    	    xmlSecError(XMLSEC_ERRORS_HERE,
			NULL,
			"xmlSecTransformCtxBinaryExecute",
			XMLSEC_ERRORS_R_XMLSEC_FAILED,
			XMLSEC_ERRORS_NO_MESSAGE);
	    if(data != NULL) {
		xmlFree(data);
	    }
	    return(NULL);
	}
	if(data != NULL) {
	    xmlFree(data);
	}
    } else {
        ret = xmlSecTransformCtxExecute(&(encCtx->transformCtx), node->doc);
	if(ret < 0) {
    	    xmlSecError(XMLSEC_ERRORS_HERE,
			NULL,
			"xmlSecTransformCtxBinaryExecute",
			XMLSEC_ERRORS_R_XMLSEC_FAILED,
			XMLSEC_ERRORS_NO_MESSAGE);
	    return(NULL);
	}
    }
    
    encCtx->result = encCtx->transformCtx.result;
    xmlSecAssert2(encCtx->result != NULL, NULL);
    
    return(encCtx->result);
}

static int 
xmlSecEncCtxEncDataNodeRead(xmlSecEncCtxPtr encCtx, xmlNodePtr node) {
    xmlNodePtr cur;
    int ret;
    
    xmlSecAssert2(encCtx != NULL, -1);
    xmlSecAssert2((encCtx->operation == xmlSecTransformOperationEncrypt) || (encCtx->operation == xmlSecTransformOperationDecrypt), -1);
    xmlSecAssert2(node != NULL, -1);

    switch(encCtx->mode) {
	case xmlEncCtxModeEncryptedData:
	    if(!xmlSecCheckNodeName(node, xmlSecNodeEncryptedData, xmlSecEncNs)) {
    		xmlSecError(XMLSEC_ERRORS_HERE,
			    NULL,
			    xmlSecErrorsSafeString(xmlSecNodeGetName(node)),
			    XMLSEC_ERRORS_R_INVALID_NODE,
			    "expected=%s",
			    xmlSecErrorsSafeString(xmlSecNodeEncryptedData));
		return(-1);	    
	    }
	    break;
	case xmlEncCtxModeEncryptedKey:
	    if(!xmlSecCheckNodeName(node, xmlSecNodeEncryptedKey, xmlSecEncNs)) {
    		xmlSecError(XMLSEC_ERRORS_HERE,
			    NULL,
			    xmlSecErrorsSafeString(xmlSecNodeGetName(node)),
			    XMLSEC_ERRORS_R_INVALID_NODE,
			    "expected=%s",
			    xmlSecErrorsSafeString(xmlSecNodeEncryptedKey));
		return(-1);	    
	    }
	    break;
    }
    
    /* first read node data */
    xmlSecAssert2(encCtx->id == NULL, -1);
    xmlSecAssert2(encCtx->type == NULL, -1);
    xmlSecAssert2(encCtx->mimeType == NULL, -1);
    xmlSecAssert2(encCtx->encoding == NULL, -1);
    xmlSecAssert2(encCtx->recipient == NULL, -1);
    xmlSecAssert2(encCtx->carriedKeyName == NULL, -1);
    
    encCtx->id = xmlGetProp(node, xmlSecAttrId);
    encCtx->type = xmlGetProp(node, xmlSecAttrType);
    encCtx->mimeType = xmlGetProp(node, xmlSecAttrMimeType);
    encCtx->encoding = xmlGetProp(node, xmlSecAttrEncoding);    
    if(encCtx->mode == xmlEncCtxModeEncryptedKey) {
	encCtx->recipient = xmlGetProp(node, xmlSecAttrRecipient);    
	/* todo: check recipient? */
    }
    cur = xmlSecGetNextElementNode(node->children);
    
    /* first node is optional EncryptionMethod, we'll read it later */
    xmlSecAssert2(encCtx->encMethodNode == NULL, -1);
    if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeEncryptionMethod, xmlSecEncNs))) {
	encCtx->encMethodNode = cur;
        cur = xmlSecGetNextElementNode(cur->next);
    }

    /* next node is optional KeyInfo, we'll process it later */
    xmlSecAssert2(encCtx->keyInfoNode == NULL, -1);
    if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeKeyInfo, xmlSecDSigNs))) {
	encCtx->keyInfoNode = cur;
	cur = xmlSecGetNextElementNode(cur->next);
    }    

    /* next is required CipherData node */
    if((cur == NULL) || (!xmlSecCheckNodeName(cur, xmlSecNodeCipherData, xmlSecEncNs))) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    xmlSecErrorsSafeString(xmlSecNodeGetName(cur)),
		    XMLSEC_ERRORS_R_INVALID_NODE,
		    "node=%s",
		    xmlSecErrorsSafeString(xmlSecNodeCipherData));
	return(-1);
    }
    
    ret = xmlSecEncCtxCipherDataNodeRead(encCtx, cur);
    if(ret < 0) {
        xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecEncCtxCipherDataNodeRead",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }
    cur = xmlSecGetNextElementNode(cur->next);

    /* next is optional EncryptionProperties node (we simply ignore it) */
    if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeEncryptionProperties, xmlSecEncNs))) {
	cur = xmlSecGetNextElementNode(cur->next);
    }

    /* there are more possible nodes for the <EncryptedKey> node */
    if(encCtx->mode == xmlEncCtxModeEncryptedKey) {
	/* next is optional ReferenceList node (we simply ignore it) */
        if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeReferenceList, xmlSecEncNs))) {
	    cur = xmlSecGetNextElementNode(cur->next);
	}

        /* next is optional CarriedKeyName node (we simply ignore it) */
	if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeCarriedKeyName, xmlSecEncNs))) {
	    encCtx->carriedKeyName = xmlNodeGetContent(cur);
	    if(encCtx->carriedKeyName == NULL) {
		xmlSecError(XMLSEC_ERRORS_HERE,
			    NULL,
			    xmlSecErrorsSafeString(xmlSecNodeGetName(cur)),
			    XMLSEC_ERRORS_R_INVALID_NODE_CONTENT,
			    "node=%s",
			    xmlSecErrorsSafeString(xmlSecNodeCipherData));
		return(-1);
	    }
	    /* TODO: decode the name? */
	    cur = xmlSecGetNextElementNode(cur->next);
	}
    }

    /* if there is something left than it's an error */
    if(cur != NULL) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    xmlSecErrorsSafeString(xmlSecNodeGetName(cur)),
		    XMLSEC_ERRORS_R_UNEXPECTED_NODE,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }

    /* now read the encryption method node */
    xmlSecAssert2(encCtx->encMethod == NULL, -1);
    if(encCtx->encMethodNode != NULL) {
	encCtx->encMethod = xmlSecTransformCtxNodeRead(&(encCtx->transformCtx), encCtx->encMethodNode,
						xmlSecTransformUsageEncryptionMethod);
	if(encCtx->encMethod == NULL) {
	    xmlSecError(XMLSEC_ERRORS_HERE,
		    	NULL,
			"xmlSecTransformCtxNodeRead",
			XMLSEC_ERRORS_R_XMLSEC_FAILED,
			"node=%s",
			xmlSecErrorsSafeString(xmlSecNodeGetName(encCtx->encMethodNode)));
	    return(-1);	    
	}	
    } else if(encCtx->defEncMethodId != xmlSecTransformIdUnknown) {
	encCtx->encMethod = xmlSecTransformCtxCreateAndAppend(&(encCtx->transformCtx), 
							      encCtx->defEncMethodId);
	if(encCtx->encMethod == NULL) {
    	    xmlSecError(XMLSEC_ERRORS_HERE,
			NULL,
			"xmlSecTransformCtxAppend",
			XMLSEC_ERRORS_R_XMLSEC_FAILED,
			XMLSEC_ERRORS_NO_MESSAGE);
	    return(-1);
	}
    } else {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    NULL,
		    XMLSEC_ERRORS_R_INVALID_DATA,
		    "encryption method not specified");
	return(-1);
    }
    encCtx->encMethod->operation = encCtx->operation;
    
    /* we have encryption method, find key */
    ret = xmlSecTransformSetKeyReq(encCtx->encMethod, &(encCtx->keyInfoReadCtx.keyReq));
    if(ret < 0) {
    	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecTransformSetKeyReq",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    "transform=%s",
		    xmlSecErrorsSafeString(xmlSecTransformGetName(encCtx->encMethod)));
	return(-1);
    }	

    /* TODO: KeyInfo node != NULL and encKey != NULL */    	
    if((encCtx->encKey == NULL) && (encCtx->keyInfoReadCtx.keysMngr != NULL) 
			&& (encCtx->keyInfoReadCtx.keysMngr->getKey != NULL)) {
	encCtx->encKey = (encCtx->keyInfoReadCtx.keysMngr->getKey)(encCtx->keyInfoNode, 
							     &(encCtx->keyInfoReadCtx));
    }
    
    /* check that we have exactly what we want */
    if((encCtx->encKey == NULL) || 
       (!xmlSecKeyMatch(encCtx->encKey, NULL, &(encCtx->keyInfoReadCtx.keyReq)))) {

	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    NULL,
		    XMLSEC_ERRORS_R_KEY_NOT_FOUND,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }
    
    /* set the key to the transform */
    ret = xmlSecTransformSetKey(encCtx->encMethod, encCtx->encKey);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecTransformSetKey",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    "transform=%s",
		    xmlSecErrorsSafeString(xmlSecTransformGetName(encCtx->encMethod)));
	return(-1);
    }

    /* if we need to write result to xml node then we need base64 encode it */
    if((encCtx->operation == xmlSecTransformOperationEncrypt) && (encCtx->cipherValueNode != NULL)) {	
	xmlSecTransformPtr base64Encode;
	
	/* we need to add base64 encode transform */
	base64Encode = xmlSecTransformCtxCreateAndAppend(&(encCtx->transformCtx), xmlSecTransformBase64Id);
    	if(base64Encode == NULL) {
    	    xmlSecError(XMLSEC_ERRORS_HERE,
			NULL,
			"xmlSecTransformCtxCreateAndAppend",
			XMLSEC_ERRORS_R_XMLSEC_FAILED,
			XMLSEC_ERRORS_NO_MESSAGE);
	    return(-1);
	}
	base64Encode->operation 	= xmlSecTransformOperationEncode;
	encCtx->resultBase64Encoded 	= 1;
    }
    
    return(0);
}

static int 
xmlSecEncCtxEncDataNodeWrite(xmlSecEncCtxPtr encCtx) {
    int ret;
    
    xmlSecAssert2(encCtx != NULL, -1);
    xmlSecAssert2(encCtx->result != NULL, -1);
    xmlSecAssert2(encCtx->encKey != NULL, -1);
    
    /* write encrypted data to xml (if requested) */
    if(encCtx->cipherValueNode != NULL) {	
	xmlSecAssert2(xmlSecBufferGetData(encCtx->result) != NULL, -1);

	xmlNodeSetContentLen(encCtx->cipherValueNode,
			    xmlSecBufferGetData(encCtx->result),
			    xmlSecBufferGetSize(encCtx->result));
	encCtx->resultReplaced = 1;
    }

    /* update <enc:KeyInfo/> node */
    if(encCtx->keyInfoNode != NULL) {
	ret = xmlSecKeyInfoNodeWrite(encCtx->keyInfoNode, encCtx->encKey, &(encCtx->keyInfoWriteCtx));
	if(ret < 0) {
    	    xmlSecError(XMLSEC_ERRORS_HERE,
			NULL,
			"xmlSecKeyInfoNodeWrite",
			XMLSEC_ERRORS_R_XMLSEC_FAILED,
			XMLSEC_ERRORS_NO_MESSAGE);
	    return(-1);
	}	
    }
    
    return(0);
}

static int 
xmlSecEncCtxCipherDataNodeRead(xmlSecEncCtxPtr encCtx, xmlNodePtr node) {
    xmlNodePtr cur;
    int ret;
    
    xmlSecAssert2(encCtx != NULL, -1);
    xmlSecAssert2(node != NULL, -1);
    
    cur = xmlSecGetNextElementNode(node->children);
    
    /* we either have CipherValue or CipherReference node  */
    xmlSecAssert2(encCtx->cipherValueNode == NULL, -1);
    if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeCipherValue, xmlSecEncNs))) {
        /* don't need data from CipherData node when we are encrypting */
	if(encCtx->operation == xmlSecTransformOperationDecrypt) {
	    xmlSecTransformPtr base64Decode;
	
	    /* we need to add base64 decode transform */
	    base64Decode = xmlSecTransformCtxCreateAndPrepend(&(encCtx->transformCtx), xmlSecTransformBase64Id);
    	    if(base64Decode == NULL) {
    		xmlSecError(XMLSEC_ERRORS_HERE,
			    NULL,
			    "xmlSecTransformCtxCreateAndPrepend",
			    XMLSEC_ERRORS_R_XMLSEC_FAILED,
			    XMLSEC_ERRORS_NO_MESSAGE);
	        return(-1);
	    }
	}
	encCtx->cipherValueNode = cur;
        cur = xmlSecGetNextElementNode(cur->next);
    } else if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeCipherReference, xmlSecEncNs))) {
        /* don't need data from CipherReference node when we are encrypting */
	if(encCtx->operation == xmlSecTransformOperationDecrypt) {
    	    ret = xmlSecEncCtxCipherReferenceNodeRead(encCtx, cur);
	    if(ret < 0) {
		xmlSecError(XMLSEC_ERRORS_HERE,
		    	    NULL,
			    "xmlSecEncCtxCipherReferenceNodeRead",
			    XMLSEC_ERRORS_R_XMLSEC_FAILED,
			    "node=%s",
			    xmlSecErrorsSafeString(xmlSecNodeGetName(cur)));
		return(-1);	    
	    }
	}	
        cur = xmlSecGetNextElementNode(cur->next);
    }
    
    if(cur != NULL) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    xmlSecErrorsSafeString(xmlSecNodeGetName(cur)),
		    XMLSEC_ERRORS_R_UNEXPECTED_NODE,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }
    return(0);
}

static int 
xmlSecEncCtxCipherReferenceNodeRead(xmlSecEncCtxPtr encCtx, xmlNodePtr node) {
    xmlNodePtr cur;
    xmlChar* uri;
    int ret;
    
    xmlSecAssert2(encCtx != NULL, -1);
    xmlSecAssert2(node != NULL, -1);
    
    /* first read the optional uri attr and check that we can process it */
    uri = xmlGetProp(node, xmlSecAttrURI);
    ret = xmlSecTransformCtxSetUri(&(encCtx->transformCtx), uri, node);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecTransformCtxSetUri",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    "uri=%s",
		    xmlSecErrorsSafeString(uri));
	xmlFree(uri);
	return(-1);	    
    }		
    xmlFree(uri);

    cur = xmlSecGetNextElementNode(node->children);
    
    /* the only one node is optional Transforms node */
    if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeTransforms, xmlSecEncNs))) {
	ret = xmlSecTransformCtxNodesListRead(&(encCtx->transformCtx), cur,
				    xmlSecTransformUsageDSigTransform);
	if(ret < 0) {
	    xmlSecError(XMLSEC_ERRORS_HERE,
		    	NULL,
			"xmlSecTransformCtxNodesListRead",
			XMLSEC_ERRORS_R_XMLSEC_FAILED,
			"node=%s",
			xmlSecErrorsSafeString(xmlSecNodeGetName(encCtx->encMethodNode)));
	    return(-1);	    
	}	
        cur = xmlSecGetNextElementNode(cur->next);
    }
    
    /* if there is something left than it's an error */
    if(cur != NULL) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    xmlSecErrorsSafeString(xmlSecNodeGetName(cur)),
		    XMLSEC_ERRORS_R_UNEXPECTED_NODE,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }
    return(0);
}

/**
 * xmlSecEncCtxDebugDump:
 * @encCtx:		the pointer to <enc:EncryptedData/> processing context.
 * @output:		the pointer to output FILE.
 *
 * Prints the debug information about @encCtx to @output.
 */
void 
xmlSecEncCtxDebugDump(xmlSecEncCtxPtr encCtx, FILE* output) {
    xmlSecAssert(encCtx != NULL);
    xmlSecAssert(output != NULL);

    switch(encCtx->mode) {
	case xmlEncCtxModeEncryptedData:
	    if(encCtx->operation == xmlSecTransformOperationEncrypt) {    
		fprintf(output, "= DATA ENCRYPTION CONTEXT\n");
	    } else {
		fprintf(output, "= DATA DECRYPTION CONTEXT\n");
	    }
	    break;
	case xmlEncCtxModeEncryptedKey:
	    if(encCtx->operation == xmlSecTransformOperationEncrypt) {    
		fprintf(output, "= KEY ENCRYPTION CONTEXT\n");
	    } else {
		fprintf(output, "= KEY DECRYPTION CONTEXT\n");
	    }
	    break;
    }
    fprintf(output, "== Status: %s\n",
	    (encCtx->resultReplaced) ? "replaced" : "not-replaced" );

    fprintf(output, "== flags: 0x%08x\n", encCtx->flags);
    fprintf(output, "== flags2: 0x%08x\n", encCtx->flags2);

    if(encCtx->id != NULL) {
	fprintf(output, "== Id: \"%s\"\n", encCtx->id);
    }
    if(encCtx->type != NULL) {
	fprintf(output, "== Type: \"%s\"\n", encCtx->type);
    }
    if(encCtx->mimeType != NULL) {
	fprintf(output, "== MimeType: \"%s\"\n", encCtx->mimeType);
    }
    if(encCtx->encoding != NULL) {
	fprintf(output, "== Encoding: \"%s\"\n", encCtx->encoding);
    }
    if(encCtx->recipient != NULL) {
	fprintf(output, "== Recipient: \"%s\"\n", encCtx->recipient);
    }
    if(encCtx->carriedKeyName != NULL) {
	fprintf(output, "== CarriedKeyName: \"%s\"\n", encCtx->carriedKeyName);
    }
    
    fprintf(output, "== Key Info Read Ctx:\n");
    xmlSecKeyInfoCtxDebugDump(&(encCtx->keyInfoReadCtx), output);

    fprintf(output, "== Key Info Write Ctx:\n");
    xmlSecKeyInfoCtxDebugDump(&(encCtx->keyInfoWriteCtx), output);

    fprintf(output, "== Encryption Transform Ctx:\n");
    xmlSecTransformCtxDebugDump(&(encCtx->transformCtx), output);

    if(encCtx->encMethod != NULL) {
        fprintf(output, "== Encryption Method:\n");
	xmlSecTransformDebugDump(encCtx->encMethod, output);
    }

    if(encCtx->encKey != NULL) {
        fprintf(output, "== Encryption Key:\n");
	xmlSecKeyDebugDump(encCtx->encKey, output);
    }
    
    if((encCtx->result != NULL) && 
       (xmlSecBufferGetData(encCtx->result) != NULL) && 
       (encCtx->resultBase64Encoded != 0)) {

	fprintf(output, "== Result - start buffer:\n");
	fwrite(xmlSecBufferGetData(encCtx->result), 
	       xmlSecBufferGetSize(encCtx->result), 1,
	       output);
	fprintf(output, "\n== Result - end buffer\n");
    }
}

/**
 * xmlSecEncCtxDebugXmlDump:
 * @encCtx:		the pointer to <enc:EncryptedData/> processing context.
 * @output:		the pointer to output FILE.
 *
 * Prints the debug information about @encCtx to @output in XML format.
 */
void 
xmlSecEncCtxDebugXmlDump(xmlSecEncCtxPtr encCtx, FILE* output) {
    xmlSecAssert(encCtx != NULL);
    xmlSecAssert(output != NULL);

    switch(encCtx->mode) {
	case xmlEncCtxModeEncryptedData:
	    if(encCtx->operation == xmlSecTransformOperationEncrypt) {    
		fprintf(output, "<DataEncryptionContext ");
	    } else {
		fprintf(output, "<DataDecryptionContext ");
	    }
	    break;
	case xmlEncCtxModeEncryptedKey:
	    if(encCtx->operation == xmlSecTransformOperationEncrypt) {    
		fprintf(output, "<KeyEncryptionContext ");
	    } else {
		fprintf(output, "<KeyDecryptionContext ");
	    }
	    break;
    }
    fprintf(output, "status=\"%s\" >\n", (encCtx->resultReplaced) ? "replaced" : "not-replaced" );

    fprintf(output, "<Flags>%08x</Flags>\n", encCtx->flags);
    fprintf(output, "<Flags2>%08x</Flags2>\n", encCtx->flags2);

    fprintf(output, "<Id>");
    xmlSecPrintXmlString(output, encCtx->id);
    fprintf(output, "</Id>");

    fprintf(output, "<Type>");
    xmlSecPrintXmlString(output, encCtx->type);
    fprintf(output, "</Type>");
    
    fprintf(output, "<MimeType>");
    xmlSecPrintXmlString(output, encCtx->mimeType);
    fprintf(output, "</MimeType>");

    fprintf(output, "<Encoding>");
    xmlSecPrintXmlString(output, encCtx->encoding);
    fprintf(output, "</Encoding>");

    fprintf(output, "<Recipient>");
    xmlSecPrintXmlString(output, encCtx->recipient);
    fprintf(output, "</Recipient>");

    fprintf(output, "<CarriedKeyName>");
    xmlSecPrintXmlString(output, encCtx->carriedKeyName);
    fprintf(output, "</CarriedKeyName>");

    fprintf(output, "<KeyInfoReadCtx>\n");
    xmlSecKeyInfoCtxDebugXmlDump(&(encCtx->keyInfoReadCtx), output);
    fprintf(output, "</KeyInfoReadCtx>\n");

    fprintf(output, "<KeyInfoWriteCtx>\n");
    xmlSecKeyInfoCtxDebugXmlDump(&(encCtx->keyInfoWriteCtx), output);
    fprintf(output, "</KeyInfoWriteCtx>\n");

    fprintf(output, "<EncryptionTransformCtx>\n");
    xmlSecTransformCtxDebugXmlDump(&(encCtx->transformCtx), output);
    fprintf(output, "</EncryptionTransformCtx>\n");

    if(encCtx->encMethod != NULL) {
        fprintf(output, "<EncryptionMethod>\n");
	xmlSecTransformDebugXmlDump(encCtx->encMethod, output);
        fprintf(output, "</EncryptionMethod>\n");
    }

    if(encCtx->encKey != NULL) {
        fprintf(output, "<EncryptionKey>\n");
	xmlSecKeyDebugXmlDump(encCtx->encKey, output);
        fprintf(output, "</EncryptionKey>\n");
    }
    
    if((encCtx->result != NULL) && 
       (xmlSecBufferGetData(encCtx->result) != NULL) && 
       (encCtx->resultBase64Encoded != 0)) {

	fprintf(output, "<Result>");
	fwrite(xmlSecBufferGetData(encCtx->result), 
	       xmlSecBufferGetSize(encCtx->result), 1,
	       output);
	fprintf(output, "</Result>\n");
    }

    switch(encCtx->mode) {
	case xmlEncCtxModeEncryptedData:
	    if(encCtx->operation == xmlSecTransformOperationEncrypt) {    
		fprintf(output, "</DataEncryptionContext>\n");
	    } else {
		fprintf(output, "</DataDecryptionContext>\n");
	    }
	    break;
	case xmlEncCtxModeEncryptedKey:
	    if(encCtx->operation == xmlSecTransformOperationEncrypt) {    
		fprintf(output, "</KeyEncryptionContext>\n");
	    } else {
		fprintf(output, "</KeyDecryptionContext>\n");
	    }
	    break;
    }
}

#endif /* XMLSEC_NO_XMLENC */

