/** 
 * XMLSec library
 * 
 * Reading/writing BIGNUM values
 * 
 * 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/buffer.h>
#include <xmlsec/base64.h>
#include <xmlsec/errors.h>

#include <xmlsec/openssl/crypto.h>
#include <xmlsec/openssl/bn.h>

/**
 * xmlSecOpenSSLNodeGetBNValue:
 * @cur: the poitner to an XML node.
 * @a: the BIGNUM buffer.
 *
 * Converts the node content from CryptoBinary format 
 * (http://www.w3.org/TR/xmldsig-core/#sec-CryptoBinary) 
 * to a BIGNUM. If no BIGNUM buffer provided then a new
 * BIGNUM is created (caller is responsible for freeing it).
 *
 * Returns a pointer to BIGNUM produced from CryptoBinary string
 * or NULL if an error occurs.
 */
BIGNUM*
xmlSecOpenSSLNodeGetBNValue(const xmlNodePtr cur, BIGNUM **a) {
    xmlSecBuffer buf;
    int ret;

    xmlSecAssert2(cur != NULL, NULL);

    ret = xmlSecBufferInitialize(&buf, 128);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecBufferInitialize",
	    	    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(NULL);
    }    
    
    ret = xmlSecBufferBase64NodeContentRead(&buf, cur);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecBufferBase64NodeContentRead",
	    	    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	xmlSecBufferFinalize(&buf);
	return(NULL);
    }    
    
    (*a) = BN_bin2bn(xmlSecBufferGetData(&buf), xmlSecBufferGetSize(&buf), (*a));
    if( (*a) == NULL) {	
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "BN_bin2bn",
	    	    XMLSEC_ERRORS_R_CRYPTO_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	xmlSecBufferFinalize(&buf);
	return(NULL);
    }
    xmlSecBufferFinalize(&buf);
    return(*a);
}

/**
 * xmlSecOpenSSLNodeSetBNValue:
 * @cur: the pointer to an XML node.
 * @a: the BIGNUM.
 * @addLineBreaks: if the flag is equal to 1 then 
 *		linebreaks will be added before and after
 *		new buffer content.
 *
 * Converts BIGNUM to CryptoBinary string
 * (http://www.w3.org/TR/xmldsig-core/#sec-CryptoBinary) 
 * and sets it as the content of the given node. If the 
 * addLineBreaks is set then line breaks are added 
 * before and after the CryptoBinary string.
 * 
 * Returns 0 on success or -1 otherwise.
 */
int
xmlSecOpenSSLNodeSetBNValue(xmlNodePtr cur, const BIGNUM *a, int addLineBreaks) {
    xmlSecBuffer buf;
    xmlSecSize size;
    int ret;
    
    xmlSecAssert2(a != NULL, -1);
    xmlSecAssert2(cur != NULL, -1);

    ret = xmlSecBufferInitialize(&buf, BN_num_bytes(a) + 1);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecBufferInitialize",
	    	    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    "size=%d", BN_num_bytes(a) + 1);
	return(-1);
    }    

    ret = BN_bn2bin(a, xmlSecBufferGetData(&buf));
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "BN_bn2bin",
	    	    XMLSEC_ERRORS_R_CRYPTO_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	xmlSecBufferFinalize(&buf);
	return(-1);
    }
    size = ret;
    
    ret = xmlSecBufferSetSize(&buf, size);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecBufferSetSize",
	    	    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    "size=%d", size);
	xmlSecBufferFinalize(&buf);
	return(-1);
    }

    if(addLineBreaks) {
	xmlNodeSetContent(cur, xmlSecStringCR);
    } else {
	xmlNodeSetContent(cur, xmlSecStringEmpty);
    }
    
    ret = xmlSecBufferBase64NodeContentWrite(&buf, cur, XMLSEC_BASE64_LINESIZE);
    if(ret < 0) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    NULL,
		    "xmlSecBufferBase64NodeContentWrite",
	    	    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	xmlSecBufferFinalize(&buf);
	return(-1);
    }

    if(addLineBreaks) {
	xmlNodeAddContent(cur, xmlSecStringCR);
    }

    xmlSecBufferFinalize(&buf);
    return(0);
}

