/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtNetwork module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/


/*!
    \class QSslKey
    \brief The QSslKey class provides an interface for private and public keys.
    \since 4.3

    \reentrant
    \ingroup network
    \ingroup ssl
    \ingroup shared
    \inmodule QtNetwork

    QSslKey provides a simple API for managing keys.

    \sa QSslSocket, QSslCertificate, QSslCipher
*/

#include "qsslkey.h"
#include "qsslkey_p.h"
#ifndef QT_NO_OPENSSL
#include "qsslsocket_openssl_symbols_p.h"
#endif
#include "qsslsocket.h"
#include "qsslsocket_p.h"
#include "qasn1element_p.h"

#include <QtCore/qatomic.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qiodevice.h>
#ifndef QT_NO_DEBUG_STREAM
#include <QtCore/qdebug.h>
#endif

QT_BEGIN_NAMESPACE

/*!
    \fn void QSslKeyPrivate::clear(bool deep)
    \internal
 */

/*!
    \fn void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
                               bool deepClear)
    \internal

    Allocates a new rsa or dsa struct and decodes \a pem into it
    according to the current algorithm and type.

    If \a deepClear is true, the rsa/dsa struct is freed if it is was
    already allocated, otherwise we "leak" memory (which is exactly
    what we want for copy construction).

    If \a passPhrase is non-empty, it will be used for decrypting
    \a pem.
*/

/*!
    Constructs a null key.

    \sa isNull()
*/
QSslKey::QSslKey()
    : d(new QSslKeyPrivate)
{
}

/*!
    \internal
*/
QByteArray QSslKeyPrivate::pemHeader() const
{
    if (type == QSsl::PublicKey)
        return QByteArrayLiteral("-----BEGIN PUBLIC KEY-----");
    else if (algorithm == QSsl::Rsa)
        return QByteArrayLiteral("-----BEGIN RSA PRIVATE KEY-----");
    else if (algorithm == QSsl::Dsa)
        return QByteArrayLiteral("-----BEGIN DSA PRIVATE KEY-----");
    else if (algorithm == QSsl::Ec)
        return QByteArrayLiteral("-----BEGIN EC PRIVATE KEY-----");
    else if (algorithm == QSsl::Dh)
        return QByteArrayLiteral("-----BEGIN PRIVATE KEY-----");

    Q_UNREACHABLE();
    return QByteArray();
}

static QByteArray pkcs8Header(bool encrypted)
{
    return encrypted
        ? QByteArrayLiteral("-----BEGIN ENCRYPTED PRIVATE KEY-----")
        : QByteArrayLiteral("-----BEGIN PRIVATE KEY-----");
}

/*!
    \internal
*/
QByteArray QSslKeyPrivate::pemFooter() const
{
    if (type == QSsl::PublicKey)
        return QByteArrayLiteral("-----END PUBLIC KEY-----");
    else if (algorithm == QSsl::Rsa)
        return QByteArrayLiteral("-----END RSA PRIVATE KEY-----");
    else if (algorithm == QSsl::Dsa)
        return QByteArrayLiteral("-----END DSA PRIVATE KEY-----");
    else if (algorithm == QSsl::Ec)
        return QByteArrayLiteral("-----END EC PRIVATE KEY-----");
    else if (algorithm == QSsl::Dh)
        return QByteArrayLiteral("-----END PRIVATE KEY-----");

    Q_UNREACHABLE();
    return QByteArray();
}

static QByteArray pkcs8Footer(bool encrypted)
{
    return encrypted
        ? QByteArrayLiteral("-----END ENCRYPTED PRIVATE KEY-----")
        : QByteArrayLiteral("-----END PRIVATE KEY-----");
}

/*!
    \internal

    Returns a DER key formatted as PEM.
*/
QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const
{
    QByteArray pem(der.toBase64());

    const int lineWidth = 64; // RFC 1421
    const int newLines = pem.size() / lineWidth;
    const bool rem = pem.size() % lineWidth;

    // ### optimize
    for (int i = 0; i < newLines; ++i)
        pem.insert((i + 1) * lineWidth + i, '\n');
    if (rem)
        pem.append('\n'); // ###

    QByteArray extra;
    if (!headers.isEmpty()) {
        QMap<QByteArray, QByteArray>::const_iterator it = headers.constEnd();
        do {
            --it;
            extra += it.key() + ": " + it.value() + '\n';
        } while (it != headers.constBegin());
        extra += '\n';
    }

    if (isEncryptedPkcs8(der)) {
        pem.prepend(pkcs8Header(true) + '\n' + extra);
        pem.append(pkcs8Footer(true) + '\n');
#if !QT_CONFIG(openssl)
    } else if (isPkcs8) {
        pem.prepend(pkcs8Header(false) + '\n' + extra);
        pem.append(pkcs8Footer(false) + '\n');
#endif
    } else {
        pem.prepend(pemHeader() + '\n' + extra);
        pem.append(pemFooter() + '\n');
    }

    return pem;
}

/*!
    \internal

    Returns a PEM key formatted as DER.
*/
QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const
{
    QByteArray header = pemHeader();
    QByteArray footer = pemFooter();

    QByteArray der(pem);

    int headerIndex = der.indexOf(header);
    int footerIndex = der.indexOf(footer, headerIndex + header.length());
    if (type != QSsl::PublicKey) {
        if (headerIndex == -1 || footerIndex == -1) {
            header = pkcs8Header(true);
            footer = pkcs8Footer(true);
            headerIndex = der.indexOf(header);
            footerIndex = der.indexOf(footer, headerIndex + header.length());
        }
        if (headerIndex == -1 || footerIndex == -1) {
            header = pkcs8Header(false);
            footer = pkcs8Footer(false);
            headerIndex = der.indexOf(header);
            footerIndex = der.indexOf(footer, headerIndex + header.length());
        }
    }
    if (headerIndex == -1 || footerIndex == -1)
        return QByteArray();

    der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size()));

    if (der.contains("Proc-Type:")) {
        // taken from QHttpNetworkReplyPrivate::parseHeader
        int i = 0;
        while (i < der.count()) {
            int j = der.indexOf(':', i); // field-name
            if (j == -1)
                break;
            const QByteArray field = der.mid(i, j - i).trimmed();
            j++;
            // any number of LWS is allowed before and after the value
            QByteArray value;
            do {
                i = der.indexOf('\n', j);
                if (i == -1)
                    break;
                if (!value.isEmpty())
                    value += ' ';
                // check if we have CRLF or only LF
                bool hasCR = (i && der[i-1] == '\r');
                int length = i -(hasCR ? 1: 0) - j;
                value += der.mid(j, length).trimmed();
                j = ++i;
            } while (i < der.count() && (der.at(i) == ' ' || der.at(i) == '\t'));
            if (i == -1)
                break; // something is wrong

            headers->insert(field, value);
        }
        der = der.mid(i);
    }

    return QByteArray::fromBase64(der); // ignores newlines
}

bool QSslKeyPrivate::isEncryptedPkcs8(const QByteArray &der) const
{
    static const QList<QByteArray> pbes1OIds {
        // PKCS5
        { PKCS5_MD2_DES_CBC_OID }, { PKCS5_MD2_RC2_CBC_OID },  { PKCS5_MD5_DES_CBC_OID },
        { PKCS5_MD5_RC2_CBC_OID }, { PKCS5_SHA1_DES_CBC_OID }, { PKCS5_SHA1_RC2_CBC_OID },
    };
    QAsn1Element elem;
    if (!elem.read(der) || elem.type() != QAsn1Element::SequenceType)
        return false;

    const auto items = elem.toList();
    if (items.size() != 2
        || items[0].type() != QAsn1Element::SequenceType
        || items[1].type() != QAsn1Element::OctetStringType) {
        return false;
    }

    const auto encryptionSchemeContainer = items[0].toList();
    if (encryptionSchemeContainer.size() != 2
        || encryptionSchemeContainer[0].type() != QAsn1Element::ObjectIdentifierType
        || encryptionSchemeContainer[1].type() != QAsn1Element::SequenceType) {
        return false;
    }

    const QByteArray encryptionScheme = encryptionSchemeContainer[0].toObjectId();
    return encryptionScheme == PKCS5_PBES2_ENCRYPTION_OID
            || pbes1OIds.contains(encryptionScheme)
            || encryptionScheme.startsWith(PKCS12_OID);
}

/*!
    Constructs a QSslKey by decoding the string in the byte array
    \a encoded using a specified \a algorithm and \a encoding format.
    \a type specifies whether the key is public or private.

    If the key is encrypted then \a passPhrase is used to decrypt it.

    After construction, use isNull() to check if \a encoded contained
    a valid key.
*/
QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm,
                 QSsl::EncodingFormat encoding, QSsl::KeyType type, const QByteArray &passPhrase)
    : d(new QSslKeyPrivate)
{
    d->type = type;
    d->algorithm = algorithm;
    if (encoding == QSsl::Der)
        d->decodeDer(encoded, passPhrase);
    else
        d->decodePem(encoded, passPhrase);
}

/*!
    Constructs a QSslKey by reading and decoding data from a
    \a device using a specified \a algorithm and \a encoding format.
    \a type specifies whether the key is public or private.

    If the key is encrypted then \a passPhrase is used to decrypt it.

    After construction, use isNull() to check if \a device provided
    a valid key.
*/
QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::EncodingFormat encoding,
                 QSsl::KeyType type, const QByteArray &passPhrase)
    : d(new QSslKeyPrivate)
{
    QByteArray encoded;
    if (device)
        encoded = device->readAll();
    d->type = type;
    d->algorithm = algorithm;
    if (encoding == QSsl::Der)
        d->decodeDer(encoded, passPhrase);
    else
        d->decodePem(encoded, passPhrase);
}

/*!
    \since 5.0
    Constructs a QSslKey from a valid native key \a handle.
    \a type specifies whether the key is public or private.

    QSslKey will take ownership for this key and you must not
    free the key using the native library.
*/
QSslKey::QSslKey(Qt::HANDLE handle, QSsl::KeyType type)
    : d(new QSslKeyPrivate)
{
#ifndef QT_NO_OPENSSL
    EVP_PKEY *evpKey = reinterpret_cast<EVP_PKEY *>(handle);
    if (!evpKey || !d->fromEVP_PKEY(evpKey)) {
        d->opaque = evpKey;
        d->algorithm = QSsl::Opaque;
    } else {
        q_EVP_PKEY_free(evpKey);
    }
#else
    d->opaque = handle;
    d->algorithm = QSsl::Opaque;
#endif
    d->type = type;
    d->isNull = !d->opaque;
}

/*!
    Constructs an identical copy of \a other.
*/
QSslKey::QSslKey(const QSslKey &other) : d(other.d)
{
}

QSslKey::QSslKey(QSslKey &&other) noexcept
    : d(nullptr)
{
    qSwap(d, other.d);
}

QSslKey &QSslKey::operator=(QSslKey &&other) noexcept
{
    if (this == &other)
        return *this;

    // If no one else is referencing the key data we want to make sure
    // before we swap the d-ptr that it is not left in memory.
    d.reset();
    qSwap(d, other.d);
    return *this;
}

/*!
    Destroys the QSslKey object.
*/
QSslKey::~QSslKey()
{
}

/*!
    Copies the contents of \a other into this key, making the two keys
    identical.

    Returns a reference to this QSslKey.
*/
QSslKey &QSslKey::operator=(const QSslKey &other)
{
    d = other.d;
    return *this;
}

/*!
    \fn void QSslKey::swap(QSslKey &other)
    \since 5.0

    Swaps this ssl key with \a other. This function is very fast and
    never fails.
*/

/*!
    Returns \c true if this is a null key; otherwise false.

    \sa clear()
*/
bool QSslKey::isNull() const
{
    return d->isNull;
}

/*!
    Clears the contents of this key, making it a null key.

    \sa isNull()
*/
void QSslKey::clear()
{
    d = new QSslKeyPrivate;
}

/*!
    Returns the length of the key in bits, or -1 if the key is null.
*/
int QSslKey::length() const
{
    return d->length();
}

/*!
    Returns the type of the key (i.e., PublicKey or PrivateKey).
*/
QSsl::KeyType QSslKey::type() const
{
    return d->type;
}

/*!
    Returns the key algorithm.
*/
QSsl::KeyAlgorithm QSslKey::algorithm() const
{
    return d->algorithm;
}

/*!
  Returns the key in DER encoding.

  The \a passPhrase argument should be omitted as DER cannot be
  encrypted. It will be removed in a future version of Qt.
*/
QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
{
    if (d->isNull || d->algorithm == QSsl::Opaque)
        return QByteArray();

    // Encrypted DER is nonsense, see QTBUG-41038.
    if (d->type == QSsl::PrivateKey && !passPhrase.isEmpty())
        return QByteArray();

#ifndef QT_NO_OPENSSL
    QMap<QByteArray, QByteArray> headers;
    return d->derFromPem(toPem(passPhrase), &headers);
#else
    return d->derData;
#endif
}

/*!
  Returns the key in PEM encoding. The result is encrypted with
  \a passPhrase if the key is a private key and \a passPhrase is
  non-empty.
*/
QByteArray QSslKey::toPem(const QByteArray &passPhrase) const
{
    return d->toPem(passPhrase);
}

/*!
    Returns a pointer to the native key handle, if there is
    one, else \nullptr.

    You can use this handle together with the native API to access
    extended information about the key.

    \warning Use of this function has a high probability of being
    non-portable, and its return value may vary across platforms, and
    between minor Qt releases.
*/
Qt::HANDLE QSslKey::handle() const
{
    return d->handle();
}

/*!
    Returns \c true if this key is equal to \a other; otherwise returns \c false.
*/
bool QSslKey::operator==(const QSslKey &other) const
{
    if (isNull())
        return other.isNull();
    if (other.isNull())
        return isNull();
    if (algorithm() != other.algorithm())
        return false;
    if (type() != other.type())
        return false;
    if (length() != other.length())
        return false;
    if (algorithm() == QSsl::Opaque)
        return handle() == other.handle();
    return toDer() == other.toDer();
}

/*! \fn bool QSslKey::operator!=(const QSslKey &other) const

  Returns \c true if this key is not equal to key \a other; otherwise
  returns \c false.
*/

#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, const QSslKey &key)
{
    QDebugStateSaver saver(debug);
    debug.resetFormat().nospace();
    debug << "QSslKey("
          << (key.type() == QSsl::PublicKey ? "PublicKey" : "PrivateKey")
          << ", " << (key.algorithm() == QSsl::Opaque ? "OPAQUE" :
                     (key.algorithm() == QSsl::Rsa ? "RSA" :
                     (key.algorithm() == QSsl::Dsa ? "DSA" :
                     (key.algorithm() == QSsl::Dh ? "DH" : "EC"))))
          << ", " << key.length()
          << ')';
    return debug;
}
#endif

QT_END_NAMESPACE
