/**
 * XML Security Library (http://www.aleksey.com/xmlsec).
 *
 * This is free software; see the Copyright file in the source distribution for precise wording.
 *
 * Copyright (C) 2002-2026 Aleksey Sanin <aleksey@aleksey.com>. All Rights Reserved.
 */
/**
 * @addtogroup xmlsec_core_keys
 * @brief Crypto key object functions.
 */
#include "globals.h"

#include <stdlib.h>
#include <string.h>

#include <libxml/tree.h>

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

#include "cast_helpers.h"

/******************************************************************************
 *
 * xmlSecKeyUseWith
 *
  *****************************************************************************/
/**
 * @brief Initializes @p keyUseWith object.
 * @param keyUseWith the pointer to information about key application/user.
 *
 * @return 0 on success or a negative value if an error occurs.
 */
int
xmlSecKeyUseWithInitialize(xmlSecKeyUseWithPtr keyUseWith) {
    xmlSecAssert2(keyUseWith != NULL, -1);

    memset(keyUseWith, 0, sizeof(xmlSecKeyUseWith));
    return(0);
}

/**
 * @brief Finalizes @p keyUseWith object.
 * @param keyUseWith the pointer to information about key application/user.
 */
void
xmlSecKeyUseWithFinalize(xmlSecKeyUseWithPtr keyUseWith) {
    xmlSecAssert(keyUseWith != NULL);

    xmlSecKeyUseWithReset(keyUseWith);
    memset(keyUseWith, 0, sizeof(xmlSecKeyUseWith));
}

/**
 * @brief Resets a keyUseWith to its initial state.
 * @details Resets the @p keyUseWith to its state after initialization.
 * @param keyUseWith the pointer to information about key application/user.
 */
void
xmlSecKeyUseWithReset(xmlSecKeyUseWithPtr keyUseWith) {
    xmlSecAssert(keyUseWith != NULL);

    xmlSecKeyUseWithSet(keyUseWith, NULL, NULL);
}

/**
 * @brief Copies information from @p dst to @p src.
 * @param dst the pointer to destination object.
 * @param src the pointer to source object.
 *
 * @return 0 on success or a negative value if an error occurs.
 */
int
xmlSecKeyUseWithCopy(xmlSecKeyUseWithPtr dst, xmlSecKeyUseWithPtr src) {
    xmlSecAssert2(dst != NULL, -1);
    xmlSecAssert2(src != NULL, -1);

    return(xmlSecKeyUseWithSet(dst, src->application, src->identifier));
}

/**
 * @brief Creates a new xmlSecKeyUseWith object.
 * @details Creates new xmlSecKeyUseWith object. The caller is responsible for destroying
 * returned object with #xmlSecKeyUseWithDestroy function.
 * @param application the application value.
 * @param identifier the identifier value.
 *
 * @return pointer to newly created object or NULL if an error occurs.
 */
xmlSecKeyUseWithPtr
xmlSecKeyUseWithCreate(const xmlChar* application, const xmlChar* identifier) {
    xmlSecKeyUseWithPtr keyUseWith;
    int ret;

    /* Allocate a new xmlSecKeyUseWith and fill the fields. */
    keyUseWith = (xmlSecKeyUseWithPtr)xmlMalloc(sizeof(xmlSecKeyUseWith));
    if(keyUseWith == NULL) {
        xmlSecMallocError(sizeof(xmlSecKeyUseWith), NULL);
        return(NULL);
    }
    memset(keyUseWith, 0, sizeof(xmlSecKeyUseWith));

    ret = xmlSecKeyUseWithInitialize(keyUseWith);
    if(ret < 0) {
        xmlSecInternalError("xmlSecKeyUseWithInitialize", NULL);
        xmlSecKeyUseWithDestroy(keyUseWith);
        return(NULL);
    }

    ret = xmlSecKeyUseWithSet(keyUseWith, application, identifier);
    if(ret < 0) {
        xmlSecInternalError("xmlSecKeyUseWithSet", NULL);
        xmlSecKeyUseWithDestroy(keyUseWith);
        return(NULL);
    }

    return(keyUseWith);
}

/**
 * @brief Duplicates a keyUseWith object.
 * @details Duplicates @p keyUseWith object. The caller is responsible for destroying
 * returned object with #xmlSecKeyUseWithDestroy function.
 * @param keyUseWith the pointer to information about key application/user.
 *
 * @return pointer to newly created object or NULL if an error occurs.
 */
xmlSecKeyUseWithPtr
xmlSecKeyUseWithDuplicate(xmlSecKeyUseWithPtr keyUseWith) {
    int ret;

    xmlSecKeyUseWithPtr newKeyUseWith;

    xmlSecAssert2(keyUseWith != NULL, NULL);

    newKeyUseWith = xmlSecKeyUseWithCreate(NULL, NULL);
    if(newKeyUseWith == NULL) {
        xmlSecInternalError("xmlSecKeyUseWithCreate", NULL);
        return(NULL);
    }

    ret = xmlSecKeyUseWithCopy(newKeyUseWith, keyUseWith);
    if(ret < 0) {
        xmlSecInternalError("xmlSecKeyUseWithCopy", NULL);
        xmlSecKeyUseWithDestroy(keyUseWith);
        return(NULL);
    }

    return(newKeyUseWith);
}

/**
 * @brief Destroys a keyUseWith object.
 * @details Destroys @p keyUseWith created with #xmlSecKeyUseWithCreate or #xmlSecKeyUseWithDuplicate
 * functions.
 * @param keyUseWith the pointer to information about key application/user.
 */
void
xmlSecKeyUseWithDestroy(xmlSecKeyUseWithPtr keyUseWith) {
    xmlSecAssert(keyUseWith != NULL);

    xmlSecKeyUseWithFinalize(keyUseWith);
    xmlFree(keyUseWith);
}

/**
 * @brief Sets application and identifier in a keyUseWith object.
 * @details Sets @p application and @p identifier in the @p keyUseWith.
 * @param keyUseWith the pointer to information about key application/user.
 * @param application the new application value.
 * @param identifier the new identifier value.
 *
 * @return 0 on success or a negative value if an error occurs.
 */
int
xmlSecKeyUseWithSet(xmlSecKeyUseWithPtr keyUseWith, const xmlChar* application, const xmlChar* identifier) {
    xmlSecAssert2(keyUseWith != NULL, -1);

    if(keyUseWith->application != NULL) {
        xmlFree(keyUseWith->application);
        keyUseWith->application = NULL;
    }
    if(keyUseWith->identifier != NULL) {
        xmlFree(keyUseWith->identifier);
        keyUseWith->identifier = NULL;
    }

    if(application != NULL) {
        keyUseWith->application = xmlStrdup(application);
        if(keyUseWith->application == NULL) {
            xmlSecStrdupError(application, NULL);
            return(-1);
        }
    }
    if(identifier != NULL) {
        keyUseWith->identifier = xmlStrdup(identifier);
        if(keyUseWith->identifier == NULL) {
            xmlSecStrdupError(identifier, NULL);
            return(-1);
        }
    }

    return(0);
}

/**
 * @brief Prints keyUseWith debug information to a file.
 * @details Prints xmlSecKeyUseWith debug information to a file @p output.
 * @param keyUseWith the pointer to information about key application/user.
 * @param output the pointer to output FILE.
 */
void
xmlSecKeyUseWithDebugDump(xmlSecKeyUseWithPtr keyUseWith, FILE* output) {
    xmlSecAssert(keyUseWith != NULL);
    xmlSecAssert(output != NULL);

    fprintf(output, "=== KeyUseWith: application=\"%s\",identifier=\"%s\"\n",
                (keyUseWith->application) ? keyUseWith->application : BAD_CAST "",
                (keyUseWith->identifier) ? keyUseWith->identifier : BAD_CAST "");
}

/**
 * @brief Prints keyUseWith debug information in XML format.
 * @details Prints xmlSecKeyUseWith debug information to a file @p output in XML format.
 * @param keyUseWith the pointer to information about key application/user.
 * @param output the pointer to output FILE.
 */
void
xmlSecKeyUseWithDebugXmlDump(xmlSecKeyUseWithPtr keyUseWith, FILE* output) {
    xmlSecAssert(keyUseWith != NULL);
    xmlSecAssert(output != NULL);

    fprintf(output, "<KeyUseWith>\n");

    fprintf(output, "<Application>");
    xmlSecPrintXmlString(output, keyUseWith->application);
    fprintf(output, "</Application>");

    fprintf(output, "<Identifier>");
    xmlSecPrintXmlString(output, keyUseWith->identifier);
    fprintf(output, "</Identifier>");

    fprintf(output, "</KeyUseWith>\n");
}

/******************************************************************************
 *
 * KeyUseWith list
 *
  *****************************************************************************/
static xmlSecPtrListKlass xmlSecKeyUseWithPtrListKlass = {
    BAD_CAST "key-use-with-list",
    (xmlSecPtrDuplicateItemMethod)xmlSecKeyUseWithDuplicate,    /* xmlSecPtrDuplicateItemMethod duplicateItem; */
    (xmlSecPtrDestroyItemMethod)xmlSecKeyUseWithDestroy,        /* xmlSecPtrDestroyItemMethod destroyItem; */
    (xmlSecPtrDebugDumpItemMethod)xmlSecKeyUseWithDebugDump,    /* xmlSecPtrDebugDumpItemMethod debugDumpItem; */
    (xmlSecPtrDebugDumpItemMethod)xmlSecKeyUseWithDebugXmlDump, /* xmlSecPtrDebugDumpItemMethod debugXmlDumpItem; */
};

/**
 * @brief The key data list klass.
 *
 * @return pointer to the key data list klass.
 */
xmlSecPtrListId
xmlSecKeyUseWithPtrListGetKlass(void) {
    return(&xmlSecKeyUseWithPtrListKlass);
}

/******************************************************************************
 *
 * xmlSecKeyReq - what key are we looking for?
 *
  *****************************************************************************/
/**
 * @brief Initializes a key requirements object.
 * @details Initialize key requirements object. Caller is responsible for
 * cleaning it with #xmlSecKeyReqFinalize function.
 * @param keyReq the pointer to key requirements object.
 *
 * @return 0 on success or a negative value if an error occurs.
 */
int
xmlSecKeyReqInitialize(xmlSecKeyReqPtr keyReq) {
    int ret;

    xmlSecAssert2(keyReq != NULL, -1);

    memset(keyReq, 0, sizeof(xmlSecKeyReq));

    keyReq->keyUsage    = xmlSecKeyUsageAny;    /* by default you can do whatever you want with the key */
    ret = xmlSecPtrListInitialize(&keyReq->keyUseWithList, xmlSecKeyUseWithPtrListId);
    if(ret < 0) {
        xmlSecInternalError("xmlSecPtrListInitialize", NULL);
        return(-1);
    }


    return(0);
}

/**
 * @brief Cleans up a key requirements object.
 * @details Cleans the key requirements object initialized with #xmlSecKeyReqInitialize
 * function.
 * @param keyReq the pointer to key requirements object.
 */
void
xmlSecKeyReqFinalize(xmlSecKeyReqPtr keyReq) {
    xmlSecAssert(keyReq != NULL);

    xmlSecPtrListFinalize(&keyReq->keyUseWithList);
    memset(keyReq, 0, sizeof(xmlSecKeyReq));
}

/**
 * @brief Resets key requirements for a new key search.
 * @details Resets key requirements object for new key search.
 * @param keyReq the pointer to key requirements object.
 */
void
xmlSecKeyReqReset(xmlSecKeyReqPtr keyReq) {
    xmlSecAssert(keyReq != NULL);

    xmlSecPtrListEmpty(&keyReq->keyUseWithList);
    keyReq->keyId       = NULL;
    keyReq->keyType     = 0;
    keyReq->keyUsage    = xmlSecKeyUsageAny;
    keyReq->keyBitsSize = 0;
}

/**
 * @brief Copies key requirements between objects.
 * @details Copies key requirements from @p src object to @p dst object.
 * @param dst the pointer to destination object.
 * @param src the pointer to source object.
 *
 * @return 0 on success and a negative value if an error occurs.
 */
int
xmlSecKeyReqCopy(xmlSecKeyReqPtr dst, xmlSecKeyReqPtr src) {
    int ret;

    xmlSecAssert2(dst != NULL, -1);
    xmlSecAssert2(src != NULL, -1);

    dst->keyId          = src->keyId;
    dst->keyType        = src->keyType;
    dst->keyUsage       = src->keyUsage;
    dst->keyBitsSize    = src->keyBitsSize;

    ret = xmlSecPtrListCopy(&dst->keyUseWithList, &src->keyUseWithList);
    if(ret < 0) {
        xmlSecInternalError("xmlSecPtrListCopy", NULL);
        return(-1);
    }

    return(0);
}

/**
 * @brief Checks whether a key matches the given requirements.
 * @details Checks whether @p key matches key requirements @p keyReq.
 * @param keyReq the pointer to key requirements object.
 * @param key the pointer to key.
 *
 * @return 1 if key matches requirements, 0 if not and a negative value
 * if an error occurs.
 */
int
xmlSecKeyReqMatchKey(xmlSecKeyReqPtr keyReq, xmlSecKeyPtr key) {
    xmlSecAssert2(keyReq != NULL, -1);
    xmlSecAssert2(xmlSecKeyIsValid(key), -1);

    if((keyReq->keyType != xmlSecKeyDataTypeUnknown) && ((xmlSecKeyGetType(key) & keyReq->keyType) == 0)) {
        return(0);
    }
    if((keyReq->keyUsage != xmlSecKeyDataUsageUnknown) && ((keyReq->keyUsage & key->usage) == 0)) {
        return(0);
    }

    return(xmlSecKeyReqMatchKeyValue(keyReq, xmlSecKeyGetValue(key)));
}

/**
 * @brief Checks whether a key value matches the given requirements.
 * @details Checks whether @p keyValue matches key requirements @p keyReq.
 * @param keyReq the pointer to key requirements.
 * @param value the pointer to key value.
 *
 * @return 1 if key value matches requirements, 0 if not and a negative value
 * if an error occurs.
 */
int
xmlSecKeyReqMatchKeyValue(xmlSecKeyReqPtr keyReq, xmlSecKeyDataPtr value) {
    xmlSecAssert2(keyReq != NULL, -1);
    xmlSecAssert2(value != NULL, -1);

    if((keyReq->keyId != xmlSecKeyDataIdUnknown) &&
       (!xmlSecKeyDataCheckId(value, keyReq->keyId))) {
        return(0);
    }
    if((keyReq->keyBitsSize > 0) &&
       (xmlSecKeyDataGetSize(value) > 0) &&
       (xmlSecKeyDataGetSize(value) < keyReq->keyBitsSize)) {
        return(0);
    }
    return(1);
}

/**
 * @brief Prints key requirements debug information.
 * @details Prints debug information about @p keyReq into @p output.
 * @param keyReq the pointer to key requirements object.
 * @param output the pointer to output FILE.
 */
void
xmlSecKeyReqDebugDump(xmlSecKeyReqPtr keyReq, FILE* output) {
    xmlSecAssert(keyReq != NULL);
    xmlSecAssert(output != NULL);

    fprintf(output, "=== KeyReq:\n");
    fprintf(output, "==== keyId: %s\n",
            (xmlSecKeyDataKlassGetName(keyReq->keyId)) ?
                xmlSecKeyDataKlassGetName(keyReq->keyId) :
                BAD_CAST "NULL");
    fprintf(output, "==== keyType: 0x%08x\n", keyReq->keyType);
    fprintf(output, "==== keyUsage: 0x%08x\n", keyReq->keyUsage);
    fprintf(output, "==== keyBitsSize: " XMLSEC_SIZE_FMT "\n", keyReq->keyBitsSize);
    xmlSecPtrListDebugDump(&(keyReq->keyUseWithList), output);
}

/**
 * @brief Prints key requirements debug information in XML format.
 * @details Prints debug information about @p keyReq into @p output in XML format.
 * @param keyReq the pointer to key requirements object.
 * @param output the pointer to output FILE.
 */
void
xmlSecKeyReqDebugXmlDump(xmlSecKeyReqPtr keyReq, FILE* output) {
    xmlSecAssert(keyReq != NULL);
    xmlSecAssert(output != NULL);

    fprintf(output, "<KeyReq>\n");

    fprintf(output, "<KeyId>");
    xmlSecPrintXmlString(output, xmlSecKeyDataKlassGetName(keyReq->keyId));
    fprintf(output, "</KeyId>\n");

    fprintf(output, "<KeyType>0x%08x</KeyType>\n", keyReq->keyType);
    fprintf(output, "<KeyUsage>0x%08x</KeyUsage>\n", keyReq->keyUsage);
    fprintf(output, "<KeyBitsSize>" XMLSEC_SIZE_FMT "</KeyBitsSize>\n", keyReq->keyBitsSize);
    xmlSecPtrListDebugXmlDump(&(keyReq->keyUseWithList), output);
    fprintf(output, "</KeyReq>\n");
}


/******************************************************************************
 *
 * xmlSecKey
 *
  *****************************************************************************/
/**
 * @brief Allocates and initializes a new key object.
 * @details Allocates and initializes new key. Caller is responsible for
 * freeing returned object with #xmlSecKeyDestroy function.
 *
 * @return the pointer to newly allocated xmlSecKey structure
 * or NULL if an error occurs.
 */
xmlSecKeyPtr
xmlSecKeyCreate(void)  {
    xmlSecKeyPtr key;

    /* Allocate a new xmlSecKey and fill the fields. */
    key = (xmlSecKeyPtr)xmlMalloc(sizeof(xmlSecKey));
    if(key == NULL) {
        xmlSecMallocError(sizeof(xmlSecKey), NULL);
        return(NULL);
    }
    memset(key, 0, sizeof(xmlSecKey));
    key->usage = xmlSecKeyUsageAny;
    return(key);
}

/**
 * @brief Clears the @p key data.
 * @param key the pointer to key.
 */
void
xmlSecKeyEmpty(xmlSecKeyPtr key) {
    xmlSecAssert(key != NULL);

    if(key->value != NULL) {
        xmlSecKeyDataDestroy(key->value);
    }
    if(key->name != NULL) {
        xmlFree(key->name);
    }
    if(key->dataList != NULL) {
        xmlSecPtrListDestroy(key->dataList);
    }

    memset(key, 0, sizeof(xmlSecKey));
}

/**
 * @brief Destroys a key object.
 * @details Destroys the key created using #xmlSecKeyCreate function.
 * @param key the pointer to key.
 */
void
xmlSecKeyDestroy(xmlSecKeyPtr key) {
    xmlSecAssert(key != NULL);

    xmlSecKeyEmpty(key);
    xmlFree(key);
}

/**
 * @brief Copies key data from @p keySrc to @p keyDst.
 * @param keyDst the destination key.
 * @param keySrc the source key.
 *
 * @return 0 on success or a negative value if an error occurs.
 */
int
xmlSecKeyCopy(xmlSecKeyPtr keyDst, xmlSecKeyPtr keySrc) {
    xmlSecAssert2(keyDst != NULL, -1);
    xmlSecAssert2(keySrc != NULL, -1);

    /* empty destination */
    xmlSecKeyEmpty(keyDst);

    /* copy everything */
    if(keySrc->name != NULL) {
        keyDst->name = xmlStrdup(keySrc->name);
        if(keyDst->name == NULL) {
            xmlSecStrdupError(keySrc->name, NULL);
            return(-1);
        }
    }

    if(keySrc->value != NULL) {
        keyDst->value = xmlSecKeyDataDuplicate(keySrc->value);
        if(keyDst->value == NULL) {
            xmlSecInternalError("xmlSecKeyDataDuplicate", NULL);
            return(-1);
        }
    }

    if(keySrc->dataList != NULL) {
        keyDst->dataList = xmlSecPtrListDuplicate(keySrc->dataList);
        if(keyDst->dataList == NULL) {
            xmlSecInternalError("xmlSecPtrListDuplicate", NULL);
            return(-1);
        }
    }

    keyDst->usage          = keySrc->usage;
    keyDst->notValidBefore = keySrc->notValidBefore;
    keyDst->notValidAfter  = keySrc->notValidAfter;
    return(0);
}

#define XMLSEC_SWAP(val1, val2, TYPE)   \
    {                                   \
        TYPE _tmp = (val1);             \
        (val1) = (val2);                \
        (val2) = _tmp;                  \
    }

/**
 * @brief Swaps key data for @p key1 and#key2
 * @param key1 the first key.
 * @param key2 the second key.
 *
 * @return 0 on success or a negative value if an error occurs.
 */
int
xmlSecKeySwap(xmlSecKeyPtr key1, xmlSecKeyPtr key2) {
    xmlSecAssert2(key1 != NULL, -1);
    xmlSecAssert2(key2 != NULL, -1);

    XMLSEC_SWAP(key1->name,             key2->name,             xmlChar*);
    XMLSEC_SWAP(key1->value,            key2->value,            xmlSecKeyDataPtr);
    XMLSEC_SWAP(key1->dataList,         key2->dataList,         xmlSecPtrListPtr);
    XMLSEC_SWAP(key1->usage,            key2->usage,            xmlSecKeyUsage);
    XMLSEC_SWAP(key1->notValidBefore,   key2->notValidBefore,   time_t);
    XMLSEC_SWAP(key1->notValidAfter,    key2->notValidAfter,    time_t);

    return(0);
}

/**
 * @brief Creates a duplicate of the given @p key.
 * @param key the pointer to the xmlSecKey structure.
 *
 * @return the pointer to newly allocated xmlSecKey structure
 * or NULL if an error occurs.
 */
xmlSecKeyPtr
xmlSecKeyDuplicate(xmlSecKeyPtr key) {
    xmlSecKeyPtr newKey;
    int ret;

    xmlSecAssert2(key != NULL, NULL);

    newKey = xmlSecKeyCreate();
    if(newKey == NULL) {
        xmlSecInternalError("xmlSecKeyCreate", NULL);
        return(NULL);
    }

    ret = xmlSecKeyCopy(newKey, key);
    if(ret < 0) {
        xmlSecInternalError("xmlSecKeyCopy", NULL);
        xmlSecKeyDestroy(newKey);
        return(NULL);
    }

    return(newKey);
}

/**
 * @brief Checks whether a key matches the given criteria.
 * @details Checks whether the @p key matches the given criteria.
 * @param key the pointer to key.
 * @param name the pointer to key name (may be NULL).
 * @param keyReq the pointer to key requirements.
 *
 * @return 1 if the key satisfies the given criteria or 0 otherwise.
 */
int
xmlSecKeyMatch(xmlSecKeyPtr key, const xmlChar *name, xmlSecKeyReqPtr keyReq) {
    xmlSecAssert2(xmlSecKeyIsValid(key), -1);
    xmlSecAssert2(keyReq != NULL, -1);

    if((name != NULL) && (!xmlStrEqual(xmlSecKeyGetName(key), name))) {
        return(0);
    }
    return(xmlSecKeyReqMatchKey(keyReq, key));
}

/**
 * @brief Gets @p key type.
 * @param key the pointer to key.
 *
 * @return key type.
 */
xmlSecKeyDataType
xmlSecKeyGetType(xmlSecKeyPtr key) {
    xmlSecKeyDataPtr data;

    xmlSecAssert2(key != NULL, xmlSecKeyDataTypeUnknown);

    data = xmlSecKeyGetValue(key);
    if(data == NULL) {
        return(xmlSecKeyDataTypeUnknown);
    }
    return(xmlSecKeyDataGetType(data));
}

/**
 * @brief Gets the key name.
 * @details Gets key name (see also #xmlSecKeySetName function).
 * @param key the pointer to key.
 *
 * @return key name.
 */
const xmlChar*
xmlSecKeyGetName(xmlSecKeyPtr key) {
    xmlSecAssert2(key != NULL, NULL);

    return(key->name);
}

/**
 * @brief Sets the key name.
 * @details Sets key name (see also #xmlSecKeyGetName function).
 * @param key the pointer to key.
 * @param name the new key name.
 *
 * @return 0 on success or a negative value if an error occurs.
 */
int
xmlSecKeySetName(xmlSecKeyPtr key, const xmlChar* name) {
    xmlSecAssert2(key != NULL, -1);

    if(key->name != NULL) {
        xmlFree(key->name);
        key->name = NULL;
    }

    if(name != NULL) {
        key->name = xmlStrdup(name);
        if(key->name == NULL) {
            xmlSecStrdupError(name, NULL);
            return(-1);
        }
    }

    return(0);
}

/**
 * @brief Sets the key name with a specific length.
 * @details Sets key name (see also #xmlSecKeyGetName function).
 * @param key the pointer to key.
 * @param name the new key name.
 * @param nameSize the size of @p name.
 *
 * @return 0 on success or a negative value if an error occurs.
 */
int
xmlSecKeySetNameEx(xmlSecKeyPtr key, const xmlChar* name, xmlSecSize nameSize) {
    xmlSecAssert2(key != NULL, -1);

    if(key->name != NULL) {
        xmlFree(key->name);
        key->name = NULL;
    }

    if((name != NULL) && (nameSize > 0)) {
        int nameLen;

        XMLSEC_SAFE_CAST_SIZE_TO_INT(nameSize, nameLen, return(-1), NULL);
        key->name = xmlStrndup(name, nameLen);
        if(key->name == NULL) {
            xmlSecStrdupError(name, NULL);
            return(-1);
        }
    }

    return(0);
}

/**
 * @brief Gets the key value (crypto material).
 * @details Gets key value (see also #xmlSecKeySetValue function).
 * @param key the pointer to key.
 *
 * @return key value (crypto material).
 */
xmlSecKeyDataPtr
xmlSecKeyGetValue(xmlSecKeyPtr key) {
    xmlSecAssert2(key != NULL, NULL);

    return(key->value);
}

/**
 * @brief Sets the key value (crypto material).
 * @details Sets key value (see also #xmlSecKeyGetValue function).
 * @param key the pointer to key.
 * @param value the new value.
 *
 * @return 0 on success or a negative value if an error occurs.
 */
int
xmlSecKeySetValue(xmlSecKeyPtr key, xmlSecKeyDataPtr value) {
    xmlSecAssert2(key != NULL, -1);

    if(key->value != NULL) {
        xmlSecKeyDataDestroy(key->value);
        key->value = NULL;
    }
    key->value = value;

    return(0);
}

/**
 * @brief Gets the key size in bits.
 * @details Gets key size (in bits). Also see #xmlSecKeyDataGetSize function.
 * @param key the pointer to key.
 *
 * @return key size (in bits).
 */
xmlSecSize
xmlSecKeyGetSize(xmlSecKeyPtr key) {
    xmlSecAssert2(key != NULL, 0);

    if(key->value == NULL) {
        return(0);
    }
    return(xmlSecKeyDataGetSize(key->value));
}

/**
 * @brief Gets key's data.
 * @param key the pointer to key.
 * @param dataId the requested data klass.
 *
 * @return additional data associated with the @p key (see also
 * #xmlSecKeyAdoptData function).
 */
xmlSecKeyDataPtr
xmlSecKeyGetData(xmlSecKeyPtr key, xmlSecKeyDataId dataId) {

    xmlSecAssert2(key != NULL, NULL);
    xmlSecAssert2(dataId != xmlSecKeyDataIdUnknown, NULL);

    /* special cases */
    if(dataId == xmlSecKeyDataValueId) {
        return(key->value);
    } else if(key->dataList != NULL) {
        xmlSecKeyDataPtr tmp;
        xmlSecSize pos, size;

        size = xmlSecPtrListGetSize(key->dataList);
        for(pos = 0; pos < size; ++pos) {
            tmp = (xmlSecKeyDataPtr)xmlSecPtrListGetItem(key->dataList, pos);
            if((tmp != NULL) && (tmp->id == dataId)) {
                return(tmp);
            }
        }
    }
    return(NULL);
}

/**
 * @brief Ensures key data of the requested klass exists.
 * @details If necessary, creates key data of @p dataId klass and adds to @p key.
 * @param key the pointer to key.
 * @param dataId the requested data klass.
 *
 * @return pointer to key data or NULL if an error occurs.
 */
xmlSecKeyDataPtr
xmlSecKeyEnsureData(xmlSecKeyPtr key, xmlSecKeyDataId dataId) {
    xmlSecKeyDataPtr data;
    int ret;

    xmlSecAssert2(key != NULL, NULL);
    xmlSecAssert2(dataId != xmlSecKeyDataIdUnknown, NULL);

    data = xmlSecKeyGetData(key, dataId);
    if(data != NULL) {
        return(data);
    }

    data = xmlSecKeyDataCreate(dataId);
    if(data == NULL) {
        xmlSecInternalError2("xmlSecKeyDataCreate", NULL,
                             "dataId=%s",
                             xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(dataId)));
        return(NULL);
    }

    ret = xmlSecKeyAdoptData(key, data);
    if(ret < 0) {
        xmlSecInternalError2("xmlSecKeyAdoptData", NULL,
                             "dataId=%s",
                             xmlSecErrorsSafeString(xmlSecKeyDataKlassGetName(dataId)));
        xmlSecKeyDataDestroy(data);
        return(NULL);
    }

    return(data);
}

/**
 * @brief Adds key data to a key object (takes ownership).
 * @details Adds @p data to the @p key. The @p data object will be destroyed
 * by @p key.
 * @param key the pointer to key.
 * @param data the pointer to key data.
 *
 * @return 0 on success or a negative value otherwise.
 */
int
xmlSecKeyAdoptData(xmlSecKeyPtr key, xmlSecKeyDataPtr data) {
    xmlSecKeyDataPtr tmp;
    xmlSecSize pos, size;

    xmlSecAssert2(key != NULL, -1);
    xmlSecAssert2(xmlSecKeyDataIsValid(data), -1);

    /* special cases */
    if(data->id == xmlSecKeyDataValueId) {
        if(key->value != NULL) {
            xmlSecKeyDataDestroy(key->value);
        }
        key->value = data;
        return(0);
    }

    if(key->dataList == NULL) {
        key->dataList = xmlSecPtrListCreate(xmlSecKeyDataListId);
        if(key->dataList == NULL) {
            xmlSecInternalError("xmlSecPtrListCreate", NULL);
            return(-1);
        }
    }


    size = xmlSecPtrListGetSize(key->dataList);
    for(pos = 0; pos < size; ++pos) {
        tmp = (xmlSecKeyDataPtr)xmlSecPtrListGetItem(key->dataList, pos);
        if((tmp != NULL) && (tmp->id == data->id)) {
            return(xmlSecPtrListSet(key->dataList, data, pos));
        }
    }

    return(xmlSecPtrListAdd(key->dataList, data));
}

/**
 * @brief Prints key information for debugging.
 * @details Prints the information about the @p key to the @p output.
 * @param key the pointer to key.
 * @param output the pointer to output FILE.
 */
void
xmlSecKeyDebugDump(xmlSecKeyPtr key, FILE *output) {
    xmlSecAssert(xmlSecKeyIsValid(key));
    xmlSecAssert(output != NULL);

    fprintf(output, "== KEY\n");
    fprintf(output, "=== method: %s\n",
            (key->value->id->dataNodeName != NULL) ?
            (char*)(key->value->id->dataNodeName) : "NULL");

    fprintf(output, "=== key type: ");
    if((xmlSecKeyGetType(key) & xmlSecKeyDataTypeSymmetric) != 0) {
        fprintf(output, "Symmetric\n");
    } else if((xmlSecKeyGetType(key) & xmlSecKeyDataTypePrivate) != 0) {
        fprintf(output, "Private\n");
    } else if((xmlSecKeyGetType(key) & xmlSecKeyDataTypePublic) != 0) {
        fprintf(output, "Public\n");
    } else {
        fprintf(output, "Unknown\n");
    }

    if(key->name != NULL) {
        fprintf(output, "=== key name: %s\n", key->name);
    }
    fprintf(output, "=== key usage: %u\n", key->usage);
    if(key->notValidBefore < key->notValidAfter) {
        fprintf(output, "=== key not valid before: %.lf\n",
            difftime(key->notValidBefore, (time_t)0));
        fprintf(output, "=== key not valid after: %.lf\n",
            difftime(key->notValidAfter, (time_t)0));
    }
    if(key->value != NULL) {
        xmlSecKeyDataDebugDump(key->value, output);
    }
    if(key->dataList != NULL) {
        xmlSecPtrListDebugDump(key->dataList, output);
    }
}

/**
 * @brief Prints key information in XML format for debugging.
 * @details Prints the information about the @p key to the @p output in XML format.
 * @param key the pointer to key.
 * @param output the pointer to output FILE.
 */
void
xmlSecKeyDebugXmlDump(xmlSecKeyPtr key, FILE *output) {
    xmlSecAssert(xmlSecKeyIsValid(key));
    xmlSecAssert(output != NULL);

    fprintf(output, "<KeyInfo>\n");

    fprintf(output, "<KeyMethod>");
    xmlSecPrintXmlString(output, key->value->id->dataNodeName);
    fprintf(output, "</KeyMethod>\n");

    fprintf(output, "<KeyType>");
    if((xmlSecKeyGetType(key) & xmlSecKeyDataTypeSymmetric) != 0) {
        fprintf(output, "Symmetric\n");
    } else if((xmlSecKeyGetType(key) & xmlSecKeyDataTypePrivate) != 0) {
        fprintf(output, "Private\n");
    } else if((xmlSecKeyGetType(key) & xmlSecKeyDataTypePublic) != 0) {
        fprintf(output, "Public\n");
    } else {
        fprintf(output, "Unknown\n");
    }
    fprintf(output, "</KeyType>\n");

    fprintf(output, "<KeyName>");
    xmlSecPrintXmlString(output, key->name);
    fprintf(output, "</KeyName>\n");

    if(key->notValidBefore < key->notValidAfter) {
        fprintf(output, "<KeyValidity notValidBefore=\"%.lf\" notValidAfter=\"%.lf\"/>\n",
            difftime(key->notValidBefore, (time_t)0),
            difftime(key->notValidAfter, (time_t)0));
    }

    if(key->value != NULL) {
        xmlSecKeyDataDebugXmlDump(key->value, output);
    }
    if(key->dataList != NULL) {
        xmlSecPtrListDebugXmlDump(key->dataList, output);
    }

    fprintf(output, "</KeyInfo>\n");
}

/**
 * @brief Generates a new key of the requested klass and type.
 * @details Generates new key of requested klass @p dataId and @p type.
 * @param dataId the requested key klass (rsa, dsa, aes, ...).
 * @param sizeBits the new key size (in bits!).
 * @param type the new key type (session, permanent, ...).
 *
 * @return pointer to newly created key or NULL if an error occurs.
 */
xmlSecKeyPtr
xmlSecKeyGenerate(xmlSecKeyDataId dataId, xmlSecSize sizeBits, xmlSecKeyDataType type) {
    xmlSecKeyPtr key;
    xmlSecKeyDataPtr data;
    int ret;

    xmlSecAssert2(dataId != xmlSecKeyDataIdUnknown, NULL);

    data = xmlSecKeyDataCreate(dataId);
    if(data == NULL) {
        xmlSecInternalError("xmlSecKeyDataCreate",
                            xmlSecKeyDataKlassGetName(dataId));
        return(NULL);
    }

    ret = xmlSecKeyDataGenerate(data, sizeBits, type);
    if(ret < 0) {
        xmlSecInternalError3("xmlSecKeyDataGenerate",
                             xmlSecKeyDataKlassGetName(dataId),
                             "size=" XMLSEC_SIZE_FMT ";type=%u", sizeBits, type);
        xmlSecKeyDataDestroy(data);
        return(NULL);
    }

    key = xmlSecKeyCreate();
    if(key == NULL) {
        xmlSecInternalError("xmlSecKeyCreate",
                            xmlSecKeyDataKlassGetName(dataId));
        xmlSecKeyDataDestroy(data);
        return(NULL);
    }

    ret = xmlSecKeySetValue(key, data);
    if(ret < 0) {
        xmlSecInternalError("xmlSecKeySetValue",
                            xmlSecKeyDataKlassGetName(dataId));
        xmlSecKeyDataDestroy(data);
        xmlSecKeyDestroy(key);
        return(NULL);
    }

    return(key);
}

/**
 * @brief Generates a new key by klass name and type.
 * @details Generates new key of requested @p klass and @p type.
 * @param name the requested key klass name (rsa, dsa, aes, ...).
 * @param sizeBits the new key size (in bits!).
 * @param type the new key type (session, permanent, ...).
 *
 * @return pointer to newly created key or NULL if an error occurs.
 */
xmlSecKeyPtr
xmlSecKeyGenerateByName(const xmlChar* name, xmlSecSize sizeBits, xmlSecKeyDataType type) {
    xmlSecKeyDataId dataId;

    xmlSecAssert2(name != NULL, NULL);

    dataId = xmlSecKeyDataIdListFindByName(xmlSecKeyDataIdsGet(), name, xmlSecKeyDataUsageAny);
    if(dataId == xmlSecKeyDataIdUnknown) {
        xmlSecOtherError(XMLSEC_ERRORS_R_KEY_DATA_NOT_FOUND, name, NULL);
        return(NULL);
    }

    return(xmlSecKeyGenerate(dataId, sizeBits, type));
}

/**
 * @brief Reads a key value from a buffer.
 * @details Reads the key value of klass @p dataId from a buffer.
 * @param dataId the key value data klass.
 * @param buffer the buffer that contains the binary data.
 *
 * @return pointer to newly created key or NULL if an error occurs.
 */
xmlSecKeyPtr
xmlSecKeyReadBuffer(xmlSecKeyDataId dataId, xmlSecBuffer* buffer) {
    xmlSecKeyInfoCtx keyInfoCtx;
    xmlSecKeyPtr key;
    int ret;

    xmlSecAssert2(dataId != xmlSecKeyDataIdUnknown, NULL);
    xmlSecAssert2(buffer != NULL, NULL);

    /* create key data */
    key = xmlSecKeyCreate();
    if(key == NULL) {
        xmlSecInternalError("xmlSecKeyCreate",
                            xmlSecKeyDataKlassGetName(dataId));
        return(NULL);
    }

    ret = xmlSecKeyInfoCtxInitialize(&keyInfoCtx, NULL);
    if(ret < 0) {
        xmlSecInternalError("xmlSecKeyInfoCtxInitialize",
                            xmlSecKeyDataKlassGetName(dataId));
        xmlSecKeyDestroy(key);
        return(NULL);
    }

    keyInfoCtx.keyReq.keyType = xmlSecKeyDataTypeAny;
    ret = xmlSecKeyDataBinRead(dataId, key,
                        xmlSecBufferGetData(buffer),
                        xmlSecBufferGetSize(buffer),
                        &keyInfoCtx);
    if(ret < 0) {
        xmlSecInternalError("xmlSecKeyDataBinRead",
                            xmlSecKeyDataKlassGetName(dataId));
        xmlSecKeyInfoCtxFinalize(&keyInfoCtx);
        xmlSecKeyDestroy(key);
        return(NULL);
    }
    xmlSecKeyInfoCtxFinalize(&keyInfoCtx);

    return(key);
}

/**
 * @brief Reads a key value from a binary file.
 * @details Reads the key value of klass @p dataId from a binary file @p filename.
 * @param dataId the key value data klass.
 * @param filename the key binary filename.
 *
 * @return pointer to newly created key or NULL if an error occurs.
 */
xmlSecKeyPtr
xmlSecKeyReadBinaryFile(xmlSecKeyDataId dataId, const char* filename) {
    xmlSecKeyPtr key;
    xmlSecBuffer buffer;
    int ret;

    xmlSecAssert2(dataId != xmlSecKeyDataIdUnknown, NULL);
    xmlSecAssert2(filename != NULL, NULL);

    /* read file to buffer */
    ret = xmlSecBufferInitialize(&buffer, 0);
    if(ret < 0) {
        xmlSecInternalError("xmlSecBufferInitialize",
                            xmlSecKeyDataKlassGetName(dataId));
        return(NULL);
    }

    ret = xmlSecBufferReadFile(&buffer, filename);
    if(ret < 0) {
        xmlSecInternalError2("xmlSecBufferReadFile",
                             xmlSecKeyDataKlassGetName(dataId),
                             "filename=%s",
                             xmlSecErrorsSafeString(filename));
        xmlSecBufferFinalize(&buffer);
        return(NULL);
    }

    key = xmlSecKeyReadBuffer(dataId, &buffer);
    if(key == NULL) {
        xmlSecInternalError2("xmlSecKeyReadBuffer",
                             xmlSecKeyDataKlassGetName(dataId),
                             "filename=%s",
                             xmlSecErrorsSafeString(filename));
        xmlSecBufferFinalize(&buffer);
        return(NULL);
    }

    xmlSecBufferFinalize(&buffer);
    return (key);
}

/**
 * @brief Reads a key value from a memory block.
 * @details Reads the key value of klass @p dataId from a memory block @p data.
 * @param dataId the key value data klass.
 * @param data the memory containing the key
 * @param dataSize the size of the memory block
 *
 * @return pointer to newly created key or NULL if an error occurs.
 */
xmlSecKeyPtr
xmlSecKeyReadMemory(xmlSecKeyDataId dataId, const xmlSecByte* data, xmlSecSize dataSize) {
    xmlSecBuffer buffer;
    xmlSecKeyPtr key;
    int ret;

    xmlSecAssert2(dataId != xmlSecKeyDataIdUnknown, NULL);
    xmlSecAssert2(data != NULL, NULL);
    xmlSecAssert2(dataSize > 0, NULL);

    /* read file to buffer */
    ret = xmlSecBufferInitialize(&buffer, 0);
    if(ret < 0) {
        xmlSecInternalError("xmlSecBufferInitialize",
                            xmlSecKeyDataKlassGetName(dataId));
        return(NULL);
    }

    if (xmlSecBufferAppend(&buffer, data, dataSize) < 0) {
        xmlSecInternalError("xmlSecBufferAppend",
                            xmlSecKeyDataKlassGetName(dataId));
        xmlSecBufferFinalize(&buffer);
        return(NULL);
    }

    key = xmlSecKeyReadBuffer(dataId, &buffer);
    if(key == NULL) {
        xmlSecInternalError("xmlSecKeyReadBuffer",
                            xmlSecKeyDataKlassGetName(dataId));
        xmlSecBufferFinalize(&buffer);
        return(NULL);
    }

    xmlSecBufferFinalize(&buffer);
    return (key);
}

/**
 * @brief Reads a KeyInfo node and extracts the key.
 * @details Reads the &lt;dsig:KeyInfo/&gt; node @p keyInfoNode and extracts the key.
 * @param keyInfoNode the pointer to &lt;dsig:KeyInfo/&gt; node.
 * @param keyInfoCtx the pointer to &lt;dsig:KeyInfo/&gt; node processing context.
 *
 * @return the pointer to key or NULL if the key is not found or
 * an error occurs.
 */
xmlSecKeyPtr
xmlSecKeysMngrGetKey(xmlNodePtr keyInfoNode, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    xmlSecKeyPtr key;
    int ret;

    xmlSecAssert2(keyInfoCtx != NULL, NULL);

    /* first try to read data from &lt;dsig:KeyInfo/&gt; node */
    key = xmlSecKeyCreate();
    if(key == NULL) {
        xmlSecInternalError("xmlSecKeyCreate", NULL);
        return(NULL);
    }

    if(keyInfoNode != NULL) {
        ret = xmlSecKeyInfoNodeRead(keyInfoNode, key, keyInfoCtx);
        if(ret < 0) {
            xmlSecInternalError2("xmlSecKeyInfoNodeRead",
                                 NULL,
                                 "node=%s",
                                 xmlSecErrorsSafeString(xmlSecNodeGetName(keyInfoNode)));
            xmlSecKeyDestroy(key);
            return(NULL);
        }

        if((xmlSecKeyGetValue(key) != NULL) &&
           (xmlSecKeyMatch(key, NULL, &(keyInfoCtx->keyReq)) != 0)) {
            return(key);
        }
    }
    xmlSecKeyDestroy(key);

    /* if we have keys manager, try to find any key that matches the required key (if lax key search is allowed) */
    if(((keyInfoCtx->flags & XMLSEC_KEYINFO_FLAGS_LAX_KEY_SEARCH) != 0) &&  (keyInfoCtx->keysMngr != NULL)) {
        key = xmlSecKeysMngrFindKey(keyInfoCtx->keysMngr, NULL, keyInfoCtx);
        if(key == NULL) {
            xmlSecInternalError("xmlSecKeysMngrFindKey", NULL);
            return(NULL);
        }
        if(xmlSecKeyGetValue(key) != NULL) {
            return(key);
        }
        xmlSecKeyDestroy(key);
    }

    xmlSecOtherError(XMLSEC_ERRORS_R_KEY_NOT_FOUND, NULL, NULL);
    return(NULL);
}

/******************************************************************************
 *
 * Keys list
 *
  *****************************************************************************/
static xmlSecPtrListKlass xmlSecKeyPtrListKlass = {
    BAD_CAST "keys-list",
    (xmlSecPtrDuplicateItemMethod)xmlSecKeyDuplicate,   /* xmlSecPtrDuplicateItemMethod duplicateItem; */
    (xmlSecPtrDestroyItemMethod)xmlSecKeyDestroy,       /* xmlSecPtrDestroyItemMethod destroyItem; */
    (xmlSecPtrDebugDumpItemMethod)xmlSecKeyDebugDump,   /* xmlSecPtrDebugDumpItemMethod debugDumpItem; */
    (xmlSecPtrDebugDumpItemMethod)xmlSecKeyDebugXmlDump,/* xmlSecPtrDebugDumpItemMethod debugXmlDumpItem; */
};

/**
 * @brief The keys list klass.
 *
 * @return keys list id.
 */
xmlSecPtrListId
xmlSecKeyPtrListGetKlass(void) {
    return(&xmlSecKeyPtrListKlass);
}
