/* mbed Microcontroller Library
 * Copyright (c) 2018-2018 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef NRF5X_CRYPTO_
#define NRF5X_CRYPTO_

#include <stdint.h>

#if !defined(MBEDTLS_CONFIG_FILE)
#include "mbedtls/config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif

#if defined(MBEDTLS_ECDH_C)

#include "mbedtls/platform.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ecp.h"

#endif

#include "platform/NonCopyable.h"
#include "ble/BLETypes.h"

namespace ble {
namespace pal {
namespace vendor {
namespace nordic {

/**
 * Toolbox of cryptographic functions used in BLE.
 */
class CryptoToolbox : mbed::NonCopyable<CryptoToolbox> {

public:
    /**
     * Size of the Key used in lesc crypto operations.
     */
    static const ptrdiff_t lesc_key_size_ = public_key_coord_t::size_;

    /**
     * Size of an IRK.
     */
    static const ptrdiff_t irk_size_ = irk_t::size_;

    /**
     * Size of the hash generated by ah.
     */
    static const ptrdiff_t hash_size_ = 3;

    /**
     * Size of prand.
     */
    static const ptrdiff_t prand_size_ = 3;

#if defined(MBEDTLS_ECDH_C)

    /**
     * Create a new CryptoToolbox.
     */
    CryptoToolbox();

    /**
     * Destroy a CryptoTioolbox object.
     */
    ~CryptoToolbox();

    /**
     * Generate lesc public and private keys.
     * @param[out] X The component X of the public key.
     * @param[out] Y The component Y of the public key.
     * @param[out] secret The secret key.
     * @return true if the shared secret has been successfully generated and
     * false otherwise.
     */
    bool generate_keys(
        Span<uint8_t, lesc_key_size_> X,
        Span<uint8_t, lesc_key_size_> Y,
        Span<uint8_t, lesc_key_size_> secret
    );

    /**
     * Generate a shared secret from a peer public key and a local secret key.
     * @param[in] peer_X The component X of the peer public key.
     * @param[in] peer_Y The component Y of the peer public key.
     * @param[in] own_secret The local secret key.
     * @param[out] shared_secret The shared secret generated.
     * @return true if the shared secret has been successfully generated and
     * false otherwise.
     */
    bool generate_shared_secret(
        const Span<const uint8_t, lesc_key_size_>& peer_X,
        const Span<const uint8_t, lesc_key_size_>& peer_Y,
        const Span<const uint8_t, lesc_key_size_>& own_secret,
        Span<uint8_t, lesc_key_size_> shared_secret
    );

#endif

    /**
     * Execute the function ah. This function can be used to generate private
     * resolvable addresses and resolve them.
     *
     * @note all parameters passed and return by this fucntion are in little
     * endian.
     *
     * @param[in] irk The key used to create hash.
     * @param[in] prand The random part from which the hash will be generated.
     * @param[out] hash The hash generated.
     *
     * @return true in case of success and false otherwise.
     */
    static bool ah(
        const Span<const uint8_t, irk_size_>& irk,
        const Span<const uint8_t, prand_size_>& prand,
        Span<uint8_t, hash_size_> hash
    );

private:

#if defined(MBEDTLS_ECDH_C)
    void load_mpi(mbedtls_mpi& dest, const Span<const uint8_t, lesc_key_size_>& src);

    void store_mpi(Span<uint8_t, lesc_key_size_>& dest, const mbedtls_mpi& src);
#endif

    static void swap_endian(uint8_t* buf, size_t len);

#if defined(MBEDTLS_ECDH_C)
    bool _initialized;
    mbedtls_entropy_context _entropy_context;
    mbedtls_ecp_group _group;
#endif

};

} // nordic
} // vendor
} // pal
} // ble

#endif // NRF5X_CRYPTO_
