/*
 * BSD 2-Clause License
 *
 * Copyright (c) 2020, Agnieszka Cicha-Cisek
 * Copyright (c) 2020, Patryk Cisek
 * 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.
 *
 * 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
*/

#ifndef NITROKEYBASE_H
#define NITROKEYBASE_H

#include <cstdint>
#include <string>
#include <vector>

#include "totpslot.h"

/**
 * @brief A base class that represents Nitrokey abstraction
 *
 * An interface class that represents interface with the actual
 * USB key. Splitting into base and impl so that it's easy to
 * create a mock class for day-to-day development -- in case, you
 * for example work on GUI, you don't care about talking to the
 * actual key. Thus, it's going to be easy to drop-in a mock
 * implementation of this interface.
 */
class NitrokeyBase
{
public:
    /// Max index of TOTP slot (effectively number of TOTP slots in the USB key).
    static constexpr std::size_t MAX_SLOT_IDX = 15;

    /// Maximum length of TOTP slot name.
    static constexpr std::size_t MAX_SLOT_NAME_LENGHT = 15;

    /**
     * @brief NitrokeyBase Constructs instance of NitrokeyBase.
     */
    NitrokeyBase();

    /**
     * @brief Connects with the USB key.
     * @return true on success. false, otherwise.
     *
     * This method must be called first after creating instance of this class.
     */
    virtual bool connect() = 0;

    /**
     * @brief Checks, if connection to the USB key has been established.
     * @return true, if connected to the key. false, otherwise.
     */
    virtual bool isConnected() const = 0;

    /**
     * @brief Initial admin authentication.
     * @param adminPin Admin PIN (factory default is: "12345678")
     * @param tempPassword A password to be used for this session, after successful
     * authentication.
     * @return true if successfully authenticated. false, otherwise.
     *
     * This method will authenticate using Admin credentials. tempPassword is
     * supposed to be generated by the caller randomly and will serve as password
     * for the established and authenticated session.
     */
    virtual bool firstAuth(const std::string &adminPin, const std::string &tempPassword) = 0;

    /**
     * @brief Authenticates using regular user credentials
     * @param userPin (factory default is: "123456")
     * @param tempPassword A password to be used for this session, after successful
     * authentication.
     *
     * This method will authenticate using regular user credentials. tempPassword is
     * supposed to be generated by the caller randomly and will serve as password
     * for the established and authenticated session.
     */
    virtual void userAuth(const std::string &userPin, const std::string &tempPassword) = 0;

    /**
     * @brief Writes TOTP slot to the key.
     * @param slotNumber Slot to be used for storing secret. Valid values are: 0-15.
     * @param slotName Name of the slot. This name will be used in the GUI. Max length
     * of the name is 15 characters.
     * @param hexSecret HEX-encoded secret.
     * @param timeWindow Time window in seconds. Typically it's 30.
     * @param temporaryPassword A temporary password, previously set when calling
     * firstAuth(const std::string &adminPin, const std::string tempPassword).
     */
    virtual void writeTotpSlot(std::uint8_t slotNumber, const std::string &slotName,
                               const std::string &hexSecret, std::uint16_t timeWindow,
                               const std::string &temporaryPassword) = 0;

    /**
     * @brief Erases TOTP slot on the USB key.
     * @param slotNumber Index of the TOTP slot to be erased.
     * @param temporaryPassword A temporary password for Admin session.
     */
    virtual void eraseTotpSlot(std::uint8_t slotNumber, const std::string &temporaryPassword) = 0;

    /**
     * @brief Returns list of initiated TOTP slots.
     * @return Vector containing list of initiated slots.
     *
     * The resulting vector contains only those slots, that have been
     * written already.
     */
    virtual std::vector<TOTPSlot> getSlots() const = 0;

    /**
     * @brief Returns a name of a slot.
     * @param slot Slot number. Valid values: 0-15.
     * @return Name of a slot.
     */
    virtual std::string getSlotName(std::uint8_t slot) const = 0;

    /**
     * @brief Sets up clock on the USB key.
     * @param timestamp A Unix timestamp -- number of seconds since
     * Unix Epoch (Jan 1st, 1970).
     *
     * In order for Nitrokey to be able to generate TOTP code, the
     * key needs to know current time. The time needs to be accurate,
     * otherwise the code, that the key generates would be inaccurate.
     *
     * This method forces the time timestamp on the key.
     */
    virtual void setTime(std::uint64_t timestamp) const = 0;

    /**
     * @brief Sets up clock on the USB key. See full description for
     * explanation on the difference between this method and
     * setTime(std::uint64_t timestamp).
     *
     * @param timestamp A Unix timestamp -- number of seconds since
     * Unix Epoch (Jan 1st, 1970).
     *
     * Similar to
     * {@code setTime(std::uint64_t timestamp)}
     * but fails if time already set on the key is greater then
     * timestamp, or if the time previously set is 0.
     */
    virtual void setTimeSoft(std::uint64_t timestamp) const = 0;

    /**
     * @brief Requests Nitrokey to generate TOTP code for <em>slot</em>.
     * @param slot Slot to be used for generating TOTP code.
     * @param tempPassword Temporary password for the current session.
     * @return TOTP code for <em>slot</em>.
     */
    virtual std::string getTOTPCode(const TOTPSlot &slot,
                                    const std::string &tempPassword) const = 0;

    /**
     * @brief Returns max length of a slot name (15 characters).
     * @return Max length of a slot name (15 characters).
     */
    static constexpr std::size_t slotNameMaxLen() { return 0xf; }

    /**
     * @brief Returns max index of TOTP slot (15).
     * @return Max index of TOTP slot (15).
     */
    static constexpr std::size_t slotMaxIndex() { return 0xf; }
};

#endif // NITROKEYBASE_H
