/**
 * 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) 2003-2026 Aleksey Sanin <aleksey@aleksey.com>. All Rights Reserved.
 * Copyright (c) 2003 America Online, Inc.  All rights reserved.
 */
/**
 * @addtogroup xmlsec_nss_pkikeys
 * @brief Private/public keys implementation for NSS.
 */
#include "globals.h"

#include <string.h>

#include <pk11pub.h>
#include <keyhi.h>
#include <pk11pqg.h>

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

#include <xmlsec/nss/crypto.h>
#include <xmlsec/nss/pkikeys.h>

#include "../cast_helpers.h"
#include "../keysdata_helpers.h"


/******************************************************************************
 *
 * Internal NSS PKI key CTX
 *
  *****************************************************************************/
typedef struct _xmlSecNssPKIKeyDataCtx  xmlSecNssPKIKeyDataCtx,
                                                *xmlSecNssPKIKeyDataCtxPtr;
struct _xmlSecNssPKIKeyDataCtx {
    SECKEYPublicKey  *pubkey;
    SECKEYPrivateKey *privkey;
};

/******************************************************************************
 *
 * PKI key data (dsa/rsa/ec)
 *
  *****************************************************************************/
XMLSEC_KEY_DATA_DECLARE(NssPKIKeyData, xmlSecNssPKIKeyDataCtx)
#define xmlSecNssPKIKeyDataSize XMLSEC_KEY_DATA_SIZE(NssPKIKeyData)

static int              xmlSecNssPKIKeyDataInitialize   (xmlSecKeyDataPtr data);
static void             xmlSecNssPKIKeyDataFinalize     (xmlSecKeyDataPtr data);
static xmlSecKeyDataType xmlSecNssPKIKeyDataGetType     (xmlSecKeyDataPtr data);
static xmlSecSize        xmlSecNssPKIKeyDataGetSize     (xmlSecKeyDataPtr data);



static void             xmlSecNSSPKIKeyDataCtxFree      (xmlSecNssPKIKeyDataCtxPtr ctx);
static int              xmlSecNSSPKIKeyDataCtxDup       (xmlSecNssPKIKeyDataCtxPtr ctxDst,
                                                         xmlSecNssPKIKeyDataCtxPtr ctxSrc);
static int              xmlSecNssPKIKeyDataAdoptKey     (xmlSecKeyDataPtr data,
                                                         SECKEYPrivateKey *privkey,
                                                         SECKEYPublicKey  *pubkey);


static int
xmlSecNssPKIKeyDataInitialize(xmlSecKeyDataPtr data) {
    xmlSecNssPKIKeyDataCtxPtr ctx;

    xmlSecAssert2(xmlSecKeyDataIsValid(data), -1);
    xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecNssPKIKeyDataSize), -1);

    ctx = xmlSecNssPKIKeyDataGetCtx(data);
    xmlSecAssert2(ctx != NULL, -1);

    memset(ctx, 0, sizeof(xmlSecNssPKIKeyDataCtx));

    return(0);
}


static void
xmlSecNssPKIKeyDataFinalize(xmlSecKeyDataPtr data) {
    xmlSecNssPKIKeyDataCtxPtr ctx;

    xmlSecAssert(xmlSecKeyDataIsValid(data));
    xmlSecAssert(xmlSecKeyDataCheckSize(data, xmlSecNssPKIKeyDataSize));

    ctx = xmlSecNssPKIKeyDataGetCtx(data);
    xmlSecAssert(ctx != NULL);

    xmlSecNSSPKIKeyDataCtxFree(ctx);
    memset(ctx, 0, sizeof(xmlSecNssPKIKeyDataCtx));
}


static void
xmlSecNSSPKIKeyDataCtxFree(xmlSecNssPKIKeyDataCtxPtr ctx)
{
    xmlSecAssert(ctx != NULL);
    if (ctx->privkey != NULL) {
        SECKEY_DestroyPrivateKey(ctx->privkey);
        ctx->privkey = NULL;
    }

    if (ctx->pubkey)
    {
        SECKEY_DestroyPublicKey(ctx->pubkey);
        ctx->pubkey = NULL;
    }

}

static int
xmlSecNSSPKIKeyDataCtxDup(xmlSecNssPKIKeyDataCtxPtr ctxDst,
                          xmlSecNssPKIKeyDataCtxPtr ctxSrc)
{
    xmlSecNSSPKIKeyDataCtxFree(ctxDst);
    if (ctxSrc->privkey != NULL) {
        ctxDst->privkey = SECKEY_CopyPrivateKey(ctxSrc->privkey);
        if(ctxDst->privkey == NULL) {
            xmlSecNssError("SECKEY_CopyPrivateKey", NULL);
            return(-1);
        }
    }

    if (ctxSrc->pubkey != NULL) {
        ctxDst->pubkey = SECKEY_CopyPublicKey(ctxSrc->pubkey);
        if(ctxDst->pubkey == NULL) {
            xmlSecNssError("SECKEY_CopyPublicKey", NULL);
            return(-1);
        }
    }
    return (0);
}

static int
xmlSecNssPKIKeyDataAdoptKey(xmlSecKeyDataPtr data,
                            SECKEYPrivateKey *privkey,
                            SECKEYPublicKey  *pubkey)
{
    xmlSecNssPKIKeyDataCtxPtr ctx;
    SECKEYPublicKey *pubkey2 = NULL;
    KeyType pubType = nullKey;
    KeyType priType = nullKey;

    xmlSecAssert2(xmlSecKeyDataIsValid(data), -1);
    xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecNssPKIKeyDataSize), -1);

    ctx = xmlSecNssPKIKeyDataGetCtx(data);
    xmlSecAssert2(ctx != NULL, -1);

    /* get public key if needed from private */
    if ((pubkey == NULL) && (privkey != NULL)) {
        pubkey2 = SECKEY_ConvertToPublicKey(privkey);
        if(pubkey2 == NULL) {
            xmlSecNssError("SECKEY_ConvertToPublicKey", NULL);
            return(-1);
        }
    }

    /* ensure key types match */
    if (privkey != NULL) {
        priType = SECKEY_GetPrivateKeyType(privkey);
    }

    if (pubkey != NULL) {
        pubType = SECKEY_GetPublicKeyType(pubkey);
    } else if (pubkey2 != NULL) {
        pubType = SECKEY_GetPublicKeyType(pubkey2);
    }
    if ((priType != nullKey) && (pubType != priType)) {
        xmlSecNssError3("SECKEY_GetPrivateKeyType/SECKEY_GetPublicKeyType", NULL, "pubType=%u; priType=%u", pubType, priType);
        if (pubkey2 != NULL) {
            SECKEY_DestroyPublicKey(pubkey2);
        }
        return(-1);
    }

    /* destroy old keys (if needed) and set new ones */
    if (ctx->privkey != NULL) {
        SECKEY_DestroyPrivateKey(ctx->privkey);
    }
    ctx->privkey = privkey;

    if (ctx->pubkey != NULL) {
        SECKEY_DestroyPublicKey(ctx->pubkey);
    }
    ctx->pubkey = (pubkey != NULL) ? pubkey : pubkey2;

    /* done */
    return(0);
}

/**
 * @brief Build a KeyData object from the given Private Key and Public
 * @param privkey the NSS Private Key handle
 * @param pubkey the NSS Public Key handle
 *
 * Key handles.
 *
 * @return pointer to KeyData object or NULL if an error occurs.
 */
xmlSecKeyDataPtr
xmlSecNssPKIAdoptKey(SECKEYPrivateKey *privkey,
                     SECKEYPublicKey  *pubkey)
{
    xmlSecKeyDataPtr data = NULL;
    int ret;
    KeyType pubType = nullKey;
    KeyType priType = nullKey;

    if(privkey != NULL) {
        priType = SECKEY_GetPrivateKeyType(privkey);
    }

    if(pubkey != NULL) {
        pubType = SECKEY_GetPublicKeyType(pubkey);
    }

    if(priType != nullKey && pubType != nullKey) {
        if(pubType != priType) {
            xmlSecNssError3("SECKEY_GetPrivateKeyType/SECKEY_GetPublicKeyType", NULL,
                "pubType=%u; priType=%u", pubType, priType);
            return(NULL);
        }
    }

    pubType = (priType != nullKey) ? priType : pubType;
    switch(pubType) {
#ifndef XMLSEC_NO_RSA
    case rsaKey:
        data = xmlSecKeyDataCreate(xmlSecNssKeyDataRsaId);
        if(data == NULL) {
            xmlSecInternalError("xmlSecKeyDataCreate(KeyDataRsaId)", NULL);
            return(NULL);
        }
        break;
#endif /* XMLSEC_NO_RSA */
#ifndef XMLSEC_NO_DSA
    case dsaKey:
        data = xmlSecKeyDataCreate(xmlSecNssKeyDataDsaId);
        if(data == NULL) {
            xmlSecInternalError("xmlSecKeyDataCreate", NULL);
            return(NULL);
        }
        break;
#endif /* XMLSEC_NO_DSA */
#ifndef XMLSEC_NO_EC
    case ecKey:
        data = xmlSecKeyDataCreate(xmlSecNssKeyDataEcId);
        if(data == NULL) {
            xmlSecInternalError("xmlSecKeyDataCreate", NULL);
            return(NULL);
        }
        break;
#endif /* XMLSEC_NO_EC */
#ifndef XMLSEC_NO_EDDSA
    case edKey:
        data = xmlSecKeyDataCreate(xmlSecNssKeyDataEdDSAId);
        if(data == NULL) {
            xmlSecInternalError("xmlSecKeyDataCreate", NULL);
            return(NULL);
        }
        break;
#endif /* XMLSEC_NO_EDDSA */
#ifndef XMLSEC_NO_XDH
    case ecMontKey:
        data = xmlSecKeyDataCreate(xmlSecNssKeyDataXdhId);
        if(data == NULL) {
            xmlSecInternalError("xmlSecKeyDataCreate", NULL);
            return(NULL);
        }
        break;
#endif /* XMLSEC_NO_XDH */
    default:
        xmlSecUnsupportedEnumValueError("pubType", pubType, NULL);
        return(NULL);
    }

    xmlSecAssert2(data != NULL, NULL);
    ret = xmlSecNssPKIKeyDataAdoptKey(data, privkey, pubkey);
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssPKIKeyDataAdoptKey", NULL);
        xmlSecKeyDataDestroy(data);
        return(NULL);
    }
    return(data);
}

/**
 * @brief Gets the Public Key from the key data.
 * @param data the pointer to NSS Key data.
 *
 *
 * @return pointer to SECKEYPublicKey or NULL if an error occurs.
 * Caller is responsible for freeing the key when done
 */
SECKEYPublicKey *
xmlSecNssPKIKeyDataGetPubKey(xmlSecKeyDataPtr data) {
    xmlSecNssPKIKeyDataCtxPtr ctx;
    SECKEYPublicKey *ret;

    xmlSecAssert2(xmlSecKeyDataIsValid(data), NULL);
    xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecNssPKIKeyDataSize), NULL);

    ctx = xmlSecNssPKIKeyDataGetCtx(data);
    xmlSecAssert2(ctx != NULL, NULL);
    xmlSecAssert2(ctx->pubkey != NULL, NULL);

    ret = SECKEY_CopyPublicKey(ctx->pubkey);
    return(ret);
}

/**
 * @brief Gets the Private Key from the key data.
 * @param data the pointer to NSS Key data.
 *
 *
 * @return pointer to SECKEYPrivateKey or NULL if an error occurs.
 * Caller is responsible for freeing the key when done
 */
SECKEYPrivateKey*
xmlSecNssPKIKeyDataGetPrivKey(xmlSecKeyDataPtr data) {
    xmlSecNssPKIKeyDataCtxPtr ctx;
    SECKEYPrivateKey* ret;

    xmlSecAssert2(xmlSecKeyDataIsValid(data), NULL);
    xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecNssPKIKeyDataSize), NULL);

    ctx = xmlSecNssPKIKeyDataGetCtx(data);
    xmlSecAssert2(ctx != NULL, NULL);
    xmlSecAssert2(ctx->privkey != NULL, NULL);

    ret = SECKEY_CopyPrivateKey(ctx->privkey);
    return(ret);
}

/**
 * @brief Gets the Key Type from the key data.
 * @param data the pointer to NSS Key data.
 * @return Key Type
 */
KeyType
xmlSecNssPKIKeyDataGetKeyType(xmlSecKeyDataPtr data) {
    xmlSecNssPKIKeyDataCtxPtr ctx;
    KeyType kt = nullKey;

    xmlSecAssert2(xmlSecKeyDataIsValid(data), nullKey);
    xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecNssPKIKeyDataSize), nullKey);

    ctx = xmlSecNssPKIKeyDataGetCtx(data);
    xmlSecAssert2(ctx != NULL, nullKey);

    if (ctx->pubkey != NULL) {
        kt = SECKEY_GetPublicKeyType(ctx->pubkey);
    } else if(ctx->privkey != NULL) {
        kt = SECKEY_GetPrivateKeyType(ctx->privkey);
    }
    return(kt);
}

/**
 * xmlSecNssPKIKeyDataDuplicate
 * @param dst the pointer to NSS Key data to copy to.
 * @param src the pointer to NSS Key data to copy from.
 *
 * @brief Duplicates the keydata from src to dst
 *
 * @return -1 on error, 0 on success
 */
int
xmlSecNssPKIKeyDataDuplicate(xmlSecKeyDataPtr dst, xmlSecKeyDataPtr src) {
    xmlSecNssPKIKeyDataCtxPtr ctxDst;
    xmlSecNssPKIKeyDataCtxPtr ctxSrc;

    xmlSecAssert2(xmlSecKeyDataIsValid(dst), -1);
    xmlSecAssert2(xmlSecKeyDataCheckSize(dst, xmlSecNssPKIKeyDataSize), -1);
    xmlSecAssert2(xmlSecKeyDataIsValid(src), -1);
    xmlSecAssert2(xmlSecKeyDataCheckSize(src, xmlSecNssPKIKeyDataSize), -1);

    ctxDst = xmlSecNssPKIKeyDataGetCtx(dst);
    xmlSecAssert2(ctxDst != NULL, -1);

    ctxSrc = xmlSecNssPKIKeyDataGetCtx(src);
    xmlSecAssert2(ctxSrc != NULL, -1);

    if (xmlSecNSSPKIKeyDataCtxDup(ctxDst, ctxSrc) != 0) {
        xmlSecInternalError("xmlSecNssPKIKeydataCtxDup",
                            xmlSecKeyDataGetName(dst));
        return(-1);
    }

    return(0);
}

static xmlSecKeyDataType
xmlSecNssPKIKeyDataGetType(xmlSecKeyDataPtr data) {
    xmlSecNssPKIKeyDataCtxPtr ctx;

    xmlSecAssert2(xmlSecKeyDataIsValid(data), xmlSecKeyDataTypeUnknown);
    xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecNssPKIKeyDataSize), xmlSecKeyDataTypeUnknown);

    ctx = xmlSecNssPKIKeyDataGetCtx(data);
    xmlSecAssert2(ctx != NULL, xmlSecKeyDataTypeUnknown);

    if(ctx->pubkey == NULL) {
        return(xmlSecKeyDataTypeUnknown);
    }
    return ((ctx->privkey != NULL) ? (xmlSecKeyDataTypePrivate | xmlSecKeyDataTypePublic) : xmlSecKeyDataTypePublic);
}

static xmlSecSize
xmlSecNssPKIKeyDataGetSize(xmlSecKeyDataPtr data) {
    xmlSecNssPKIKeyDataCtxPtr ctx;

    xmlSecAssert2(xmlSecKeyDataIsValid(data), 0);
    xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecNssPKIKeyDataSize), 0);

    ctx = xmlSecNssPKIKeyDataGetCtx(data);
    xmlSecAssert2(ctx != NULL, 0);
    xmlSecAssert2(ctx->pubkey != NULL, 0);

    switch(SECKEY_GetPublicKeyType(ctx->pubkey)) {
    case dsaKey:
    case rsaKey:
        return(8 * SECKEY_PublicKeyStrength(ctx->pubkey));
    case ecKey:
        return(SECKEY_SignatureLen(ctx->pubkey));
#ifndef XMLSEC_NO_EDDSA
    case edKey:
        return(SECKEY_SignatureLen(ctx->pubkey));
#endif /* XMLSEC_NO_EDDSA */
#ifndef XMLSEC_NO_XDH
    case ecMontKey:
        return(8 * ctx->pubkey->u.ec.publicValue.len);
#endif /* XMLSEC_NO_XDH */
    default:
        break;
    }
    return(0);
}

/******************************************************************************
 *
 * Helpers
 *
  *****************************************************************************/
static int
xmlSecNssGetBigNumValue(xmlSecBufferPtr buf, PRArenaPool *arena, SECItem *val) {
    xmlSecByte* data;
    xmlSecSize size;

    xmlSecAssert2(buf != NULL, -1);
    xmlSecAssert2(arena != NULL, -1);
    xmlSecAssert2(val != NULL, -1);
    xmlSecAssert2(val->data == NULL, -1);
    xmlSecAssert2(val->len == 0, -1);

    data = xmlSecBufferGetData(buf);
    size = xmlSecBufferGetSize(buf);
    xmlSecAssert2(data != NULL, -1);
    xmlSecAssert2(size > 0, -1);

    XMLSEC_SAFE_CAST_SIZE_TO_UINT(size, val->len, return(-1), NULL);
    val->data = PORT_ArenaZAlloc(arena, val->len);
    if(val->data == NULL) {
        xmlSecMallocError(size, NULL);
        val->len = 0;
        return(-1);
    }
    PORT_Memcpy(val->data, data, val->len);
    return(0);
}

static int
xmlSecNssSetBigNumValue(const SECItem *val, xmlSecBufferPtr buf) {
    int ret;

    xmlSecAssert2(val != NULL, -1);
    xmlSecAssert2(val->data != NULL, -1);
    xmlSecAssert2(val->len > 0, -1);
    xmlSecAssert2(buf != NULL, -1);

    ret = xmlSecBufferSetData(buf, val->data, val->len);
    if(ret < 0) {
        xmlSecInternalError2("xmlSecBufferSetData", NULL,
            "size=%u", val->len);
        return(-1);
    }
    return(0);
}

/******************************************************************************
 *
 * Helper macro to declare PKI key data klass (dsa/rsa/ec)
 *
  *****************************************************************************/
#define XMLSEC_NSS_PKI_KEY_KLASS_EX(lcname, ucname, ns, generate, xmlRead, xmlWrite)           \
static xmlSecKeyDataKlass xmlSecNssKeyData ## lcname ## Klass = {                               \
    sizeof(xmlSecKeyDataKlass),                 /* xmlSecSize klassSize */                       \
    xmlSecNssPKIKeyDataSize,                    /* xmlSecSize objSize */                         \
    /* data */                                                                                   \
    xmlSecName ## ucname ## KeyValue,           /* const xmlChar* name; */                       \
    xmlSecKeyDataUsageReadFromFile | xmlSecKeyDataUsageKeyValueNode | xmlSecKeyDataUsageRetrievalMethodNodeXml, \
                                                /* xmlSecKeyDataUsage usage; */                  \
    xmlSecHref ## ucname ## KeyValue,           /* const xmlChar* href; */                       \
    xmlSecNode ## ucname ## KeyValue,           /* const xmlChar* dataNodeName; */               \
    ns,                                         /* const xmlChar* dataNodeNs; */                 \
    /* constructors/destructor */                                                                \
    xmlSecNssPKIKeyDataInitialize,              /* xmlSecKeyDataInitializeMethod initialize; */  \
    xmlSecNssPKIKeyDataDuplicate,               /* xmlSecKeyDataDuplicateMethod duplicate; */    \
    xmlSecNssPKIKeyDataFinalize,                /* xmlSecKeyDataFinalizeMethod finalize; */      \
    generate,                                   /* xmlSecKeyDataGenerateMethod generate; */      \
    /* get info */                                                                               \
    xmlSecNssPKIKeyDataGetType,                 /* xmlSecKeyDataGetTypeMethod getType; */        \
    xmlSecNssPKIKeyDataGetSize,                 /* xmlSecKeyDataGetSizeMethod getSize; */        \
    NULL,                                       /* DEPRECATED xmlSecKeyDataGetIdentifier getIdentifier; */ \
    /* read/write */                                                                             \
    xmlRead,                                    /* xmlSecKeyDataXmlReadMethod xmlRead; */        \
    xmlWrite,                                   /* xmlSecKeyDataXmlWriteMethod xmlWrite; */      \
    NULL,                                       /* xmlSecKeyDataBinReadMethod binRead; */        \
    NULL,                                       /* xmlSecKeyDataBinWriteMethod binWrite; */      \
    /* debug */                                                                                  \
    xmlSecKeyDataDebugDumpImpl,                 /* xmlSecKeyDataDebugDumpMethod debugDump; */    \
    xmlSecKeyDataDebugXmlDumpImpl,              /* xmlSecKeyDataDebugDumpMethod debugXmlDump; */ \
    /* reserved for the future */                                                                \
    NULL,                                       /* void* reserved0; */                          \
    NULL,                                       /* void* reserved1; */                          \
};


#ifndef XMLSEC_NO_DSA
/******************************************************************************
 *
 * &lt;dsig:DSAKeyValue/&gt; processing
 *
 *
 * The DSAKeyValue Element (http://www.w3.org/TR/xmldsig-core/#sec-DSAKeyValue)
 *
 * DSA keys and the DSA signature algorithm are specified in [DSS].
 * DSA public key values can have the following fields:
 *
 *   * P - a prime modulus meeting the [DSS] requirements
 *   * Q - an integer in the range 2**159 < Q < 2**160 which is a prime
 *         divisor of P-1
 *   * G - an integer with certain properties with respect to P and Q
 *   * Y - G**X mod P (where X is part of the private key and not made
 *         public)
 *   * J - (P - 1) / Q
 *   * seed - a DSA prime generation seed
 *   * pgenCounter - a DSA prime generation counter
 *
 * Parameter J is available for inclusion solely for efficiency as it is
 * calculatable from P and Q. Parameters seed and pgenCounter are used in the
 * DSA prime number generation algorithm specified in [DSS]. As such, they are
 * optional but must either both be present or both be absent. This prime
 * generation algorithm is designed to provide assurance that a weak prime is
 * not being used and it yields a P and Q value. Parameters P, Q, and G can be
 * public and common to a group of users. They might be known from application
 * context. As such, they are optional but P and Q must either both appear or
 * both be absent. If all of P, Q, seed, and pgenCounter are present,
 * implementations are not required to check if they are consistent and are
 * free to use either P and Q or seed and pgenCounter. All parameters are
 * encoded as base64 [MIME] values.
 *
 * Arbitrary-length integers (e.g. "bignums" such as RSA moduli) are
 * represented in XML as octet strings as defined by the ds:CryptoBinary type.
 *
 * Schema Definition:
 *
 * <element name="DSAKeyValue" type="ds:DSAKeyValueType"/>
 * <complexType name="DSAKeyValueType">
 *   <sequence>
 *     <sequence minOccurs="0">
 *        <element name="P" type="ds:CryptoBinary"/>
 *        <element name="Q" type="ds:CryptoBinary"/>
 *     </sequence>
 *     <element name="G" type="ds:CryptoBinary" minOccurs="0"/>
 *     <element name="Y" type="ds:CryptoBinary"/>
 *     <element name="J" type="ds:CryptoBinary" minOccurs="0"/>
 *     <sequence minOccurs="0">
 *       <element name="Seed" type="ds:CryptoBinary"/>
 *       <element name="PgenCounter" type="ds:CryptoBinary"/>
 *     </sequence>
 *   </sequence>
 * </complexType>
 *
 * DTD Definition:
 *
 *  <!ELEMENT DSAKeyValue ((P, Q)?, G?, Y, J?, (Seed, PgenCounter)?) >
 *  <!ELEMENT P (#PCDATA) >
 *  <!ELEMENT Q (#PCDATA) >
 *  <!ELEMENT G (#PCDATA) >
 *  <!ELEMENT Y (#PCDATA) >
 *  <!ELEMENT J (#PCDATA) >
 *  <!ELEMENT Seed (#PCDATA) >
 *  <!ELEMENT PgenCounter (#PCDATA) >
 *
 * ============================================================================
 *
 * To support reading/writing private keys an X element added (before Y).
 *
 * The current implementation does not support Seed and PgenCounter!
 * by this the P, Q and G are *required*!
 *
  *****************************************************************************/
static int              xmlSecNssKeyDataDsaXmlRead      (xmlSecKeyDataId id,
                                                         xmlSecKeyPtr key,
                                                         xmlNodePtr node,
                                                         xmlSecKeyInfoCtxPtr keyInfoCtx);
static int              xmlSecNssKeyDataDsaXmlWrite     (xmlSecKeyDataId id,
                                                         xmlSecKeyPtr key,
                                                         xmlNodePtr node,
                                                         xmlSecKeyInfoCtxPtr keyInfoCtx);
static int              xmlSecNssKeyDataDsaGenerate     (xmlSecKeyDataPtr data,
                                                         xmlSecSize sizeBits,
                                                         xmlSecKeyDataType type);


static xmlSecKeyDataPtr xmlSecNssKeyDataDsaRead         (xmlSecKeyDataId id,
                                                         xmlSecKeyValueDsaPtr dsaValue);
static int              xmlSecNssKeyDataDsaWrite        (xmlSecKeyDataId id,
                                                         xmlSecKeyDataPtr data,
                                                         xmlSecKeyValueDsaPtr dsaValue,
                                                         int writePrivateKey);

XMLSEC_NSS_PKI_KEY_KLASS_EX(Dsa, DSA, xmlSecDSigNs,
    xmlSecNssKeyDataDsaGenerate,
    xmlSecNssKeyDataDsaXmlRead,
    xmlSecNssKeyDataDsaXmlWrite)

/**
 * @brief The DSA key data klass.
 * @return pointer to DSA key data klass.
 */
xmlSecKeyDataId
xmlSecNssKeyDataDsaGetKlass(void) {
    return(&xmlSecNssKeyDataDsaKlass);
}


static int
xmlSecNssKeyDataDsaXmlRead(xmlSecKeyDataId id, xmlSecKeyPtr key,
                           xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    xmlSecAssert2(id == xmlSecNssKeyDataDsaId, -1);
    return(xmlSecKeyDataDsaXmlRead(id, key, node, keyInfoCtx,
        xmlSecNssKeyDataDsaRead));
}

static int
xmlSecNssKeyDataDsaXmlWrite(xmlSecKeyDataId id, xmlSecKeyPtr key,
                                xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    xmlSecAssert2(id == xmlSecNssKeyDataDsaId, -1);
    return(xmlSecKeyDataDsaXmlWrite(id, key, node, keyInfoCtx,
        xmlSecBase64GetDefaultLineSize(), 1, /* add line breaks */
        xmlSecNssKeyDataDsaWrite));
}

static int
xmlSecNssKeyDataDsaGenerate(xmlSecKeyDataPtr data, xmlSecSize sizeBits, xmlSecKeyDataType type XMLSEC_ATTRIBUTE_UNUSED) {
    PQGParams    *pqgParams = NULL;
    PQGVerify    *pqgVerify = NULL;
    SECStatus     rv;
    SECStatus     res;
    PK11SlotInfo *slot = NULL;
    SECKEYPrivateKey *privkey = NULL;
    SECKEYPublicKey  *pubkey = NULL;
    int               ret = -1;
    int               index;
    unsigned int      uIndex;

    xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecNssKeyDataDsaId), -1);
    xmlSecAssert2(sizeBits > 0, -1);

    index = PQG_PBITS_TO_INDEX(sizeBits);
    if(index < 0) {
        xmlSecNssError2("PQG_PBITS_TO_INDEX", xmlSecKeyDataGetName(data),
            "size=" XMLSEC_SIZE_FMT, sizeBits);
        goto done;
    }
    XMLSEC_SAFE_CAST_INT_TO_UINT(index, uIndex, goto done, xmlSecKeyDataGetName(data));

    rv = PK11_PQG_ParamGen(uIndex, &pqgParams, &pqgVerify);
    if (rv != SECSuccess) {
        xmlSecNssError2("PK11_PQG_ParamGen", xmlSecKeyDataGetName(data),
            "size=" XMLSEC_SIZE_FMT, sizeBits);
        goto done;
    }

    rv = PK11_PQG_VerifyParams(pqgParams, pqgVerify, &res);
    if (rv != SECSuccess || res != SECSuccess) {
        xmlSecNssError2("PK11_PQG_VerifyParams", xmlSecKeyDataGetName(data),
            "size=" XMLSEC_SIZE_FMT, sizeBits);
        goto done;
    }

    slot = PK11_GetBestSlot(CKM_DSA_KEY_PAIR_GEN, NULL);
    if(slot == NULL) {
        xmlSecNssError("PK11_GetBestSlot", xmlSecKeyDataGetName(data));
        goto done;
    }

    rv = PK11_Authenticate(slot, PR_TRUE, NULL /* default pwd callback */);
    if (rv != SECSuccess) {
        xmlSecNssError2("PK11_Authenticate", xmlSecKeyDataGetName(data),
                        "token=%s", xmlSecErrorsSafeString(PK11_GetTokenName(slot)));
        goto done;
    }

    privkey = PK11_GenerateKeyPair(slot, CKM_DSA_KEY_PAIR_GEN, pqgParams,
                                   &pubkey, PR_FALSE, PR_TRUE, NULL);

    if((privkey == NULL) || (pubkey == NULL)) {
        xmlSecNssError("PK11_GenerateKeyPair", xmlSecKeyDataGetName(data));
        goto done;
    }

    ret = xmlSecNssPKIKeyDataAdoptKey(data, privkey, pubkey);
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssPKIKeyDataAdoptKey",
                            xmlSecKeyDataGetName(data));
        goto done;
    }

    ret = 0;

done:
    if (slot != NULL) {
        PK11_FreeSlot(slot);
    }
    if (pqgParams != NULL) {
        PK11_PQG_DestroyParams(pqgParams);
    }
    if (pqgVerify != NULL) {
        PK11_PQG_DestroyVerify(pqgVerify);
    }
    if (ret == 0) {
        return (0);
    }
    if (pubkey != NULL) {
        SECKEY_DestroyPublicKey(pubkey);
    }
    if (privkey != NULL) {
        SECKEY_DestroyPrivateKey(privkey);
    }
    return(-1);
}

static xmlSecKeyDataPtr
xmlSecNssKeyDataDsaRead(xmlSecKeyDataId id, xmlSecKeyValueDsaPtr dsaValue) {
    xmlSecKeyDataPtr data = NULL;
    xmlSecKeyDataPtr res = NULL;
    PK11SlotInfo *slot = NULL;
    CK_OBJECT_HANDLE handle;
    SECKEYPublicKey *pubkey=NULL;
    PRArenaPool *arena = NULL;
    int ret;

    xmlSecAssert2(id == xmlSecNssKeyDataDsaId, NULL);
    xmlSecAssert2(dsaValue != NULL, NULL);

    slot = PK11_GetBestSlot(CKM_DSA, NULL);
    if(slot == NULL) {
        xmlSecNssError("PK11_GetBestSlot", xmlSecKeyDataKlassGetName(id));
        goto done;
    }

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if(arena == NULL) {
        xmlSecNssError("PORT_NewArena", xmlSecKeyDataKlassGetName(id));
        goto done;
    }

    pubkey = (SECKEYPublicKey *)PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey));
    if(pubkey == NULL) {
        xmlSecNssError2("PORT_ArenaZAlloc", xmlSecKeyDataKlassGetName(id),
            "size=" XMLSEC_SIZE_T_FMT, sizeof(SECKEYPublicKey));
        goto done;
    }
    pubkey->arena = arena;
    pubkey->u.dsa.params.arena = arena;
    pubkey->keyType = dsaKey;
    arena = NULL; /* owned by pubkey */

    /* p */
    ret = xmlSecNssGetBigNumValue(&(dsaValue->p), pubkey->arena, &(pubkey->u.dsa.params.prime));
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssGetBigNumValue(p)",
                            xmlSecKeyDataKlassGetName(id));
        goto done;
    }

    /* q */
    ret = xmlSecNssGetBigNumValue(&(dsaValue->q), pubkey->arena, &(pubkey->u.dsa.params.subPrime));
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssGetBigNumValue(q)",
                            xmlSecKeyDataKlassGetName(id));
        goto done;
    }

    /* g */
    ret = xmlSecNssGetBigNumValue(&(dsaValue->g), pubkey->arena, &(pubkey->u.dsa.params.base));
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssGetBigNumValue(g)",
                            xmlSecKeyDataKlassGetName(id));
        goto done;
    }

    /* next is X (priv key). NSS does not support it, we just ignore it */

    /* y */
    ret = xmlSecNssGetBigNumValue(&(dsaValue->y), pubkey->arena, &(pubkey->u.dsa.publicValue));
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssGetBigNumValue(y)",
                            xmlSecKeyDataKlassGetName(id));
        goto done;
    }

    /* create key */
    handle = PK11_ImportPublicKey(slot, pubkey, PR_FALSE);
    if(handle == CK_INVALID_HANDLE) {
        xmlSecNssError("PK11_ImportPublicKey",
                       xmlSecKeyDataKlassGetName(id));
        goto done;
    }

    data = xmlSecKeyDataCreate(id);
    if(data == NULL) {
        xmlSecInternalError("xmlSecKeyDataCreate",
                            xmlSecKeyDataKlassGetName(id));
        goto done;
    }

    ret = xmlSecNssPKIKeyDataAdoptKey(data, NULL, pubkey);
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssPKIKeyDataAdoptKey",
                            xmlSecKeyDataGetName(data));
        goto done;
    }
    pubkey = NULL; /* owned by data now */

    /* success */
    res = data;
    data = NULL;

done:
    if (slot != NULL) {
        PK11_FreeSlot(slot);
    }
    if (arena != NULL) {
        PORT_FreeArena(arena, PR_FALSE);
    }
    if (pubkey != NULL) {
        SECKEY_DestroyPublicKey(pubkey);
    }
    if (data != NULL) {
        xmlSecKeyDataDestroy(data);
    }
    return(res);
}

static int
xmlSecNssKeyDataDsaWrite(xmlSecKeyDataId id, xmlSecKeyDataPtr data,
                         xmlSecKeyValueDsaPtr dsaValue,
                         int writePrivateKey XMLSEC_ATTRIBUTE_UNUSED) {
    xmlSecNssPKIKeyDataCtxPtr ctx;
    int ret;

    xmlSecAssert2(id == xmlSecNssKeyDataDsaId, -1);
    xmlSecAssert2(data != NULL, -1);
    xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecNssKeyDataDsaId), -1);
    xmlSecAssert2(dsaValue != NULL, -1);
    UNREFERENCED_PARAMETER(writePrivateKey);

    ctx = xmlSecNssPKIKeyDataGetCtx(data);
    xmlSecAssert2(ctx != NULL, -1);
    xmlSecAssert2(ctx->pubkey != NULL, -1);
    xmlSecAssert2(SECKEY_GetPublicKeyType(ctx->pubkey) == dsaKey, -1);

    /* p */
    ret = xmlSecNssSetBigNumValue(&(ctx->pubkey->u.dsa.params.prime), &(dsaValue->p));
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssNodeSetBigNumValue(p)",
                            xmlSecKeyDataKlassGetName(id));
        return(-1);
    }

    /* q */
    ret = xmlSecNssSetBigNumValue(&(ctx->pubkey->u.dsa.params.subPrime), &(dsaValue->q));
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssNodeSetBigNumValue(q)",
                            xmlSecKeyDataKlassGetName(id));
        return(-1);
    }

    /* g */
    ret = xmlSecNssSetBigNumValue(&(ctx->pubkey->u.dsa.params.base), &(dsaValue->g));
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssNodeSetBigNumValue(g)",
                            xmlSecKeyDataKlassGetName(id));
        return(-1);
    }

    /* x: not supported in NSS */

    /* y */
    ret = xmlSecNssSetBigNumValue(&(ctx->pubkey->u.dsa.publicValue), &(dsaValue->y));
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssNodeSetBigNumValue(y)",
                            xmlSecKeyDataKlassGetName(id));
        return(-1);
    }

    /* done */
    return(0);
}

#endif /* XMLSEC_NO_DSA */

#ifndef XMLSEC_NO_RSA
/******************************************************************************
 *
 * &lt;dsig:RSAKeyValue/&gt; processing
 *
 * http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
 * The RSAKeyValue Element
 *
 * RSA key values have two fields: Modulus and Exponent.
 *
 * <RSAKeyValue>
 *   <Modulus>xA7SEU+e0yQH5rm9kbCDN9o3aPIo7HbP7tX6WOocLZAtNfyxSZDU16ksL6W
 *     jubafOqNEpcwR3RdFsT7bCqnXPBe5ELh5u4VEy19MzxkXRgrMvavzyBpVRgBUwUlV
 *        5foK5hhmbktQhyNdy/6LpQRhDUDsTvK+g9Ucj47es9AQJ3U=
 *   </Modulus>
 *   <Exponent>AQAB</Exponent>
 * </RSAKeyValue>
 *
 * Arbitrary-length integers (e.g. "bignums" such as RSA moduli) are
 * represented in XML as octet strings as defined by the ds:CryptoBinary type.
 *
 * Schema Definition:
 *
 * <element name="RSAKeyValue" type="ds:RSAKeyValueType"/>
 * <complexType name="RSAKeyValueType">
 *   <sequence>
 *     <element name="Modulus" type="ds:CryptoBinary"/>
 *     <element name="Exponent" type="ds:CryptoBinary"/>
 *   </sequence>
 * </complexType>
 *
 * DTD Definition:
 *
 * <!ELEMENT RSAKeyValue (Modulus, Exponent) >
 * <!ELEMENT Modulus (#PCDATA) >
 * <!ELEMENT Exponent (#PCDATA) >
 *
 * ============================================================================
 *
 * To support reading/writing private keys an PrivateExponent element is added
 * to the end
 *
  *****************************************************************************/

static int              xmlSecNssKeyDataRsaXmlRead      (xmlSecKeyDataId id,
                                                         xmlSecKeyPtr key,
                                                         xmlNodePtr node,
                                                         xmlSecKeyInfoCtxPtr keyInfoCtx);
static int              xmlSecNssKeyDataRsaXmlWrite     (xmlSecKeyDataId id,
                                                         xmlSecKeyPtr key,
                                                         xmlNodePtr node,
                                                         xmlSecKeyInfoCtxPtr keyInfoCtx);
static int              xmlSecNssKeyDataRsaGenerate     (xmlSecKeyDataPtr data,
                                                         xmlSecSize sizeBits,
                                                        xmlSecKeyDataType type);

static xmlSecKeyDataPtr xmlSecNssKeyDataRsaRead         (xmlSecKeyDataId id,
                                                         xmlSecKeyValueRsaPtr rsaValue);
static int              xmlSecNssKeyDataRsaWrite        (xmlSecKeyDataId id,
                                                         xmlSecKeyDataPtr data,
                                                         xmlSecKeyValueRsaPtr rsaValue,
                                                         int writePrivateKey);

XMLSEC_NSS_PKI_KEY_KLASS_EX(Rsa, RSA, xmlSecDSigNs,
    xmlSecNssKeyDataRsaGenerate,
    xmlSecNssKeyDataRsaXmlRead,
    xmlSecNssKeyDataRsaXmlWrite)

/**
 * @brief The RSA key data klass.
 * @return pointer to RSA key data klass.
 */
xmlSecKeyDataId
xmlSecNssKeyDataRsaGetKlass(void) {
    return(&xmlSecNssKeyDataRsaKlass);
}

static int
xmlSecNssKeyDataRsaXmlRead(xmlSecKeyDataId id, xmlSecKeyPtr key,
                           xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    xmlSecAssert2(id == xmlSecNssKeyDataRsaId, -1);
    return(xmlSecKeyDataRsaXmlRead(id, key, node, keyInfoCtx,
        xmlSecNssKeyDataRsaRead));
}

static int
xmlSecNssKeyDataRsaXmlWrite(xmlSecKeyDataId id, xmlSecKeyPtr key,
                            xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    xmlSecAssert2(id == xmlSecNssKeyDataRsaId, -1);
    return(xmlSecKeyDataRsaXmlWrite(id, key, node, keyInfoCtx,
        xmlSecBase64GetDefaultLineSize(), 1, /* add line breaks */
        xmlSecNssKeyDataRsaWrite));
}

static xmlSecKeyDataPtr
xmlSecNssKeyDataRsaRead(xmlSecKeyDataId id, xmlSecKeyValueRsaPtr rsaValue) {
    xmlSecKeyDataPtr data = NULL;
    xmlSecKeyDataPtr res = NULL;
    PK11SlotInfo *slot = NULL;
    SECKEYPublicKey *pubkey=NULL;
    PRArenaPool *arena = NULL;
    int ret;

    xmlSecAssert2(id == xmlSecNssKeyDataRsaId, NULL);
    xmlSecAssert2(rsaValue != NULL, NULL);

    slot = PK11_GetBestSlot(CKM_RSA_PKCS, NULL);
    if(slot == NULL) {
        xmlSecNssError("PK11_GetBestSlot", xmlSecKeyDataKlassGetName(id));
        goto done;
    }

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if(arena == NULL) {
        xmlSecNssError("PORT_NewArena", xmlSecKeyDataKlassGetName(id));
        goto done;
    }

    pubkey = (SECKEYPublicKey *)PORT_ArenaZAlloc(arena,
                                                 sizeof(SECKEYPublicKey));
    if(pubkey == NULL) {
        xmlSecNssError("PORT_ArenaZAlloc", xmlSecKeyDataKlassGetName(id));
        goto done;
    }
    pubkey->arena = arena;
    pubkey->keyType = rsaKey;
    arena = NULL; /* owned by pubkey */

    /* Modulus */
    ret = xmlSecNssGetBigNumValue(&(rsaValue->modulus), pubkey->arena, &(pubkey->u.rsa.modulus));
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssGetBigNumValue(Modulus)",
                            xmlSecKeyDataKlassGetName(id));
        goto done;
    }

    /* Exponent */
    ret = xmlSecNssGetBigNumValue(&(rsaValue->publicExponent), pubkey->arena, &(pubkey->u.rsa.publicExponent));
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssGetBigNumValue(Exponent)",
                            xmlSecKeyDataKlassGetName(id));
        goto done;
    }

    /* next is PrivateExponent (priv key). NSS does not support it, we just ignore it */

    /* create key */
    data = xmlSecKeyDataCreate(id);
    if(data == NULL) {
        xmlSecInternalError("xmlSecKeyDataCreate",
                            xmlSecKeyDataKlassGetName(id));
        ret = -1;
        goto done;
    }

    ret = xmlSecNssPKIKeyDataAdoptKey(data, NULL, pubkey);
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssPKIKeyDataAdoptKey",
                            xmlSecKeyDataKlassGetName(id));
        xmlSecKeyDataDestroy(data);
        goto done;
    }
    pubkey = NULL; /* owned by data now */

    /* success */
    res = data;
    data = NULL;

done:
    if (slot != 0) {
        PK11_FreeSlot(slot);
    }
    if(arena != NULL) {
        PORT_FreeArena(arena, PR_FALSE);
    }
    if (pubkey != 0) {
        SECKEY_DestroyPublicKey(pubkey);
    }
    if (data != 0) {
        xmlSecKeyDataDestroy(data);
    }
    return(res);
}

static int
xmlSecNssKeyDataRsaWrite(xmlSecKeyDataId id,xmlSecKeyDataPtr data,
                         xmlSecKeyValueRsaPtr rsaValue,
                         int writePrivateKey XMLSEC_ATTRIBUTE_UNUSED) {
    xmlSecNssPKIKeyDataCtxPtr ctx;
    int ret;

    xmlSecAssert2(id == xmlSecNssKeyDataRsaId, -1);
    xmlSecAssert2(data != NULL, -1);
    xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecNssKeyDataRsaId), -1);
    xmlSecAssert2(rsaValue != NULL, -1);
    UNREFERENCED_PARAMETER(writePrivateKey);

    ctx = xmlSecNssPKIKeyDataGetCtx(data);
    xmlSecAssert2(ctx != NULL, -1);
    xmlSecAssert2(ctx->pubkey != NULL, -1);
    xmlSecAssert2(SECKEY_GetPublicKeyType(ctx->pubkey) == rsaKey, -1);

    /* Modulus */
    ret = xmlSecNssSetBigNumValue(&(ctx->pubkey->u.rsa.modulus), &(rsaValue->modulus));
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssNodeSetBigNumValue(Modulus)",
                            xmlSecKeyDataKlassGetName(id));
        return(-1);
    }

    /* Exponent */
    ret = xmlSecNssSetBigNumValue(&(ctx->pubkey->u.rsa.publicExponent), &(rsaValue->publicExponent));
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssNodeSetBigNumValue(Exponent)",
                            xmlSecKeyDataKlassGetName(id));
        return(-1);
    }

    /* next is PrivateExponent node: not supported in NSS */

    return(0);
}

static int
xmlSecNssKeyDataRsaGenerate(xmlSecKeyDataPtr data, xmlSecSize sizeBits, xmlSecKeyDataType type XMLSEC_ATTRIBUTE_UNUSED) {
    PK11RSAGenParams  params;
    PK11SlotInfo *slot = NULL;
    SECKEYPrivateKey *privkey = NULL;
    SECKEYPublicKey  *pubkey = NULL;
    SECStatus rv;
    int  ret = -1;

    xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecNssKeyDataRsaId), -1);
    xmlSecAssert2(sizeBits > 0, -1);

    XMLSEC_SAFE_CAST_SIZE_TO_INT(sizeBits, params.keySizeInBits, return(-1), xmlSecKeyDataGetName(data));
    params.pe = 65537;

    slot = PK11_GetBestSlot(CKM_RSA_PKCS_KEY_PAIR_GEN, NULL);
    if(slot == NULL) {
        xmlSecNssError("PK11_GetBestSlot", xmlSecKeyDataGetName(data));
        goto done;
    }

    rv = PK11_Authenticate(slot, PR_TRUE, NULL /* default pwd callback */);
    if (rv != SECSuccess) {
        xmlSecNssError2("PK11_Authenticate", xmlSecKeyDataGetName(data),
                        "token=%s", xmlSecErrorsSafeString(PK11_GetTokenName(slot)));
        goto done;
    }

    privkey = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, &params,
                                   &pubkey, PR_FALSE, PR_TRUE, NULL);
    if(privkey == NULL || pubkey == NULL) {
        xmlSecNssError("PK11_GenerateKeyPair", xmlSecKeyDataGetName(data));
        goto done;
    }

    ret = xmlSecNssPKIKeyDataAdoptKey(data, privkey, pubkey);
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssPKIKeyDataAdoptKey",
                            xmlSecKeyDataGetName(data));
        goto done;
    }

    ret = 0;

done:
    if (slot != NULL) {
        PK11_FreeSlot(slot);
    }
    if (ret == 0) {
        return (0);
    }

    if (pubkey != NULL) {
        SECKEY_DestroyPublicKey(pubkey);
    }
    if (privkey != NULL) {
        SECKEY_DestroyPrivateKey(privkey);
    }
    return(-1);
}

#endif /* XMLSEC_NO_RSA */

#ifndef XMLSEC_NO_EC

static int              xmlSecNssKeyDataEcXmlRead       (xmlSecKeyDataId id,
                                                         xmlSecKeyPtr key,
                                                         xmlNodePtr node,
                                                         xmlSecKeyInfoCtxPtr keyInfoCtx);
static int              xmlSecNssKeyDataEcXmlWrite      (xmlSecKeyDataId id,
                                                         xmlSecKeyPtr key,
                                                         xmlNodePtr node,
                                                         xmlSecKeyInfoCtxPtr keyInfoCtx);

static xmlSecKeyDataPtr xmlSecNssKeyDataEcRead          (xmlSecKeyDataId id,
                                                         xmlSecKeyValueEcPtr ecValue);
static int              xmlSecNssKeyDataEcWrite         (xmlSecKeyDataId id,
                                                         xmlSecKeyDataPtr data,
                                                         xmlSecKeyValueEcPtr ecValue);


XMLSEC_NSS_PKI_KEY_KLASS_EX(Ec, EC, xmlSecDSig11Ns,
    NULL,
    xmlSecNssKeyDataEcXmlRead,
    xmlSecNssKeyDataEcXmlWrite)

/**
 * @brief The EC key data klass.
 * @return pointer to EC key data klass.
 */
xmlSecKeyDataId
xmlSecNsskeyDataEcGetKlass(void) {
    return(&xmlSecNssKeyDataEcKlass);
}

static int
xmlSecNssKeyDataEcXmlRead(xmlSecKeyDataId id, xmlSecKeyPtr key,
                           xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    xmlSecAssert2(id == xmlSecNssKeyDataEcId, -1);
    return(xmlSecKeyDataEcXmlRead(id, key, node, keyInfoCtx,
        xmlSecNssKeyDataEcRead));
}

static int
xmlSecNssKeyDataEcXmlWrite(xmlSecKeyDataId id, xmlSecKeyPtr key,
                                xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    xmlSecAssert2(id == xmlSecNssKeyDataEcId, -1);
    return(xmlSecKeyDataEcXmlWrite(id, key, node, keyInfoCtx,
        xmlSecBase64GetDefaultLineSize(), 1, /* add line breaks */
        xmlSecNssKeyDataEcWrite));
}

typedef struct _xmlSecNssKeyDataEcCurveNameAndOID {
    SECOidTag curveOidTag;
    xmlChar oid[128];
} xmlSecNssKeyDataEcCurveNameAndOID;

static const xmlSecNssKeyDataEcCurveNameAndOID g_xmlSecNssKeyDataEcCurveNameAndOID[] = {
    { SEC_OID_ANSIX962_EC_PRIME192V1, "1.2.840.10045.3.1.1" }, /* "prime192v1" */
    { SEC_OID_ANSIX962_EC_PRIME192V2, "1.2.840.10045.3.1.2" },
    { SEC_OID_ANSIX962_EC_PRIME192V3, "1.2.840.10045.3.1.3" },
    { SEC_OID_ANSIX962_EC_PRIME239V1, "1.2.840.10045.3.1.4" },
    { SEC_OID_ANSIX962_EC_PRIME239V2, "1.2.840.10045.3.1.5" },
    { SEC_OID_ANSIX962_EC_PRIME239V3, "1.2.840.10045.3.1.6" },
    { SEC_OID_ANSIX962_EC_PRIME256V1, "1.2.840.10045.3.1.7" }, /* prime256v1 */
    { SEC_OID_SECG_EC_SECP224R1, "1.3.132.0.33" }, /* secp224r1 */
    { SEC_OID_SECG_EC_SECP384R1, "1.3.132.0.34" }, /* secp384r1 */
    { SEC_OID_SECG_EC_SECP521R1, "1.3.132.0.35" }  /* secp521r1 */
};


static const xmlChar*
xmlSecNssKeyDataEcGetOidFromOidTag(SECOidTag curveOidTag) {
    xmlSecSize size = sizeof(g_xmlSecNssKeyDataEcCurveNameAndOID) / sizeof(g_xmlSecNssKeyDataEcCurveNameAndOID[0]);

    xmlSecAssert2(curveOidTag != SEC_OID_UNKNOWN, NULL);

    for(xmlSecSize ii = 0; ii < size; ++ii) {
        if(curveOidTag == g_xmlSecNssKeyDataEcCurveNameAndOID[ii].curveOidTag) {
            return(g_xmlSecNssKeyDataEcCurveNameAndOID[ii].oid);
        }
    }
    return(NULL);
}

static SECOidTag
xmlSecNssKeyDataEcGetOidTagFromOid(const xmlChar * oid) {
    xmlSecSize size = sizeof(g_xmlSecNssKeyDataEcCurveNameAndOID) / sizeof(g_xmlSecNssKeyDataEcCurveNameAndOID[0]);

    xmlSecAssert2(oid != NULL, SEC_OID_UNKNOWN);

    for(xmlSecSize ii = 0; ii < size; ++ii) {
        if(xmlStrcmp(oid, g_xmlSecNssKeyDataEcCurveNameAndOID[ii].oid) == 0) {
            return(g_xmlSecNssKeyDataEcCurveNameAndOID[ii].curveOidTag);
        }
    }
    return(SEC_OID_UNKNOWN);
}


static xmlSecKeyDataPtr
xmlSecNssKeyDataEcRead(xmlSecKeyDataId id, xmlSecKeyValueEcPtr ecValue) {
    xmlSecKeyDataPtr data = NULL;
    xmlSecKeyDataPtr res = NULL;
    PK11SlotInfo *slot = NULL;
    CK_OBJECT_HANDLE handle;
    SECKEYPublicKey *pubkey=NULL;
    PRArenaPool *arena = NULL;
    SECItem ecparams = { siBuffer, NULL, 0 };
    SECOidData *oidData = NULL;
    SECOidTag oidTag;
    SECStatus rv;
    int ret;

    xmlSecAssert2(id == xmlSecNssKeyDataEcId, NULL);
    xmlSecAssert2(ecValue != NULL, NULL);
    xmlSecAssert2(ecValue->curve != NULL, NULL);

    /* prepare and create public key */
    slot = PK11_GetBestSlot(CKM_ECDSA, NULL);
    if(slot == NULL) {
        xmlSecNssError("PK11_GetBestSlot", xmlSecKeyDataKlassGetName(id));
        goto done;
    }

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if(arena == NULL) {
        xmlSecNssError("PORT_NewArena", xmlSecKeyDataKlassGetName(id));
        goto done;
    }

    pubkey = (SECKEYPublicKey *)PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey));
    if(pubkey == NULL) {
        xmlSecNssError2("PORT_ArenaZAlloc", xmlSecKeyDataKlassGetName(id),
            "size=" XMLSEC_SIZE_T_FMT, sizeof(SECKEYPublicKey));
        goto done;
    }
    pubkey->arena = arena;
    pubkey->keyType = ecKey;
    arena = NULL; /* owned by pubkey */

    /* get curve */
    oidTag = xmlSecNssKeyDataEcGetOidTagFromOid(ecValue->curve);
    if(oidTag == SEC_OID_UNKNOWN) {
        xmlSecInternalError2("xmlSecNssKeyDataEcGetOidTagFromOid",  xmlSecKeyDataKlassGetName(id),
            "curve_oid=%s", xmlSecErrorsSafeString(ecValue->curve));
        goto done;
    }
    oidData = SECOID_FindOIDByTag(oidTag);
    if(oidData == NULL) {
        xmlSecNssError2("SECOID_FindOIDByTag",  xmlSecKeyDataKlassGetName(id),
            "curve_oid=%s", xmlSecErrorsSafeString(ecValue->curve));
        goto done;
    }
    if(SECITEM_AllocItem(pubkey->arena, &ecparams, (2 + oidData->oid.len)) == NULL) {
        xmlSecNssError2("SECITEM_AllocItem",  xmlSecKeyDataKlassGetName(id),
            "curve_oid=%u", (2 + oidData->oid.len));
        goto done;
    }
    ecparams.data[0] = SEC_ASN1_OBJECT_ID;
    XMLSEC_SAFE_CAST_UINT_TO_BYTE(oidData->oid.len, ecparams.data[1], goto done, xmlSecKeyDataKlassGetName(id));
    memcpy(ecparams.data + 2, oidData->oid.data, oidData->oid.len);

    rv = SECITEM_CopyItem(pubkey->arena, &(pubkey->u.ec.DEREncodedParams), &(ecparams));
    if(rv != SECSuccess) {
        xmlSecNssError("SECITEM_CopyItem", xmlSecKeyDataKlassGetName(id));
        goto done;
    }

    /* publicValue */
    ret = xmlSecNssGetBigNumValue(&(ecValue->pubkey), pubkey->arena, &(pubkey->u.ec.publicValue));
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssGetBigNumValue(publicValue)", xmlSecKeyDataKlassGetName(id));
        goto done;
    }

    /* create key */
    handle = PK11_ImportPublicKey(slot, pubkey, PR_FALSE);
    if(handle == CK_INVALID_HANDLE) {
        xmlSecNssError("PK11_ImportPublicKey", xmlSecKeyDataKlassGetName(id));
        goto done;
    }

    data = xmlSecKeyDataCreate(id);
    if(data == NULL) {
        xmlSecInternalError("xmlSecKeyDataCreate", xmlSecKeyDataKlassGetName(id));
        goto done;
    }

    ret = xmlSecNssPKIKeyDataAdoptKey(data, NULL, pubkey);
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssPKIKeyDataAdoptKey", xmlSecKeyDataGetName(data));
        goto done;
    }
    pubkey = NULL; /* owned by data now */

    /* success */
    res = data;
    data = NULL;

done:
    if (pubkey != NULL) {
        SECKEY_DestroyPublicKey(pubkey);
    }
    if (arena != NULL) {
        PORT_FreeArena(arena, PR_FALSE);
    }
    if (slot != NULL) {
        PK11_FreeSlot(slot);
    }
    if (data != NULL) {
        xmlSecKeyDataDestroy(data);
    }
    return(res);
}


static SECOidTag
xmlSecNssKeyDataEcGetOidTag(const SECKEYECParams *params)
{
    SECItem oid = { siBuffer, NULL, 0 };
    SECOidData *oidData = NULL;

    xmlSecAssert2(params != NULL, SEC_OID_UNKNOWN);

    /*
     * params->data needs to contain the ASN encoding of an object ID (OID)
     * representing a named curve. Here, we strip away everything
     * before the actual OID and use the OID to look up a named curve.
     */
    if((params->len <= 2) || (params->data[0] != SEC_ASN1_OBJECT_ID)) {
        return(SEC_OID_UNKNOWN);
    }
    oid.len = params->len - 2;
    oid.data = params->data + 2;
    oidData = SECOID_FindOID(&oid);
    if(oidData == NULL) {
        return(SEC_OID_UNKNOWN);
    }
    return oidData->offset;
}


static int
xmlSecNssKeyDataEcWrite(xmlSecKeyDataId id, xmlSecKeyDataPtr data, xmlSecKeyValueEcPtr ecValue) {
    xmlSecNssPKIKeyDataCtxPtr ctx;
    SECOidTag oidTag;
    const xmlChar * curve;
    int ret;

    xmlSecAssert2(id == xmlSecNssKeyDataEcId, -1);
    xmlSecAssert2(data != NULL, -1);
    xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecNssKeyDataEcId), -1);
    xmlSecAssert2(ecValue != NULL, -1);
    xmlSecAssert2(ecValue->curve == NULL, -1);

    ctx = xmlSecNssPKIKeyDataGetCtx(data);
    xmlSecAssert2(ctx != NULL, -1);
    xmlSecAssert2(ctx->pubkey != NULL, -1);
    xmlSecAssert2(SECKEY_GetPublicKeyType(ctx->pubkey) == ecKey, -1);

    /* curve */
    oidTag = xmlSecNssKeyDataEcGetOidTag(&(ctx->pubkey->u.ec.DEREncodedParams));
    if(oidTag == SEC_OID_UNKNOWN) {
        xmlSecInternalError("xmlSecNssKeyDataEcGetOidTag", xmlSecKeyDataKlassGetName(id));
        return(-1);
    }
    curve = xmlSecNssKeyDataEcGetOidFromOidTag(oidTag);
    if(curve == NULL) {
        xmlSecNssError2("xmlSecNssKeyDataEcGetOidFromOidTag", xmlSecKeyDataKlassGetName(id),
            "oidTag=%d", (int)oidTag);
        return(-1);
    }
    ecValue->curve = xmlStrdup(curve);
    if(ecValue->curve == NULL) {
        xmlSecStrdupError(curve, xmlSecKeyDataKlassGetName(id));
        return(-1);
    }

    /* publicValue */
    ret = xmlSecNssSetBigNumValue(&(ctx->pubkey->u.ec.publicValue), &(ecValue->pubkey));
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssNodeSetBigNumValue(p)", xmlSecKeyDataKlassGetName(id));
        return(-1);
    }

    /* done */
    return(0);
}
#endif /* XMLSEC_NO_EC */

#ifndef XMLSEC_NO_EDDSA
/******************************************************************************
 *
 * EdDSA key data (Ed25519 and Ed448)
 *
  *****************************************************************************/

/* EdDSA klass: no XML KeyValue representation, load from file/DER/PEM only */
#define XMLSEC_NSS_PKI_KEY_KLASS_EDDSA(lcname, ucname, generate)                          \
static xmlSecKeyDataKlass xmlSecNssKeyData ## lcname ## Klass = {                        \
    sizeof(xmlSecKeyDataKlass),                 /* xmlSecSize klassSize */                \
    xmlSecNssPKIKeyDataSize,                    /* xmlSecSize objSize */                  \
    /* data */                                                                            \
    xmlSecName ## ucname ## KeyValue,           /* const xmlChar* name; */                \
    xmlSecKeyDataUsageReadFromFile | xmlSecKeyDataUsageRetrievalMethodNodeXml,            \
                                                /* xmlSecKeyDataUsage usage; */           \
    xmlSecHref ## ucname ## KeyValue,           /* const xmlChar* href; */                \
    NULL,                                       /* const xmlChar* dataNodeName; */        \
    NULL,                                       /* const xmlChar* dataNodeNs; */          \
    /* constructors/destructor */                                                         \
    xmlSecNssPKIKeyDataInitialize,              /* xmlSecKeyDataInitializeMethod initialize; */ \
    xmlSecNssPKIKeyDataDuplicate,               /* xmlSecKeyDataDuplicateMethod duplicate; */   \
    xmlSecNssPKIKeyDataFinalize,                /* xmlSecKeyDataFinalizeMethod finalize; */      \
    generate,                                   /* xmlSecKeyDataGenerateMethod generate; */      \
    /* get info */                                                                        \
    xmlSecNssPKIKeyDataGetType,                 /* xmlSecKeyDataGetTypeMethod getType; */ \
    xmlSecNssPKIKeyDataGetSize,                 /* xmlSecKeyDataGetSizeMethod getSize; */ \
    NULL,                                       /* DEPRECATED xmlSecKeyDataGetIdentifier getIdentifier; */ \
    /* read/write */                                                                      \
    NULL,                                       /* xmlSecKeyDataXmlReadMethod xmlRead; */ \
    NULL,                                       /* xmlSecKeyDataXmlWriteMethod xmlWrite; */ \
    NULL,                                       /* xmlSecKeyDataBinReadMethod binRead; */ \
    NULL,                                       /* xmlSecKeyDataBinWriteMethod binWrite; */ \
    /* debug */                                                                           \
    xmlSecKeyDataDebugDumpImpl,                 /* xmlSecKeyDataDebugDumpMethod debugDump; */ \
    xmlSecKeyDataDebugXmlDumpImpl,              /* xmlSecKeyDataDebugDumpMethod debugXmlDump; */ \
    /* reserved for the future */                                                         \
    NULL,                                       /* void* reserved0; */                   \
    NULL,                                       /* void* reserved1; */                   \
};

XMLSEC_NSS_PKI_KEY_KLASS_EDDSA(EdDSA, EdDSA, NULL)

/**
 * @brief The EdDSA key data klass (Ed25519 and Ed448).
 * @return pointer to EdDSA key data klass.
 */
xmlSecKeyDataId
xmlSecNssKeyDataEdDSAGetKlass(void) {
    return(&xmlSecNssKeyDataEdDSAKlass);
}

#endif /* XMLSEC_NO_EDDSA */

#ifndef XMLSEC_NO_XDH
/******************************************************************************
 *
 * XDH key data (X25519 and X448, RFC 7748)
 *
  *****************************************************************************/

/* XDH klass: no XML KeyValue representation, load from file/DER/PEM only */
#define XMLSEC_NSS_PKI_KEY_KLASS_XDH(lcname, ucname, generate)                          \
static xmlSecKeyDataKlass xmlSecNssKeyData ## lcname ## Klass = {                        \
    sizeof(xmlSecKeyDataKlass),                 /* xmlSecSize klassSize */                \
    xmlSecNssPKIKeyDataSize,                    /* xmlSecSize objSize */                  \
    /* data */                                                                            \
    xmlSecName ## ucname ## KeyValue,           /* const xmlChar* name; */                \
    xmlSecKeyDataUsageReadFromFile | xmlSecKeyDataUsageRetrievalMethodNodeXml,            \
                                                /* xmlSecKeyDataUsage usage; */           \
    xmlSecHref ## ucname ## KeyValue,           /* const xmlChar* href; */                \
    NULL,                                       /* const xmlChar* dataNodeName; */        \
    NULL,                                       /* const xmlChar* dataNodeNs; */          \
    /* constructors/destructor */                                                         \
    xmlSecNssPKIKeyDataInitialize,              /* xmlSecKeyDataInitializeMethod initialize; */ \
    xmlSecNssPKIKeyDataDuplicate,               /* xmlSecKeyDataDuplicateMethod duplicate; */   \
    xmlSecNssPKIKeyDataFinalize,                /* xmlSecKeyDataFinalizeMethod finalize; */      \
    generate,                                   /* xmlSecKeyDataGenerateMethod generate; */      \
    /* get info */                                                                        \
    xmlSecNssPKIKeyDataGetType,                 /* xmlSecKeyDataGetTypeMethod getType; */ \
    xmlSecNssPKIKeyDataGetSize,                 /* xmlSecKeyDataGetSizeMethod getSize; */ \
    NULL,                                       /* DEPRECATED xmlSecKeyDataGetIdentifier getIdentifier; */ \
    /* read/write */                                                                      \
    NULL,                                       /* xmlSecKeyDataXmlReadMethod xmlRead; */ \
    NULL,                                       /* xmlSecKeyDataXmlWriteMethod xmlWrite; */ \
    NULL,                                       /* xmlSecKeyDataBinReadMethod binRead; */ \
    NULL,                                       /* xmlSecKeyDataBinWriteMethod binWrite; */ \
    /* debug */                                                                           \
    xmlSecKeyDataDebugDumpImpl,                 /* xmlSecKeyDataDebugDumpMethod debugDump; */ \
    xmlSecKeyDataDebugXmlDumpImpl,              /* xmlSecKeyDataDebugDumpMethod debugXmlDump; */ \
    /* reserved for the future */                                                         \
    NULL,                                       /* void* reserved0; */                   \
    NULL,                                       /* void* reserved1; */                   \
};

XMLSEC_NSS_PKI_KEY_KLASS_XDH(Xdh, XDH, NULL)

/**
 * @brief The XDH key data klass (X25519 and X448).
 * @return pointer to XDH key data klass.
 */
xmlSecKeyDataId
xmlSecNssKeyDataXdhGetKlass(void) {
    return(&xmlSecNssKeyDataXdhKlass);
}

#endif /* XMLSEC_NO_XDH */
