/*
 * Copyright (c) 2008-2015 Freescale Semiconductor, Inc.
 * Copyright 2016-2018 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#if !defined(_Keyblob_h_)
#define _Keyblob_h_

#include "smart_ptr.h"
#include "OptionContext.h"
#include "DataTarget.h"
#include "AESKey.h"
#include "AESCounter.h"

//! The names of the options.
#define kKeyblobOptionNameStart "start"
#define kKeyblobOptionNameEnd "end"
#define kKeyblobOptionNameKey "key"
#define kKeyblobOptionNameCounter "counter"
#define kKeyblobOptionNamedynamicCtrBase "dynamicCtrBase"

namespace elftosb
{
/*!
* @brief Keyblob specification.
*/
class Keyblob
{
public:
    typedef std::vector<OptionContext *> option_vector_t; //!< List of options entries.

    //! @brief Constants for encryption.
    enum _encryption_constants
    {
        k_encryptBlockSize = 16, //!< Block size for AES-128 encryption
        k_qspiAlignlength =
            512 //!< QSPI image alignment length, 512 is supposed to be the safe alignment level for any QSPI device
                //!<  this means that all QSPI images generated by this tool will be sizes of multiple 512
    };

public:
    Keyblob()
        : m_id(0)
        , m_options(0)
    {
    }
    Keyblob(uint32_t identifier)
        : m_id(identifier)
        , m_options(0)
    {
    }
    virtual ~Keyblob() {}
    static uint32_t getKekSizeBytes() { return kKekSizeBytes; }
    void setIdentifier(uint32_t identifier) { m_id = identifier; }
    uint32_t getIdentifier() const { return m_id; }
    //! \brief Add options.
    inline void addOptions(OptionContext *context) { m_options.push_back(context); }
    //! \brief Return options vector.
    inline option_vector_t *getOptions() { return &m_options; }
    //! \brief Create and wrap keyblob data based on options.
    //!
    //! \retval Pointer to new wrapped data (caller must delete)
    //! \retval Number of bytes in wrapped data
    uint8_t *createWrappedKeyblobData(uint8_t *kek, uint32_t *byteCount);

    //! \brief Encrypt for OTFAD if range is within a keyblob entry range.
    //!
    //! len must be a multiple of k_qspiAlignlength, which is also
    //! a multiple of k_encryptBlockSize.
    //!
    //! \retval True if data was encrypted (range matched)
    bool encryptMatchingRange(uint32_t start, uint32_t len, uint8_t *data);

protected:
    //! @brief Keywrap constants.
    enum _keywrap_constants
    {
        kKeySizeBits = 128,            //!< AES-128
        kExpandedKekSizeInts = 64,     //!< Expanded KeK size
        kNumKeyBlobs = 4,              //!< Number of key blobs
        kKeyBlobSizeBytes = 64,        //!< Key blob size in bytes
        kOutputLineSize = 16,          //!< Number of bytes per output line
        kRegionAddrMask = (0x400 - 1), //!< Region addresses are modulo 1024
        kFlagMask = 0x07,              //!< Key flags mask
        kKeyFlags = 0x03,              //!< Default flags: RO=0, ADE=1, VLD=1
        kAesKeySizeBytes = 16,         //!< Number of bytes in AES-128 key
        kCtrSizeBytes = 8,             //!< Number of bytes in Ctr
        kKekSizeBytes = 16,            //!< Number of bytes in KeK
        kNumKeyChars = 32,             //!< Number of characters in key string
        kNumCtrChars = 16,             //!< Number of characters in ctr string
        kCrc32SizeBytes = 32           //!< Number of bytes covered by CRC32
    };

    //! @brief Key Blob format.
    typedef struct KeyBlob
    {
        unsigned char key[kAesKeySizeBytes]; // 16 bytes, 128-bits, KEY[A15...A00]
        unsigned char ctr[kCtrSizeBytes];    // 8 bytes, 64-bits, CTR[C7...C0]
        unsigned int srtaddr;                // region start, STRADDR[31 - 10]
        unsigned int endaddr;                // region end, ENDADDR[31 - 10]
        // end of 32-byte area covered by CRC
        unsigned int zero_fill;      // zeros
        unsigned int key_blob_crc32; // crc32 over 1st 32-bytes
        // end of 40 byte (5*64-bit) key blob data
        unsigned char expanded_wrap_data[8]; // 8 bytes, used for wrap expanded data
        // end of 48 byte (6*64-bit) wrap data
        unsigned char unused_filler[16]; // unused fill to 64 bytes
    } keyblob_t;

    void populateKeyBlob(keyblob_t *blob, uint32_t start, uint32_t end, const char *keyHex, const char *counterHex, uint32_t dynamicCtrBase);
    void encrypt(uint32_t length, uint8_t *data, AESKey<128> *key, AESCounter<128> *counter);
    bool getOptionValues(OptionContext &opt, const char **key, const char **ctr, uint32_t *start, uint32_t *end, uint32_t *dynamicCtrBase = nullptr);

    uint32_t m_id;             //!< Unique identifier.
    option_vector_t m_options; //!< List of option entries.
};

}; // namespace elftosb

#endif // _Keyblob_h_
