
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
**     * Redistributions of source code must retain the above copyright
**       notice, this list of conditions and the following disclaimer.
**     * Redistributions in binary form must reproduce the above copyright
**       notice, this list of conditions and the following disclaimer in the
**       documentation and/or other materials provided with the distribution.
**     * Neither the name of the LibQxt project nor the
**       names of its contributors may be used to endorse or promote products
**       derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** <http://libqxt.org>  <foundation@libqxt.org>
*****************************************************************************/

#include "mailhmac.h"
#include <QtGlobal>

/*
\class QxtHmac

\inmodule QxtCore

\brief The QxtHmac class calculates keyed-Hash Message Authentication Codes

HMAC is a well-known algorithm for generating a message authentication code (MAC) that can be used to verify the
integrity and authenticity of a message.

This class requires Qt 4.3.0 or greater.

To verify a message, the sender creates a MAC using a key, which is a secret known only to the sender and recipient,
and the content of the message. This MAC is then sent along with the message. The recipient then creates another MAC
using the shared key and the content of the message. If the two codes match, the message is verified.

HMAC has been used as a password encryption scheme. The final output of the HMAC algorithm depends on the shared key
and an inner hash. This inner hash is generated from the message content and the key. To use HMAC as a password
scheme, the key should be the username; the message should be the user's password. The authenticating party (for
instance, a login server) only needs to store this inner hash generated by the innerHash() function. When requesting
authentication, the user calculates a HMAC using this key and message and sends his username and this HMAC to the
authenticator. The authenticator can then use verify() using the provided HMAC and the stored inner hash. When using
this scheme, the password is never stored or transmitted in plain text.
*/

#ifndef QXT_DOXYGEN_RUN
class QxtHmacPrivate
{
public:
    QxtHmacPrivate() : ohash(0), ihash(0) {}
    ~QxtHmacPrivate()
    {
        // deleting NULL is safe, so no tests are needed here
        delete ohash;
        delete ihash;
    }
    QCryptographicHash* ohash;
    QCryptographicHash* ihash;
    QByteArray opad, ipad, result;
    QCryptographicHash::Algorithm algorithm;
};
#endif

/*!
 * Constructs a QxtHmac object using the specified algorithm.
 */
QxtHmac::QxtHmac(QCryptographicHash::Algorithm algorithm)
    :d_ptr(new QxtHmacPrivate)
{
    d_ptr->ohash = new QCryptographicHash(algorithm);
    d_ptr->ihash = new QCryptographicHash(algorithm);
    d_ptr->algorithm = algorithm;
}

/*!
  Destructs the object.
*/
QxtHmac::~QxtHmac()
{

}

/*!
 * Sets the shared secret key for the message authentication code.
 *
 * Any data that had been processed using addData() will be discarded.
 */
void QxtHmac::setKey(QByteArray key)
{
    Q_D(QxtHmac);
    // We make the assumption that all hashes use a 512-bit block size; as of Qt 4.4.0 this is true of all supported hash functions
    d->opad = QByteArray(64, 0x5c);
    d->ipad = QByteArray(64, 0x36);
    if (key.size() > 64)
    {
        key = QCryptographicHash::hash(key, d->algorithm);
    }
    for (int i = key.size() - 1; i >= 0; --i)
    {
        d->opad[i] = d->opad[i] ^ key[i];
        d->ipad[i] = d->ipad[i] ^ key[i];
    }
    reset();
}

/*!
 * Resets the object.
 *
 * Any data that had been processed using addData() will be discarded.
 * The key, if set, will be preserved.
 */
void QxtHmac::reset()
{
    Q_D(QxtHmac);

    d->ihash->reset();
    d->ihash->addData(d->ipad);
}

/*!
 * Returns the inner hash of the HMAC function.
 *
 * This hash can be stored in lieu of the shared secret on the authenticating side
 * and used for verifying an HMAC code. When used in this manner, HMAC can be used
 * to provide a form of secure password authentication. See the documentation above
 * for details.
 */
QByteArray QxtHmac::innerHash() const
{
    return d_func()->ihash->result();
}

/*!
 * Returns the authentication code for the message.
 */
QByteArray QxtHmac::result()
{
    Q_D(QxtHmac);
    Q_ASSERT(d->opad.size());
    if (d->result.size())
        return d->result;
    d->ohash->reset();
    d->ohash->addData(d->opad);
    d->ohash->addData(innerHash());
    d->result = d->ohash->result();
    return d->result;
}

/*!
 * Verifies the authentication code against a known inner hash.
 *
 * \sa innerHash()
 */
bool QxtHmac::verify(const QByteArray& otherInner)
{
    Q_D(QxtHmac);
    result(); // populates d->result
    d->ohash->reset();
    d->ohash->addData(d->opad);
    d->ohash->addData(otherInner);
    return d->result == d->ohash->result();
}

/*!
 * Adds the provided data to the message to be authenticated.
 */
void QxtHmac::addData(const char* data, int length)
{
    Q_D(QxtHmac);
    Q_ASSERT(d->opad.size());
    d->ihash->addData(data, length);
    d->result.clear();
}

/*!
 * Adds the provided data to the message to be authenticated.
 */
void QxtHmac::addData(const QByteArray& data)
{
    addData(data.constData(), data.size());
}

/*!
 * Returns the HMAC of the provided data using the specified key and hashing algorithm.
 */
QByteArray QxtHmac::hash(const QByteArray& key, const QByteArray& data, Algorithm algorithm)
{
    QxtHmac hmac(algorithm);
    hmac.setKey(key);
    hmac.addData(data);
    return hmac.result();
}

/*!
 * Verifies a HMAC against a known key and inner hash using the specified hashing algorithm.
 */
bool QxtHmac::verify(const QByteArray& key, const QByteArray& hmac, const QByteArray& inner, Algorithm algorithm)
{
    QxtHmac calc(algorithm);
    calc.setKey(key);

    QxtHmacPrivate* d = calc.d_func();
    d->ohash->reset();
    d->ohash->addData(d->opad);
    d->ohash->addData(inner);
    return hmac == d->ohash->result();
}
