/* -*- mode: c++; indent-tabs-mode: nil -*- */
/*
    QC_SSLPrivateKey.qpp

    Qore Programming Language

    Copyright (C) 2003 - 2023 Qore Technologies, s.r.o.

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.

    Note that the Qore library is released under a choice of three open-source
    licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
    information.
*/

#include <qore/Qore.h>
#include "qore/intern/QC_SSLPrivateKey.h"
#include "qore/intern/QC_Serializable.h"

#include <openssl/ssl.h>
#include <openssl/pem.h>

static QoreHashNode* SSLPrivateKey_serializer(const QoreObject& self, const QoreSSLPrivateKey& pk, QoreSerializationContext& context, ExceptionSink* xsink) {
    ReferenceHolder<QoreHashNode> rv(new QoreHashNode(autoTypeInfo), xsink);
    // we serialize with DER format as it's approx 2/3 the size of a PEM string
    SimpleRefHolder<BinaryNode> der(pk.getDER(xsink));
    if (*xsink) {
        return nullptr;
    }
    //printd(5, "SSLPrivateKey_serializer() der: %p %d bytes\n", *der, der->size());
    rv->setKeyValue("der", der.release(), xsink);
    return *xsink ? nullptr : rv.release();
}

static void SSLPrivateKey_deserializer(QoreObject& self, const QoreHashNode* sdata, QoreDeserializationContext& context, ExceptionSink* xsink) {
    if (sdata) {
        // we serialize with DER format as it's approx 2/3 the size of a PEM string
        QoreValue val = sdata->getKeyValue("der");
        if (val) {
            if (val.getType() != NT_BINARY) {
                xsink->raiseException("DESERIALIZATION-ERROR", "SSLPrivateKey serialization key 'der' has type '%s'; expecting 'binary'", val.getTypeName());
                return;
            }
            const BinaryNode* der = val.get<const BinaryNode>();
            SimpleRefHolder<QoreSSLPrivateKey> qpk(new QoreSSLPrivateKey(der, xsink));
            if (!*xsink) {
                self.setPrivate(CID_SSLPRIVATEKEY, qpk.release());
            }
            return;
        }
    }

    xsink->raiseException("DESERIALIZATION-ERROR", "missing serialization data for the SSLPrivateKey class");
}

//! This class implements a container for private key data
/** The SSLPrivateKey class supports @ref Serializable "data serialization"; note that the
    serialized data is a representation of the data that does not require a password, so
    for source data that requires a password to use, the serialized representation of the
    private key should be protected carefully (transmitted only in an encrypted stream)
 */
qclass SSLPrivateKey [arg=QoreSSLPrivateKey* pk; vparent=Serializable; serializer=reinterpret_cast<q_serializer_t>(SSLPrivateKey_serializer); deserializer=SSLPrivateKey_deserializer];

//! Creates the SSLPrivateKey object from the PEM-encoded text representation of the private key passed
/** @param pem The PEM-encoded text representation of the private key
    @param pass The optional password or passphrase for the private key

    @par Example:
    @code{.py}
SSLPrivateKey key(pem);
    @endcode

    @throw SSLPRIVATEKEY-CONSTRUCTOR-ERROR error in private key data

    @since 0.8.4: the deprecated functionality where the if the string passed was less than 200 bytes long, it was assumed to be a file name has been removed; the string is assumed to be the PEM-encoded private key itself
 */
SSLPrivateKey::constructor(string pem, *string pass) {
    SimpleRefHolder<QoreSSLPrivateKey> qpk(new QoreSSLPrivateKey(pem, pass ? pass->c_str() : nullptr, xsink));

    if (!*xsink) {
        self->setPrivate(CID_SSLPRIVATEKEY, qpk.release());
    }
}

//! Creates the SSLPrivateKey object from the data argument passed
/** @param der The DER-encoded binary representation of the private key

    @par Example:
    @code{.py}
SSLPrivateKey key(der);
    @endcode

    @throw SSLPRIVATEKEY-CONSTRUCTOR-ERROR error in private key data
 */
SSLPrivateKey::constructor(binary der) {
    SimpleRefHolder<QoreSSLPrivateKey> qc(new QoreSSLPrivateKey(der, xsink));
    if (!*xsink) {
        self->setPrivate(CID_SSLPRIVATEKEY, qc.release());
    }
}

//! Returns a copy of the private key object
/**
    @since %Qore 0.9
 */
SSLPrivateKey::copy() {
    SimpleRefHolder<BinaryNode> der(pk->getDER(xsink));
    if (*xsink) {
        return;
    }
    SimpleRefHolder<QoreSSLPrivateKey> qpk(new QoreSSLPrivateKey(*der, xsink));
    if (!*xsink) {
        self->setPrivate(CID_SSLPRIVATEKEY, qpk.release());
    }
}

//! Returns a hash of all information for the private key
/** @return a hash of all information for the private key with the following keys:
    - \c "type": The type of private key (see SSLPrivateKey::getType())
    - \c "version": The version of the private key (see SSLPrivateKey::getVersion())
    - \c "bitLength": The bit length of the private key (see SSLPrivateKey::getBitLength())

    @par Example:
    @code{.py}
hash<auto> h = key.getInfo();
    @endcode
 */
hash<auto> SSLPrivateKey::getInfo() [flags=CONSTANT] {
    return pk->getInfo();
}

//! Returns a string giving the algorithm used for the private key
/** @return a string giving the algorithm used for the private key (ex: \c "RSA", \c "RSA2", \c "DSA", \c "DSA1", \c "DSA2", \c "DAS3", \c "DAS4", \c "DH", \c "unknown")

    @par Example:
    @code{.py}
string str = pkey.getType();
    @endcode
 */
string SSLPrivateKey::getType() [flags=CONSTANT] {
    return new QoreStringNode(pk->getType());
}

//! Returns a constant value of 1; do not use; only included for backwards-compatibility
/** The openssl library never put any usable value into the internal fields that were used to provide this information; newer versions of openssl do not expose this information at all, therefore this method now returns a constant value of 1 for backwards-compatibility.

    @return a constant value of 1; do not use; only included for backwards-compatibility
 */
int SSLPrivateKey::getVersion() [flags=CONSTANT] {
    return pk->getVersion();
}

//! Returns the bit length of the private key
/** @return the bit length of the private key

    @par Example:
    @code{.py}
int len = key.getBitLength();
    @endcode
 */
int SSLPrivateKey::getBitLength() [flags=CONSTANT] {
    return pk->getBitLength();
}

//! Returns a string in PEM format representing the private key
/** @return a string in PEM format representing the private key

    @par Example:
    @code{.py}
string pem = key.getPEM();
    @endcode

    @note No password is required to use the PEM data produced even if the object was originally
    created with a password

    @throw SSLPRIVATEKEY-ERROR could not create PEM string from private key data
*/
string SSLPrivateKey::getPEM() [flags=RET_VALUE_ONLY] {
    return pk->getPEM(xsink);
}

//! Returns a binary object in DER format representing the private key
/** @par Example:
    @code{.py}
binary der = key.getDER();
    @endcode

    @return a binary object in DER format representing the private key

    @note No password is required to use the DER data produced even if the object was originally
    created with a password

    @throw SSLPRIVATEKEY-ERROR could not create DER data from private key data

    @since %Qore 0.9
*/
binary SSLPrivateKey::getDER() [flags=RET_VALUE_ONLY] {
    return pk->getDER(xsink);
}
