/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */

/*
 * ----------------------------------------------------------------------------------
 * The code in this file is copied (with a slight modifications) from:
 *     Crypto.h
 * ----------------------------------------------------------------------------------
 * BearSSL Copyright (C) 2016 Thomas Pornin (pornin@bolet.org)
 * The rest of this file Copyright (C) 2019 Anders Löfgren
 *
 * MIT License
 *
 * 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.
 * ----------------------------------------------------------------------------------
 */


#ifndef ECXX_PLATFORM_NODEMCU_CRYPTO_H
#define ECXX_PLATFORM_NODEMCU_CRYPTO_H


#include "CoreLib.h"
#include "TypeConversion.h"


namespace eCxx {

namespace NodeMCU {

namespace Crypto {


constexpr uint8_t  ENCRYPTION_KEY_LENGTH = 32;
constexpr uint32_t CT_MAX_DIFF           = 1073741823; // (2 ^ 30) - 1


using NonceGeneratorType = eCxx::function<uint8_t*(uint8_t*, const size_t)>;


extern void setCtMinDataLength(const size_t ctMinDataLength);
extern size_t getCtMinDataLength();


extern void setCtMaxDataLength(const size_t ctMaxDataLength);
extern size_t getCtMaxDataLength();


extern void setNonceGenerator(NonceGeneratorType nonceGenerator);
extern NonceGeneratorType getNonceGenerator();


struct MD5 {
    static constexpr uint8_t NATURAL_LENGTH = 16;

    static void* hash(const void* data, const size_t dataLength, void* resultArray) __attribute__((deprecated));

    static const WString hash(const WString& message) __attribute__((deprecated));

    static void* hmac(const void* data, const size_t dataLength, const void* hashKey, const size_t hashKeyLength, void* resultArray, const size_t outputLength);

    static const WString hmac(const WString& message, const void* hashKey, const size_t hashKeyLength, const size_t hmacLength);

    static void* hmacCT(const void* data, const size_t dataLength, const void* hashKey, const size_t hashKeyLength, void* resultArray, const size_t outputLength);

    static const WString hmacCT(const WString& message, const void* hashKey, const size_t hashKeyLength, const size_t hmacLength);
};


struct SHA1 {
    static constexpr uint8_t NATURAL_LENGTH = 20;

    static void* hash(const void* data, const size_t dataLength, void* resultArray) __attribute__((deprecated));

    static const WString hash(const WString& message) __attribute__((deprecated));

    static void* hmac(const void* data, const size_t dataLength, const void* hashKey, const size_t hashKeyLength, void* resultArray, const size_t outputLength);

    static const WString hmac(const WString& message, const void* hashKey, const size_t hashKeyLength, const size_t hmacLength);

    static void* hmacCT(const void* data, const size_t dataLength, const void* hashKey, const size_t hashKeyLength, void* resultArray, const size_t outputLength);

    static const WString hmacCT(const WString& message, const void* hashKey, const size_t hashKeyLength, const size_t hmacLength);
};


struct SHA224 {
    static constexpr uint8_t NATURAL_LENGTH = 28;

    static void* hash(const void* data, const size_t dataLength, void* resultArray);

    static const WString hash(const WString& message);

    static void* hmac(const void* data, const size_t dataLength, const void* hashKey, const size_t hashKeyLength, void* resultArray, const size_t outputLength);

    static const WString hmac(const WString& message, const void* hashKey, const size_t hashKeyLength, const size_t hmacLength);

    static void* hmacCT(const void* data, const size_t dataLength, const void* hashKey, const size_t hashKeyLength, void* resultArray, const size_t outputLength);

    static const WString hmacCT(const WString& message, const void* hashKey, const size_t hashKeyLength, const size_t hmacLength);
};


struct SHA256 {
    static constexpr uint8_t NATURAL_LENGTH = 32;

    static void* hash(const void* data, const size_t dataLength, void* resultArray);

    static const WString hash(const WString& message);

    static void* hmac(const void* data, const size_t dataLength, const void* hashKey, const size_t hashKeyLength, void* resultArray, const size_t outputLength);

    static const WString hmac(const WString& message, const void* hashKey, const size_t hashKeyLength, const size_t hmacLength);

    static void* hmacCT(const void* data, const size_t dataLength, const void* hashKey, const size_t hashKeyLength, void* resultArray, const size_t outputLength);

    static const WString hmacCT(const WString& message, const void* hashKey, const size_t hashKeyLength, const size_t hmacLength);
};


struct SHA384 {
    static constexpr uint8_t NATURAL_LENGTH = 48;

    static void* hash(const void* data, const size_t dataLength, void* resultArray);

    static const WString hash(const WString& message);

    static void* hmac(const void* data, const size_t dataLength, const void* hashKey, const size_t hashKeyLength, void* resultArray, const size_t outputLength);

    static const WString hmac(const WString& message, const void* hashKey, const size_t hashKeyLength, const size_t hmacLength);

    static void* hmacCT(const void* data, const size_t dataLength, const void* hashKey, const size_t hashKeyLength, void* resultArray, const size_t outputLength);

    static const WString hmacCT(const WString& message, const void* hashKey, const size_t hashKeyLength, const size_t hmacLength);
};


struct SHA512 {
    static constexpr uint8_t NATURAL_LENGTH = 64;

    static void* hash(const void* data, const size_t dataLength, void* resultArray);

    static const WString hash(const WString& message);

    static void* hmac(const void* data, const size_t dataLength, const void* hashKey, const size_t hashKeyLength, void* resultArray, const size_t outputLength);

    static const WString hmac(const WString& message, const void* hashKey, const size_t hashKeyLength, const size_t hmacLength);

    static void* hmacCT(const void* data, const size_t dataLength, const void* hashKey, const size_t hashKeyLength, void* resultArray, const size_t outputLength);

    static const WString hmacCT(const WString& message, const void* hashKey, const size_t hashKeyLength, const size_t hmacLength);
};


struct MD5SHA1 {
    static constexpr uint8_t NATURAL_LENGTH = 36;

    static void* hash(const void* data, const size_t dataLength, void* resultArray);

    static const WString hash(const WString& message);
};


struct HKDF {
    HKDF(const void* keyMaterial, const size_t keyMaterialLength, const void* salt = nullptr, const size_t saltLength = 0);

    void init(const void* keyMaterial, const size_t keyMaterialLength, const void* salt = nullptr, const size_t saltLength = 0);

    size_t produce(void* resultArray, const size_t outputLength, const void* info = nullptr, size_t infoLength = 0);

private:
    ::br_hkdf_context hkdfContext;
};


struct ChaCha20Poly1305 {
    static void encrypt(void* data, const size_t dataLength, const void* key, const void* keySalt, const size_t keySaltLength, void* resultingNonce, void* resultingTag, const void* aad = nullptr, const size_t aadLength = 0);

    static bool decrypt(void* data, const size_t dataLength, const void* key, const void* keySalt, const size_t keySaltLength, const void* encryptionNonce, const void* encryptionTag, const void* aad = nullptr, const size_t aadLength = 0);
};


} // namespace Crypto

} // namespace NodeMCU

} // namespace eCxx


#endif
