//-----------------------------------------------------------------------------
// Borrowed initially from https://github.com/nfc-tools/libfreefare
// Copyright (C) 2010, Romain Tartiere.
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------

#ifndef __DESFIRE_CRYPTO_H
#define __DESFIRE_CRYPTO_H

#include "common.h"
#include "mifare.h"
#include "desfire.h"
#include "mbedtls/aes.h"
#include "mbedtls/des.h"

/* Mifare DESFire EV1 Application crypto operations */
//#define APPLICATION_CRYPTO_DES    0x00
//#define APPLICATION_CRYPTO_3K3DES 0x40
//#define APPLICATION_CRYPTO_AES    0x80

typedef enum {
    MCD_SEND,
    MCD_RECEIVE
} MifareCryptoDirection;

typedef enum {
    MCO_ENCYPHER,
    MCO_DECYPHER
} MifareCryptoOperation;

#define MDCM_MASK 0x000F

#define CMAC_NONE 0

// Data send to the PICC is used to update the CMAC
#define CMAC_COMMAND 0x010
// Data received from the PICC is used to update the CMAC
#define CMAC_VERIFY  0x020

// MAC the command (when MDCM_MACED)
#define MAC_COMMAND 0x100
// The command returns a MAC to verify (when MDCM_MACED)
#define MAC_VERIFY  0x200

#define ENC_COMMAND 0x1000
#define NO_CRC      0x2000

#define MAC_MASK   0x0F0
#define CMAC_MACK  0xF00

/* Communication mode */
#define MDCM_PLAIN      0x00
#define MDCM_MACED      0x01
#define MDCM_ENCIPHERED 0x03

/* Error code managed by the library */
#define CRYPTO_ERROR            0x01

typedef enum {
    AS_LEGACY,
    AS_NEW
} DesfireAuthScheme;

/*
typedef enum {
    MDFT_STANDARD_DATA_FILE             = 0x00,
    MDFT_BACKUP_DATA_FILE               = 0x01,
    MDFT_VALUE_FILE_WITH_BACKUP         = 0x02,
    MDFT_LINEAR_RECORD_FILE_WITH_BACKUP = 0x03,
    MDFT_CYCLIC_RECORD_FILE_WITH_BACKUP = 0x04
} DesfireFileType;

typedef enum {
    OPERATION_OK                        = 0x00,
    NO_CHANGES                          = 0x0c,
    OUT_OF_EEPROM_ERROR                 = 0x0e,
    ILLEGAL_COMMAND_CODE                = 0x1c,
    INTEGRITY_ERROR                     = 0x1e,
    NO_SUCH_KEY                         = 0x40,
    LENGTH_ERROR                        = 0x7e,
    PERMISSION_DENIED                   = 0x9d,
    PARAMETER_ERROR                     = 0x9e,
    APPLICATION_NOT_FOUND               = 0xa0,
    APPL_INTEGRITY_ERROR                = 0xa1,
    AUTHENTICATION_ERROR                = 0xae,
    ADDITIONAL_FRAME                    = 0xaf,
    BOUNDARY_ERROR                      = 0xbe,
    PICC_INTEGRITY_ERROR                = 0xc1,
    COMMAND_ABORTED                     = 0xca,
    PICC_DISABLED_ERROR                 = 0xcd,
    COUNT_ERROR                         = 0xce,
    DUPLICATE_ERROR                     = 0xde,
    EEPROM_ERROR                        = 0xee,
    FILE_NOT_FOUND                      = 0xf0,
    FILE_INTEGRITY_ERROR                = 0xf1
} DesfireStatus;

typedef enum {
    CREATE_APPLICATION                  = 0xca,
    DELETE_APPLICATION                  = 0xda,
    GET_APPLICATION_IDS                 = 0x6a,
    SELECT_APPLICATION                  = 0x5a,
    FORMAT_PICC                         = 0xfc,
    GET_VERSION                         = 0x60,
    READ_DATA                           = 0xbd,
    WRITE_DATA                          = 0x3d,
    GET_VALUE                           = 0x6c,
    CREDIT                              = 0x0c,
    DEBIT                               = 0xdc,
    LIMITED_CREDIT                      = 0x1c,
    WRITE_RECORD                        = 0x3b,
    READ_RECORDS                        = 0xbb,
    CLEAR_RECORD_FILE                   = 0xeb,
    COMMIT_TRANSACTION                  = 0xc7,
    ABORT_TRANSACTION                   = 0xa7,
    GET_FREE_MEMORY                     = 0x6e,
    GET_FILE_IDS                        = 0x6f,
    GET_FILE_SETTINGS                   = 0xf5,
    GET_DF_NAMES                        = 0x6d,
    CHANGE_FILE_SETTINGS                = 0x5f,
    CREATE_STD_DATA_FILE                = 0xcd,
    CREATE_BACKUP_DATA_FILE             = 0xcb,
    CREATE_VALUE_FILE                   = 0xcc,
    CREATE_LINEAR_RECORD_FILE           = 0xc1,
    CREATE_CYCLIC_RECORD_FILE           = 0xc0,
    DELETE_FILE                         = 0xdf,
    AUTHENTICATE                        = 0x0a,  // AUTHENTICATE_NATIVE
    AUTHENTICATE_ISO                    = 0x1a,  // AUTHENTICATE_STANDARD
    AUTHENTICATE_AES                    = 0xaa,
    CHANGE_KEY_SETTINGS                 = 0x54,
    GET_KEY_SETTINGS                    = 0x45,
    CHANGE_KEY                          = 0xc4,
    GET_KEY_VERSION                     = 0x64,
    AUTHENTICATION_FRAME                = 0xAF
} DesfireCmd;
*/

#define DESFIRE_KEY(key) ((struct desfire_key *) key)
struct desfire_key {
    DesfireCryptoAlgorithm type;
    uint8_t data[24];
    uint8_t cmac_sk1[24];
    uint8_t cmac_sk2[24];
    uint8_t aes_version;
};
typedef struct desfire_key *desfirekey_t;

#define DESFIRE(tag) ((struct desfire_tag *) tag)
struct desfire_tag {
    iso14a_card_select_t info;
    int active;
    uint8_t last_picc_error;
    uint8_t last_internal_error;
    uint8_t last_pcd_error;
    desfirekey_t session_key;
    DesfireAuthScheme authentication_scheme;
    uint8_t authenticated_key_no;

    uint8_t ivect[DESFIRE_MAX_CRYPTO_BLOCK_SIZE];
    uint8_t cmac[16];
    uint8_t *crypto_buffer;
    size_t crypto_buffer_size;
    uint32_t selected_application;
};
typedef struct desfire_tag *desfiretag_t;
void des_encrypt(void *out, const void *in, const void *key);
void des_decrypt(void *out, const void *in, const void *key);
void tdes_nxp_receive(const void *in, void *out, size_t length, const void *key, unsigned char iv[8], int keymode);
void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, unsigned char iv[8], int keymode);

void aes128_nxp_receive(const void *in, void *out, size_t length, const void *key, unsigned char iv[16]);
void aes128_nxp_send(const void *in, void *out, size_t length, const void *key, unsigned char iv[16]);

void Desfire_des_key_new(const uint8_t value[8], desfirekey_t key);
void Desfire_3des_key_new(const uint8_t value[16], desfirekey_t key);
void Desfire_des_key_new_with_version(const uint8_t value[8], desfirekey_t key);
void Desfire_3des_key_new_with_version(const uint8_t value[16], desfirekey_t key);
void Desfire_3k3des_key_new(const uint8_t value[24], desfirekey_t key);
void Desfire_3k3des_key_new_with_version(const uint8_t value[24], desfirekey_t key);
void Desfire_2k3des_key_new_with_version(const uint8_t value[16], desfirekey_t key);
void Desfire_aes_key_new(const uint8_t value[16], desfirekey_t key);
void Desfire_aes_key_new_with_version(const uint8_t value[16], uint8_t version, desfirekey_t key);
uint8_t Desfire_key_get_version(desfirekey_t key);
void Desfire_key_set_version(desfirekey_t key, uint8_t version);
void Desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], desfirekey_t authkey, desfirekey_t key);

void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, size_t offset, int communication_settings);
void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes, int communication_settings);
void mifare_cypher_single_block(desfirekey_t  key, uint8_t *data, uint8_t *ivect, MifareCryptoDirection direction, MifareCryptoOperation operation, size_t block_size);
void mifare_cypher_blocks_chained(desfiretag_t tag, desfirekey_t key, uint8_t *ivect, uint8_t *data, size_t data_size, MifareCryptoDirection direction, MifareCryptoOperation operation);
size_t key_block_size(const desfirekey_t  key);
size_t padded_data_length(const size_t nbytes, const size_t block_size);
size_t maced_data_length(const desfirekey_t  key, const size_t nbytes);
size_t enciphered_data_length(const desfiretag_t tag, const size_t nbytes, int communication_settings);
void cmac_generate_subkeys(desfirekey_t key);
void cmac(const desfirekey_t  key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac);

#endif
