//-----------------------------------------------------------------------------
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// Hitag2 emulation
//
// (c) 2009 Henryk Plötz <henryk@ploetzli.ch>
//-----------------------------------------------------------------------------
// Hitag2 complete rewrite of the code
// - Fixed modulation/encoding issues
// - Rewrote code for transponder emulation
// - Added sniffing of transponder communication
// - Added reader functionality
//
// (c) 2012 Roel Verdult
//-----------------------------------------------------------------------------
// Piwi, 2019
// Iceman, 2019
// Anon, 2019
// Doegox, 2020

#define DBG  if (DBGLEVEL >= DBG_EXTENDED)

#include "hitag2.h"
#include "hitag2_crypto.h"
#include "string.h"
#include "proxmark3_arm.h"
#include "cmd.h"
#include "BigBuf.h"
#include "fpgaloader.h"
#include "ticks.h"
#include "dbprint.h"
#include "util.h"
#include "lfadc.h"
#include "lfsampling.h"
#include "lfdemod.h"
#include "commonutil.h"


#define test_bit(data, i)  (*(data + (i/8)) >> (7-(i % 8))) & 1
#define set_bit(data, i)   *(data + (i/8)) |= (1 << (7-(i % 8)))
#define clear_bit(data, i) *(data + (i/8)) &= ~(1 << (7-(i % 8)))
#define flip_bit(data, i)  *(data + (i/8)) ^= (1 << (7-(i % 8)))

// Successful crypto auth
static bool bCrypto;
// Is in auth stage
static bool bAuthenticating;
// Successful password auth
bool bSelecting;
bool bCollision;
static bool bPwd;
static bool bSuccessful;

static struct hitag2_tag tag = {
    .state = TAG_STATE_RESET,
    .sectors = {                         // Password mode:               | Crypto mode:
        [0]  = { 0x02, 0x4e, 0x02, 0x20}, // UID                          | UID
        [1]  = { 0x4d, 0x49, 0x4b, 0x52}, // Password RWD                 | 32 bit LSB key
        [2]  = { 0x20, 0xf0, 0x4f, 0x4e}, // Reserved                     | 16 bit MSB key, 16 bit reserved
        [3]  = { 0x0e, 0xaa, 0x48, 0x54}, // Configuration, password TAG  | Configuration, password TAG
        [4]  = { 0x46, 0x5f, 0x4f, 0x4b}, // Data: F_OK
        [5]  = { 0x55, 0x55, 0x55, 0x55}, // Data: UUUU
        [6]  = { 0xaa, 0xaa, 0xaa, 0xaa}, // Data: ....
        [7]  = { 0x55, 0x55, 0x55, 0x55}, // Data: UUUU
        [8]  = { 0x00, 0x00, 0x00, 0x00}, // RSK Low
        [9]  = { 0x00, 0x00, 0x00, 0x00}, // RSK High
        [10] = { 0x00, 0x00, 0x00, 0x00}, // RCF
        [11] = { 0x00, 0x00, 0x00, 0x00}, // SYNC
        // up to index 15 reserved for HITAG1/HITAGS public data
    },
};

static enum {
    WRITE_STATE_START = 0x0,
    WRITE_STATE_PAGENUM_WRITTEN,
    WRITE_STATE_PROG
} writestate;

// ToDo: define a meaningful maximum size for auth_table. The bigger this is, the lower will be the available memory for traces.
// Historically it used to be FREE_BUFFER_SIZE, which was 2744.
#define AUTH_TABLE_LENGTH 2744
static uint8_t *auth_table;
static size_t auth_table_pos = 0;
static size_t auth_table_len = AUTH_TABLE_LENGTH;

static uint8_t password[4];
static uint8_t NrAr[8];
static uint8_t key[8];
static uint8_t writedata[4];
uint8_t logdata_0[4], logdata_1[4];
uint8_t nonce[4];
bool key_no;
static uint64_t cipher_state;

size_t blocknr;
size_t flipped_bit = 0;
uint32_t byte_value = 0;

static int hitag2_reset(void) {
    tag.state = TAG_STATE_RESET;
    tag.crypto_active = 0;
    return 0;
}

static int hitag2_init(void) {
    hitag2_reset();
    return 0;
}

// Sam7s has several timers, we will use the source TIMER_CLOCK1 (aka AT91C_TC_CLKS_TIMER_DIV1_CLOCK)
// TIMER_CLOCK1 = MCK/2, MCK is running at 48 MHz, Timer is running at 48/2 = 24 MHz
// Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier)
// T0 = TIMER_CLOCK1 / 125000 = 192
#ifndef HITAG_T0
#define HITAG_T0               192
#endif

#define HITAG_FRAME_LEN  20
#define HITAG_T_STOP     36 /* T_EOF should be > 36 */
#define HITAG_T_LOW      8  /* T_LOW should be 4..10 */
#define HITAG_T_0_MIN    15 /* T[0] should be 18..22 */
#define HITAG_T_0        20 /* T[0] should be 18..22 */
#define HITAG_T_1_MIN    25 /* T[1] should be 26..30 */
#define HITAG_T_1        30 /* T[1] should be 26..30 */
//#define HITAG_T_EOF      40 /* T_EOF should be > 36 */
#define HITAG_T_EOF      80 /* T_EOF should be > 36 */
#define HITAG_T_WAIT_1_MIN   199 /* T_wresp should be 199..206 */
#define HITAG_T_WAIT_2_MIN   90 /* T_wait2 should be at least 90 */
#define HITAG_T_WAIT_MAX 300 /* bit more than HITAG_T_WAIT_1 + HITAG_T_WAIT_2 */
#define HITAG_T_PROG     614

#define HITAG_T_TAG_ONE_HALF_PERIOD     10
#define HITAG_T_TAG_TWO_HALF_PERIOD     25
#define HITAG_T_TAG_THREE_HALF_PERIOD   41
#define HITAG_T_TAG_FOUR_HALF_PERIOD    57

#define HITAG_T_TAG_HALF_PERIOD         16
#define HITAG_T_TAG_FULL_PERIOD         32

#define HITAG_T_TAG_CAPTURE_ONE_HALF    13
#define HITAG_T_TAG_CAPTURE_TWO_HALF    25
#define HITAG_T_TAG_CAPTURE_THREE_HALF  41
#define HITAG_T_TAG_CAPTURE_FOUR_HALF   57

/*
// sim
static void hitag_send_bit(int bit) {
    LED_A_ON();

    // Reset clock for the next bit
    AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;

    // Fixed modulation, earlier proxmark version used inverted signal
    // check datasheet if reader uses BiPhase?
    if (bit == 0) {
        // Manchester: Unloaded, then loaded |__--|
        LOW(GPIO_SSC_DOUT);
        while (AT91C_BASE_TC0->TC_CV < HITAG_T0 * HITAG_T_TAG_HALF_PERIOD);
        HIGH(GPIO_SSC_DOUT);
        while (AT91C_BASE_TC0->TC_CV < HITAG_T0 * HITAG_T_TAG_FULL_PERIOD);
    } else {
        // Manchester: Loaded, then unloaded |--__|
        HIGH(GPIO_SSC_DOUT);
        while (AT91C_BASE_TC0->TC_CV < HITAG_T0 * HITAG_T_TAG_HALF_PERIOD);
        LOW(GPIO_SSC_DOUT);
        while (AT91C_BASE_TC0->TC_CV < HITAG_T0 * HITAG_T_TAG_FULL_PERIOD);
    }
    LED_A_OFF();
}

// sim
static void hitag_send_frame(const uint8_t *frame, size_t frame_len) {
    // SOF - send start of frame
    hitag_send_bit(1);
    hitag_send_bit(1);
    hitag_send_bit(1);
    hitag_send_bit(1);
    hitag_send_bit(1);

    // Send the content of the frame
    for (size_t i = 0; i < frame_len; i++) {
        hitag_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1);
    }

    // Drop the modulation
    LOW(GPIO_SSC_DOUT);
}
*/

// sim
static void hitag2_handle_reader_command(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen) {
    uint8_t rx_air[HITAG_FRAME_LEN];

    // Copy the (original) received frame how it is send over the air
    memcpy(rx_air, rx, nbytes(rxlen));

    if (tag.crypto_active) {
        hitag2_cipher_transcrypt(&(tag.cs), rx, rxlen / 8, rxlen % 8);
    }

    // Reset the transmission frame length
    *txlen = 0;

    // Try to find out which command was send by selecting on length (in bits)
    switch (rxlen) {
        // Received 11000 from the reader, request for UID, send UID
        case 05: {
            // Always send over the air in the clear plaintext mode
            if (rx_air[0] != 0xC0) {
                // Unknown frame ?
                return;
            }
            *txlen = 32;
            memcpy(tx, tag.sectors[0], 4);
            tag.crypto_active = 0;
        }
        break;

        // Read/Write command: ..xx x..y  yy with yyy == ~xxx, xxx is sector number
        case 10: {
            uint16_t sector = (~(((rx[0] << 2) & 0x04) | ((rx[1] >> 6) & 0x03)) & 0x07);

            // Verify complement of sector index
            if (sector != ((rx[0] >> 3) & 0x07)) {
                DbpString("Transmission error (read/write)");
                return;
            }

            switch (rx[0] & 0xC6) {
                // Read command: 11xx x00y
                case 0xC0: {
                    memcpy(tx, tag.sectors[sector], 4);
                    *txlen = 32;
                    break;
                }
                // Inverted Read command: 01xx x10y
                case 0x44: {
                    for (size_t i = 0; i < 4; i++) {
                        tx[i] = tag.sectors[sector][i] ^ 0xff;
                    }
                    *txlen = 32;
                    break;
                }
                // Write command: 10xx x01y
                case 0x82: {
                    // Prepare write, acknowledge by repeating command
                    memcpy(tx, rx, nbytes(rxlen));
                    *txlen = rxlen;
                    tag.active_sector = sector;
                    tag.state = TAG_STATE_WRITING;
                    break;
                }
                // Unknown command
                default: {
                    Dbprintf("Unknown command: %02x %02x", rx[0], rx[1]);
                    return;
                }
            }
        }
        break;

        // Writing data or Reader password
        case 32: {
            if (tag.state == TAG_STATE_WRITING) {
                // These are the sector contents to be written. We don't have to do anything else.
                memcpy(tag.sectors[tag.active_sector], rx, nbytes(rxlen));
                tag.state = TAG_STATE_RESET;
                return;
            } else {
                // Received RWD password, respond with configuration and our password
                if (memcmp(rx, tag.sectors[1], 4) != 0) {
                    DbpString("Reader password is wrong");
                    return;
                }
                *txlen = 32;
                memcpy(tx, tag.sectors[3], 4);
            }
        }
        break;

        // Received RWD authentication challenge and respnse
        case 64: {
            // Store the authentication attempt
            if (auth_table_len < (AUTH_TABLE_LENGTH - 8)) {
                memcpy(auth_table + auth_table_len, rx, 8);
                auth_table_len += 8;
            }

            // Reset the cipher state
            hitag2_cipher_reset(&tag, rx);

            // Check if the authentication was correct
            if (!hitag2_cipher_authenticate(&(tag.cs), rx + 4)) {
                // The reader failed to authenticate, do nothing
                Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x Failed!", rx[0], rx[1], rx[2], rx[3], rx[4], rx[5], rx[6], rx[7]);
                return;
            }
            // Activate encryption algorithm for all further communication
            tag.crypto_active = 1;

            // Use the tag password as response
            memcpy(tx, tag.sectors[3], 4);
            *txlen = 32;
        }
        break;
    }

    // LogTrace(rx, nbytes(rxlen), 0, 0, NULL, false);
    // LogTrace(tx, nbytes(txlen), 0, 0, NULL, true);

    if (tag.crypto_active) {
        hitag2_cipher_transcrypt(&(tag.cs), tx, *txlen / 8, *txlen % 8);
    }
}

// reader/writer
// returns how long it took
static uint32_t hitag_reader_send_bit(int bit) {
    uint32_t wait = 0;
    LED_A_ON();
    // Binary pulse length modulation (BPLM) is used to encode the data stream
    // This means that a transmission of a one takes longer than that of a zero

    // Enable modulation, which means, drop the field
    lf_modulation(true);

    // Wait for 4-10 times the carrier period
    lf_wait_periods(8); // wait for 4-10 times the carrier period
    wait += 8;

    // Disable modulation, just activates the field again
    lf_modulation(false);

    if (bit == 0) {
        // Zero bit: |_-|
        lf_wait_periods(HITAG_T_0 - HITAG_T_LOW); // wait for 18-22 times the carrier period
        wait += HITAG_T_0 - HITAG_T_LOW;
    } else {
        // One bit: |_--|
        lf_wait_periods(HITAG_T_1 - HITAG_T_LOW); // wait for 26-32 times the carrier period
        wait += HITAG_T_1 - HITAG_T_LOW;
    }

    LED_A_OFF();
    return wait;
}

// reader / writer commands
static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len) {

    uint32_t wait = 0;
    // Send the content of the frame
    for (size_t i = 0; i < frame_len; i++) {
        wait += hitag_reader_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1);
    }

    // Enable modulation, which means, drop the field
    lf_modulation(true);

    // Wait for 4-10 times the carrier period
    lf_wait_periods(HITAG_T_LOW);
    wait += HITAG_T_LOW;

    // Disable modulation, just activates the field again
    lf_modulation(false);

    // t_stop, high field for stop condition (> 36)
    lf_wait_periods(HITAG_T_STOP);
    wait += HITAG_T_STOP;

    return wait;
}

uint8_t hitag_crc(uint8_t *data, size_t length) {
    uint8_t crc = 0xff;
    unsigned int byte, bit;
    for (byte = 0; byte < ((length + 7) / 8); byte++) {
        crc ^= *(data + byte);
        bit = length < (8 * (byte + 1)) ? (length % 8) : 8;
        while (bit--) {
            if (crc & 0x80) {
                crc <<= 1;
                crc ^= 0x1d;
            } else {
                crc <<= 1;
            }
        }
    }
    return crc;
}

/*
void fix_ac_decoding(uint8_t *input, size_t len) {
    // Reader routine tries to decode AC data after Manchester decoding
    // AC has double the bitrate, extract data from bit-pairs
    uint8_t temp[len / 16];
    memset(temp, 0, sizeof(temp));

    for (size_t i = 1; i < len; i += 2) {
        if (test_bit(input, i) && test_bit(input, (i + 1))) {
            set_bit(temp, (i / 2));
        }
    }
    memcpy(input, temp, sizeof(temp));
}
*/


// looks at number of received bits.  
// 0 = collision?
// 32 =  good response
bool hitag_plain(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen, bool hitag_s) {
    uint8_t crc;
    *txlen = 0;
    switch (rxlen) {
        case 0: {
            // retry waking up card
            /*tx[0] = 0xb0; // Rev 3.0*/
            tx[0] = 0x30; // Rev 2.0
            *txlen = 5;
            if (!bCollision) blocknr--;
            if (blocknr < 0) {
                blocknr = 0;
            }
            if (!hitag_s) {
                if (blocknr > 1 && blocknr < 31) {
                    blocknr = 31;
                }
            }
            bCollision = true;
            return true;
        }
        case 32: {
            if (bCollision) {
                // Select card by serial from response
                tx[0] = 0x00 | rx[0] >> 5;
                tx[1] = rx[0] << 3 | rx[1] >> 5;
                tx[2] = rx[1] << 3 | rx[2] >> 5;
                tx[3] = rx[2] << 3 | rx[3] >> 5;
                tx[4] = rx[3] << 3;
                crc = hitag_crc(tx, 37);
                tx[4] |= crc >> 5;
                tx[5] = crc << 3;
                *txlen = 45;
                bCollision = false;
            } else {
                memcpy(tag.sectors[blocknr], rx, 4);
                blocknr++;
                if (!hitag_s) {
                    if (blocknr > 1 && blocknr < 31) {
                        blocknr = 31;
                    }
                }
                if (blocknr > 63) {
                    DbpString("Read succesful!");
                    *txlen = 0;
                    bSuccessful = true;
                    return false;
                }
                // read next page of card until done
                Dbprintf("Reading page %02u", blocknr);
                tx[0] = 0xc0 | blocknr >> 4; // RDPPAGE
                tx[1] = blocknr << 4;
                crc = hitag_crc(tx, 12);
                tx[1] |= crc >> 4;
                tx[2] = crc << 4;
                *txlen = 20;
            }
        }
        break;
        default: {
            Dbprintf("Uknown frame length: %d", rxlen);
            return false;
        }
        break;
    }
    return true;
}


bool hitag1_authenticate(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen) {
    uint8_t crc;
    *txlen = 0;
    switch (rxlen) {
        case 0: {
            // retry waking up card
            /*tx[0] = 0xb0; // Rev 3.0*/
            tx[0] = 0x30; // Rev 2.0
            *txlen = 5;
            if (bCrypto && byte_value <= 0xff) {
                // to retry
                bCrypto = false;
            }
            if (!bCollision) blocknr--;
            if (blocknr < 0) {
                blocknr = 0;
            }
            bCollision = true;
            // will receive 32-bit UID
        }
        break;
        case 2: {
            if (bAuthenticating) {
                // received Auth init ACK, send nonce
                // TODO Roel, bit-manipulation goes here
                /*nonce[0] = 0x2d;*/
                /*nonce[1] = 0x74;*/
                /*nonce[2] = 0x80;*/
                /*nonce[3] = 0xa5;*/
                nonce[0] = byte_value;
                byte_value++;
                /*set_bit(nonce,flipped_bit);*/
                memcpy(tx, nonce, 4);
                *txlen = 32;
                // will receive 32 bit encrypted Logdata
            } else if (bCrypto) {
                // authed, start reading
                tx[0] = 0xe0 | blocknr >> 4; // RDCPAGE
                tx[1] = blocknr << 4;
                crc = hitag_crc(tx, 12);
                tx[1] |= crc >> 4;
                tx[2] = crc << 4;
                *txlen = 20;
                // will receive 32-bit encrypted page
            }
        }
        break;
        case 32: {
            if (bCollision) {
                // Select card by serial from response
                tx[0] = 0x00 | rx[0] >> 5;
                tx[1] = rx[0] << 3 | rx[1] >> 5;
                tx[2] = rx[1] << 3 | rx[2] >> 5;
                tx[3] = rx[2] << 3 | rx[3] >> 5;
                tx[4] = rx[3] << 3;
                crc = hitag_crc(tx, 37);
                tx[4] |= crc >> 5;
                tx[5] = crc << 3;
                *txlen = 45;
                bCollision = false;
                bSelecting = true;
                // will receive 32-bit configuration page
            } else if (bSelecting) {
                // Initiate auth
                tx[0] = 0xa0 | key_no >> 4; // WRCPAGE
                tx[1] = blocknr << 4;
                crc = hitag_crc(tx, 12);
                tx[1] |= crc >> 4;
                tx[2] = crc << 4;
                *txlen = 20;
                bSelecting = false;
                bAuthenticating = true;
                // will receive 2-bit ACK
            } else if (bAuthenticating) {
                // received 32-bit logdata 0
                // TODO decrypt logdata 0, verify against logdata_0
                memcpy(tag.sectors[0], rx, 4);
                memcpy(tag.sectors[1], tx, 4);
                Dbprintf("%02x%02x%02x%02x %02x%02x%02x%02x", rx[0], rx[1], rx[2], rx[3], tx[0], tx[1], tx[2], tx[3]);
                // TODO replace with secret data stream
                // TODO encrypt logdata_1
                memcpy(tx, logdata_1, 4);
                *txlen = 32;
                bAuthenticating = false;
                bCrypto = true;
                // will receive 2-bit ACK
            } else if (bCrypto) {
                // received 32-bit encrypted page
                // TODO decrypt rx
                memcpy(tag.sectors[blocknr], rx, 4);
                blocknr++;
                if (blocknr > 63) {
                    DbpString("Read succesful!");
                    bSuccessful = true;
                    return false;
                }

                // TEST
                Dbprintf("Succesfully authenticated with logdata:");
                Dbhexdump(4, logdata_1, false);
                bSuccessful = true;
                return false;

                // read next page of card until done
                tx[0] = 0xe0 | blocknr >> 4; // RDCPAGE
                tx[1] = blocknr << 4;
                crc = hitag_crc(tx, 12);
                tx[1] |= crc >> 4;
                tx[2] = crc << 4;
                *txlen = 20;
            }
        }
        break;
        default: {
            Dbprintf("Uknown frame length: %d", rxlen);
            return false;
        }
        break;
    }

    return true;
}

//-----------------------------------------------------------------------------
// Hitag2 operations
//-----------------------------------------------------------------------------

static bool hitag2_write_page(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen) {
    switch (writestate) {
        case WRITE_STATE_START:
            *txlen = 10;
            tx[0] = 0x82 | (blocknr << 3) | ((blocknr ^ 7) >> 2);
            tx[1] = ((blocknr ^ 7) << 6);
            writestate = WRITE_STATE_PAGENUM_WRITTEN;
            break;
        case WRITE_STATE_PAGENUM_WRITTEN:
            // Check if page number was received correctly
            if ((rxlen == 10)
                    && (rx[0] == (0x82 | (blocknr << 3) | ((blocknr ^ 7) >> 2)))
                    && (rx[1] == (((blocknr & 0x3) ^ 0x3) << 6))) {

                *txlen = 32;
                memset(tx, 0, HITAG_FRAME_LEN);
                memcpy(tx, writedata, 4);
                writestate = WRITE_STATE_PROG;
            } else {
                Dbprintf("hitag2_write_page: Page number was not received correctly: rxlen=%d rx=%02x%02x%02x%02x",
                         rxlen, rx[0], rx[1], rx[2], rx[3]);
                bSuccessful = false;
                return false;
            }
            break;
        case WRITE_STATE_PROG:
            if (rxlen == 0) {
                bSuccessful = true;
            } else {
                bSuccessful = false;
                Dbprintf("hitag2_write_page: unexpected rx data (%d) after page write", rxlen);
            }
            return false;
        default:
            DbpString("hitag2_write_page: Unknown state %d");
            bSuccessful = false;
            return false;
    }

    return true;
}

static bool hitag2_password(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen, bool write) {
    // Reset the transmission frame length
    *txlen = 0;

    if (bPwd && !bAuthenticating && write) {
        if (!hitag2_write_page(rx, rxlen, tx, txlen)) {
            return false;
        }
    } else {
        // Try to find out which command was send by selecting on length (in bits)
        switch (rxlen) {
            // No answer, try to resurrect
            case 0: {
                // Stop if there is no answer (after sending password)
                if (bPwd) {
                    DbpString("Password failed!");
                    return false;
                }
                *txlen = 5;
                memcpy(tx, "\xC0", nbytes(*txlen));
            }
            break;

            // Received UID, tag password
            case 32: {
                // stage 1, got UID
                if (!bPwd) {
                    bPwd = true;
                    bAuthenticating = true;
                    memcpy(tx, password, 4);
                    *txlen = 32;
                } else {
                    // stage 2, got config byte+password TAG, discard as will read later
                    if (bAuthenticating) {
                        bAuthenticating = false;
                        if (write) {
                            if (!hitag2_write_page(rx, rxlen, tx, txlen)) {
                                return false;
                            }
                            break;
                        }
                    }
                    // stage 2+, got data block
                    else {
                        memcpy(tag.sectors[blocknr], rx, 4);
                        blocknr++;
                    }

                    if (blocknr > 7) {
                        bSuccessful = true;
                        return false;
                    }

                    *txlen = 10;
                    tx[0] = 0xC0 | (blocknr << 3) | ((blocknr ^ 7) >> 2);
                    tx[1] = ((blocknr ^ 7) << 6);
                }
            }
            break;

            // Unexpected response
            default: {
                Dbprintf("Unknown frame length: %d", rxlen);
                return false;
            }
            break;
        }
    }

    return true;
}

static bool hitag2_crypto(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen, bool write) {
    // Reset the transmission frame length
    *txlen = 0;

    if (bCrypto) {
        hitag2_cipher_transcrypt(&cipher_state, rx, rxlen / 8, rxlen % 8);
    }

    if (bCrypto && !bAuthenticating && write) {
        if (!hitag2_write_page(rx, rxlen, tx, txlen)) {
            return false;
        }
    } else {

        // Try to find out which command was send by selecting on length (in bits)
        switch (rxlen) {
            // No answer, try to resurrect
            case 0: {
                // Stop if there is no answer while we are in crypto mode (after sending NrAr)
                if (bCrypto) {
                    // Failed during authentication
                    if (bAuthenticating) {
                        DbpString("Authentication failed!");
                        return false;
                    } else {
                        // Failed reading a block, could be (read/write) locked, skip block and re-authenticate
                        if (blocknr == 1) {
                            // Write the low part of the key in memory
                            memcpy(tag.sectors[1], key + 2, 4);
                        } else if (blocknr == 2) {
                            // Write the high part of the key in memory
                            tag.sectors[2][0] = 0x00;
                            tag.sectors[2][1] = 0x00;
                            tag.sectors[2][2] = key[0];
                            tag.sectors[2][3] = key[1];
                        } else {
                            // Just put zero's in the memory (of the unreadable block)
                            memset(tag.sectors[blocknr], 0x00, 4);
                        }
                        blocknr++;
                        bCrypto = false;
                    }
                } else {
                    *txlen = 5;
                    memcpy(tx, "\xc0", nbytes(*txlen));
                }
                break;
            }
            // Received UID, crypto tag answer
            case 32: {
                // stage 1, got UID
                if (!bCrypto) {
                    uint64_t ui64key = key[0] | ((uint64_t)key[1]) << 8 | ((uint64_t)key[2]) << 16 | ((uint64_t)key[3]) << 24 | ((uint64_t)key[4]) << 32 | ((uint64_t)key[5]) << 40;
                    uint32_t ui32uid = rx[0] | ((uint32_t)rx[1]) << 8 | ((uint32_t)rx[2]) << 16 | ((uint32_t)rx[3]) << 24;
                    Dbprintf("hitag2_crypto: key=0x%x%x uid=0x%x", (uint32_t)((REV64(ui64key)) >> 32), (uint32_t)((REV64(ui64key)) & 0xffffffff), REV32(ui32uid));
                    cipher_state = _hitag2_init(REV64(ui64key), REV32(ui32uid), 0);
                    // PRN
                    memset(tx, 0x00, 4);
                    // Secret data
                    memset(tx + 4, 0xff, 4);
                    hitag2_cipher_transcrypt(&cipher_state, tx + 4, 4, 0);
                    *txlen = 64;
                    bCrypto = true;
                    bAuthenticating = true;
                } else {
                    // stage 2, got config byte+password TAG, discard as will read later
                    if (bAuthenticating) {
                        bAuthenticating = false;
                        if (write) {
                            if (!hitag2_write_page(rx, rxlen, tx, txlen)) {
                                return false;
                            }
                            break;
                        }
                    }
                    // stage 2+, got data block
                    else {
                        // Store the received block
                        memcpy(tag.sectors[blocknr], rx, 4);
                        blocknr++;
                    }
                    if (blocknr > 7) {
                        DbpString("Read successful!");
                        bSuccessful = true;
                        return false;
                    } else {
                        *txlen = 10;
                        tx[0] = 0xc0 | (blocknr << 3) | ((blocknr ^ 7) >> 2);
                        tx[1] = ((blocknr ^ 7) << 6);
                    }
                }
            }
            break;

            // Unexpected response
            default: {
                Dbprintf("Unknown frame length: %d", rxlen);
                return false;
            }
            break;
        }
    }

    if (bCrypto) {
        // We have to return now to avoid double encryption
        if (!bAuthenticating) {
            hitag2_cipher_transcrypt(&cipher_state, tx, *txlen / 8, *txlen % 8);
        }
    }

    return true;
}

static bool hitag2_authenticate(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen) {
    // Reset the transmission frame length
    *txlen = 0;

    // Try to find out which command was send by selecting on length (in bits)
    switch (rxlen) {
        // No answer, try to resurrect
        case 0: {
            // Stop if there is no answer while we are in crypto mode (after sending NrAr)
            if (bCrypto) {
                DbpString("Authentication failed!");
                return false;
            }
            *txlen = 5;
            memcpy(tx, "\xC0", nbytes(*txlen));
        }
        break;

        // Received UID, crypto tag answer
        case 32: {
            if (!bCrypto) {
                *txlen = 64;
                memcpy(tx, NrAr, 8);
                bCrypto = true;
            } else {
                DbpString("Authentication successful!");
                return true;
            }
        }
        break;

        // Unexpected response
        default: {
            Dbprintf("Unknown frame length: %d", rxlen);
            return false;
        }
        break;
    }

    return true;
}

static bool hitag2_test_auth_attempts(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen) {

    // Reset the transmission frame length
    *txlen = 0;

    // Try to find out which command was send by selecting on length (in bits)
    switch (rxlen) {
        // No answer, try to resurrect
        case 0: {
            // Stop if there is no answer while we are in crypto mode (after sending NrAr)
            if (bCrypto) {
                Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x Failed, removed entry!", NrAr[0], NrAr[1], NrAr[2], NrAr[3], NrAr[4], NrAr[5], NrAr[6], NrAr[7]);

                // Removing failed entry from authentiations table
                memcpy(auth_table + auth_table_pos, auth_table + auth_table_pos + 8, 8);
                auth_table_len -= 8;

                // Return if we reached the end of the authentications table
                bCrypto = false;
                if (auth_table_pos == auth_table_len) {
                    return false;
                }

                // Copy the next authentication attempt in row (at the same position, b/c we removed last failed entry)
                memcpy(NrAr, auth_table + auth_table_pos, 8);
            }
            *txlen = 5;
            memcpy(tx, "\xc0", nbytes(*txlen));
        }
        break;

        // Received UID, crypto tag answer, or read block response
        case 32: {
            if (!bCrypto) {
                *txlen = 64;
                memcpy(tx, NrAr, 8);
                bCrypto = true;
            } else {
                Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x OK", NrAr[0], NrAr[1], NrAr[2], NrAr[3], NrAr[4], NrAr[5], NrAr[6], NrAr[7]);
                bCrypto = false;
                if ((auth_table_pos + 8) == auth_table_len) {
                    return false;
                }
                auth_table_pos += 8;
                memcpy(NrAr, auth_table + auth_table_pos, 8);
            }
        }
        break;

        default: {
            Dbprintf("Unknown frame length: %d", rxlen);
            return false;
        }
        break;
    }

    return true;
}

static bool hitag2_read_uid(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen) {
    // Reset the transmission frame length
    *txlen = 0;

    // Try to find out which command was send by selecting on length (in bits)
    switch (rxlen) {
        // No answer, try to resurrect
        case 0: {
            // Just starting or if there is no answer
            *txlen = 5;
            memcpy(tx, "\xC0", nbytes(*txlen));
        }
        break;
        // Received UID
        case 32: {
            // Check if we received answer tag (at)
            if (bAuthenticating) {
                bAuthenticating = false;
            } else {
                // Store the received block
                memcpy(tag.sectors[blocknr], rx, 4);
                blocknr++;

                DBG Dbhexdump(4, rx, false);
            }
            if (blocknr > 0) {
                DBG DbpString("Read successful!");
                bSuccessful = true;
                return true;
            }
        }
        break;
        // Unexpected response
        default: {
            DBG Dbprintf("Unknown frame length: %d", rxlen);
            return false;
        }
        break;
    }
    return true;
}

// Hitag2 Sniffing
void SniffHitag2(void) {

    BigBuf_free();
    BigBuf_Clear_ext(false);
    clear_trace();
    set_tracing(true);

    auth_table_len = 0;
    auth_table_pos = 0;

    auth_table = (uint8_t *)BigBuf_malloc(AUTH_TABLE_LENGTH);
    memset(auth_table, 0x00, AUTH_TABLE_LENGTH);

    DbpString("Starting Hitag2 sniffing");
    LED_D_ON();

    lf_init(false, false);

    logging = false;

    size_t periods = 0;
    uint8_t periods_bytes[4];

    int16_t checked = 0;

    /*bool waiting_for_first_edge = true;*/
    LED_C_ON();

    while (!BUTTON_PRESS()) {

        WDT_HIT();

        // only every 1000th times, in order to save time when collecting samples.
        if (checked == 1000) {
            if (data_available()) {
                checked = -1;
                break;
            } else {
                checked = 0;
            }
        }
        ++checked;


        // Receive frame, watch for at most T0*EOF periods
//        lf_reset_counter();

        // Wait "infinite" for reader modulation
        periods = lf_detect_gap(20000);

        // Test if we detected the first reader modulation edge
        if (periods != 0) {
            if (logging == false) {
                logging = true;
                LED_D_ON();
            }
        }

        /*lf_count_edge_periods(10000);*/
        while ((periods = lf_detect_gap(64)) != 0) {
            num_to_bytes(periods, 4, periods_bytes);
            LogTrace(periods_bytes, 4, 0, 0, NULL, true);
        }

    }

    lf_finalize();

    DbpString("Hitag2 sniffing finish. Use `lf hitag list` for annotations");
}

// Hitag2 simulation
void SimulateHitag2(bool tag_mem_supplied, uint8_t *data) {

    BigBuf_free();
    BigBuf_Clear_ext(false);
    clear_trace();
    set_tracing(true);

    // empties bigbuff etc
    lf_init(false, true);

    int response = 0;
    uint8_t rx[HITAG_FRAME_LEN];
    size_t rxlen = 0;
    uint8_t tx[HITAG_FRAME_LEN];
    size_t txlen = 0;

    auth_table_len = 0;
    auth_table_pos = 0;
//    auth_table = BigBuf_malloc(AUTH_TABLE_LENGTH);
//    memset(auth_table, 0x00, AUTH_TABLE_LENGTH);

    // Reset the received frame, frame count and timing info
//    memset(rx, 0x00, sizeof(rx));
//    memset(tx, 0x00, sizeof(tx));

    DbpString("Starting Hitag2 simulation");

    // hitag2 state machine?
    hitag2_init();

    // copy user supplied emulation data
    if (tag_mem_supplied) {
        DbpString("Loading hitag2 memory...");
        memcpy((uint8_t *)tag.sectors, data, 48);
    }

    // printing
    uint32_t block = 0;
    for (size_t i = 0; i < 12; i++) {

       // num2bytes?
        for (size_t j = 0; j < 4; j++) {
            block <<= 8;
            block |= tag.sectors[i][j];
        }
        Dbprintf("| %d | %08x |", i, block);
    }

    uint8_t reader_modulation;
    size_t max_nrzs = 8 * HITAG_FRAME_LEN + 5;
    uint8_t nrz_samples[max_nrzs];
    size_t nrzs = 0, periods = 0;

//    uint32_t command_start = 0, command_duration = 0;
  //  int16_t checked = 0;

// SIMULATE

    while (!BUTTON_PRESS()) {

        LED_D_ON();
    
//        lf_reset_counter();
        LED_A_OFF();
        WDT_HIT();

/*
        // only every 1000th times, in order to save time when collecting samples.
        if (checked == 100) {
            if (data_available()) {
                checked = -1;
                break;
            } else {
                checked = 0;
            }
        }
        ++checked;
*/

        rxlen = 0;

        // Keep administration of the first edge detection
        bool waiting_for_first_edge = true;

        // Did we detected any modulaiton at all
        bool detected_modulation = false;

        // Use the current modulation state as starting point
        reader_modulation = lf_get_reader_modulation();

        // Receive frame, watch for at most max_nrzs periods
        // Reset the number of NRZ samples and use edge detection to detect them
        nrzs = 0;
        while (nrzs < max_nrzs) {
            // Get the timing of the next edge in number of wave periods
            periods = lf_count_edge_periods(128);

            // Just break out of loop after an initial time-out (tag is probably not available)
            // The function lf_count_edge_periods() returns 0 when a time-out occurs
            if (periods == 0) {
                break;
            }

            LED_A_ON();

            // Are we dealing with the first incoming edge
            if (waiting_for_first_edge) {

                // Register the number of periods that have passed
                response = periods;

                // Indicate that we have dealt with the first edge
                waiting_for_first_edge = false;

                // The first edge is always a single NRZ bit, force periods on 16
                periods = 16;

                // We have received more than 0 periods, so we have detected a tag response
                detected_modulation = true;
            }

            // Evaluate the number of periods before the next edge
            if (periods > 24 && periods <= 64) {
                // Detected two sequential equal bits and a modulation switch
                // NRZ modulation: (11 => --|) or (11 __|)
                nrz_samples[nrzs++] = reader_modulation;
                nrz_samples[nrzs++] = reader_modulation;
                // Invert tag modulation state
                reader_modulation ^= 1;
            } else if (periods > 0 && periods <= 24) {
                // Detected one bit and a modulation switch
                // NRZ modulation: (1 => -|) or (0 _|)
                nrz_samples[nrzs++] = reader_modulation;
                reader_modulation ^= 1;
            } else {
                reader_modulation ^= 1;
                // The function lf_count_edge_periods() returns > 64 periods, this is not a valid number periods
                Dbprintf("Detected unexpected period count: %d", periods);
                break;
            }
        }

        LED_D_OFF();

        // If there is no response, just repeat the loop
        if (!detected_modulation) continue;

        LED_A_OFF();

        // Make sure we always have an even number of samples. This fixes the problem
        // of ending the manchester decoding with a zero. See the example below where
        // the '|' character is end of modulation
        //  One at the end: ..._-|_____...
        // Zero at the end: ...-_|_____...
        // The last modulation change of a zero is not detected, but we should take
        // the half period in account, otherwise the demodulator will fail.
        if ((nrzs % 2) != 0) {
            nrz_samples[nrzs++] = reader_modulation;
        }

        LED_B_ON();

        // decode bitstream
        manrawdecode((uint8_t *)nrz_samples, &nrzs, true, 0);

        // Verify if the header consists of five consecutive ones
        if (nrzs < 5) {
            Dbprintf("Detected unexpected number of manchester decoded samples [%d]", nrzs);
            continue;
       } else {
            for (size_t i = 0; i < 5; i++){
                if (nrz_samples[i] != 1) {
                    Dbprintf("Detected incorrect header, the bit [%d] is zero instead of one", i);
                }
            }
        }

        // Pack the response into a byte array
        for (size_t i = 5; i < 37; i++){
            uint8_t bit = nrz_samples[i];
            rx[rxlen / 8] |= bit << (7 - (rxlen % 8));
            rxlen++;
        }

        // Check if frame was captured
        if (rxlen > 4) {

            LogTrace(rx, nbytes(rxlen), response, 0, NULL, true);

            // Process the incoming frame (rx) and prepare the outgoing frame (tx)
            hitag2_handle_reader_command(rx, rxlen, tx, &txlen);

            // Wait for HITAG_T_WAIT_1 carrier periods after the last reader bit,
            // not that since the clock counts since the rising edge, but T_Wait1 is
            // with respect to the falling edge, we need to wait actually (T_Wait1 - T_Low)
            // periods. The gap time T_Low varies (4..10). All timer values are in
            // terms of T0 units (HITAG_T_WAIT_1_MIN - HITAG_T_LOW )
            lf_wait_periods(HITAG_T_WAIT_1_MIN);

            // Send and store the tag answer (if there is any)
            if (txlen) {
                // Transmit the tag frame
                //hitag_send_frame(tx, txlen);
                lf_manchester_send_bytes(tx, txlen);

                // Store the frame in the trace
                LogTrace(tx, nbytes(txlen), 0, 0, NULL, false);
            }

            // Reset the received frame and response timing info
            memset(rx, 0x00, sizeof(rx));
            response = 0;

            LED_B_OFF();
        }
    }

    lf_finalize();

    // release allocated memory from BigBuff.
    BigBuf_free();

    DbpString("Sim stopped");

//    reply_ng(CMD_LF_HITAG_SIMULATE, (checked == -1) ? PM3_EOPABORTED : PM3_SUCCESS, (uint8_t *)tag.sectors, tag_size);
}

void ReaderHitag(hitag_function htf, hitag_data *htd) {

    uint32_t command_start = 0, command_duration = 0;
    uint32_t response_start = 0, response_duration = 0;

    uint8_t rx[HITAG_FRAME_LEN];
    size_t rxlen = 0;
    uint8_t txbuf[HITAG_FRAME_LEN];
    uint8_t *tx = txbuf;
    size_t txlen = 0;

    int t_wait_1;
    int t_wait_1_guard = 8;
    int t_wait_2;
    size_t tag_size;
    bool bStop = false;

    // Raw demodulation/decoding by sampling edge periods
    size_t periods = 0;

    // Reset the return status
    bSuccessful = false;
    bCrypto = false;

    // Clean up trace and prepare it for storing frames
    set_tracing(true);
    clear_trace();

    // Check configuration
    switch (htf) {
        case RHT1F_PLAIN: {
            DBG Dbprintf("Read public blocks in plain mode");
            // this part will be unreadable
            memset(tag.sectors + 2, 0x0, 30);
            blocknr = 0;
            break;
        }
        case RHT1F_AUTHENTICATE: {
            DBG Dbprintf("Read all blocks in authed mode");
            memcpy(nonce, htd->ht1auth.nonce, 4);
            memcpy(key, htd->ht1auth.key, 4);
            memcpy(logdata_0, htd->ht1auth.logdata_0, 4);
            memcpy(logdata_1, htd->ht1auth.logdata_1, 4);
            // TEST
            memset(nonce, 0x0, 4);
            memset(logdata_1, 0x00, 4);
            byte_value = 0;
            key_no = htd->ht1auth.key_no;
            DBG Dbprintf("Authenticating using key #%d:", key_no);
            DBG Dbhexdump(4, key, false);
            DBG DbpString("Nonce:");
            DBG Dbhexdump(4, nonce, false);
            DBG DbpString("Logdata_0:");
            DBG Dbhexdump(4, logdata_0, false);
            DBG DbpString("Logdata_1:");
            DBG Dbhexdump(4, logdata_1, false);
            blocknr = 0;
            break;
        }
        case RHT2F_PASSWORD: {
            DBG Dbprintf("List identifier in password mode");
            if (memcmp(htd->pwd.password, "\x00\x00\x00\x00", 4) == 0)
                memcpy(password, tag.sectors[1], sizeof(password));
            else
                memcpy(password, htd->pwd.password, sizeof(password));

            blocknr = 0;
            bPwd = false;
            bAuthenticating = false;
            break;
        }
        case RHT2F_AUTHENTICATE: {
            DBG DbpString("Authenticating using nr,ar pair:");
            memcpy(NrAr, htd->auth.NrAr, 8);
            DBG Dbhexdump(8, NrAr, false);
            bCrypto = false;
            bAuthenticating = false;
            break;
        }
        case RHT2F_CRYPTO: {
            DBG DbpString("Authenticating using key:");
            memcpy(key, htd->crypto.key, 6);  //HACK; 4 or 6??  I read both in the code.
            DBG Dbhexdump(6, key, false);
            DBG DbpString("Nonce:");
            DBG Dbhexdump(4, nonce, false);
            memcpy(nonce, htd->crypto.data, 4);
            blocknr = 0;
            bCrypto = false;
            bAuthenticating = false;
            break;
        }
        case RHT2F_TEST_AUTH_ATTEMPTS: {
            DBG Dbprintf("Testing %d authentication attempts", (auth_table_len / 8));
            auth_table_pos = 0;
            memcpy(NrAr, auth_table, 8);
            bCrypto = false;
            break;
        }
        case RHT2F_UID_ONLY: {
            blocknr = 0;
            bCrypto = false;
            bAuthenticating = false;
            break;
        }
        default: {
            DBG Dbprintf("Error, unknown function: %d", htf);
            set_tracing(false);
            return;
        }
    }

    LED_D_ON();

    // hitag2 state machine?
    hitag2_init();

    uint8_t attempt_count = 0;

    // Tag specific configuration settings (sof, timings, etc.)
    if (htf < 10) {
        // hitagS settings
        t_wait_1 = 204;
        t_wait_2 = 128;
        /*tag_size = 256;*/
        flipped_bit = 0;
        tag_size = 8;
        DBG DbpString("Configured for hitagS reader");
    } else if (htf < 20) {
        // hitag1 settings
        t_wait_1 = 204;
        t_wait_2 = 128;
        tag_size = 256;
        flipped_bit = 0;
        DBG DbpString("Configured for hitag1 reader");
    } else if (htf < 30) {
        // hitag2 settings
        t_wait_1 = HITAG_T_WAIT_1_MIN;
        t_wait_2 = HITAG_T_WAIT_2_MIN;
        tag_size = 48;
        DBG DbpString("Configured for hitag2 reader");
    } else {
        DBG Dbprintf("Error, unknown hitag reader type: %d", htf);
        return;
    }

    // init as reader
    lf_init(true, false);

    uint8_t tag_modulation;
    size_t max_nrzs = (8 * HITAG_FRAME_LEN + 5) * 2; // up to 2 nrzs per bit
    uint8_t nrz_samples[max_nrzs];
    size_t nrzs = 0;
    int16_t checked = 0;

    while (!bStop && !BUTTON_PRESS()) {

        WDT_HIT();

        // only every 1000th times, in order to save time when collecting samples.
        if (checked == 1000) {
            if (data_available()) {
                checked = -1;
                break;
            } else {
                checked = 0;
            }
        }
        ++checked;

        // By default reset the transmission buffer
        tx = txbuf;
        switch (htf) {
            case RHT1F_PLAIN: {
                bStop = !hitag_plain(rx, rxlen, tx, &txlen, false);
                break;
            }
            case RHT1F_AUTHENTICATE: {
                bStop = !hitag1_authenticate(rx, rxlen, tx, &txlen);
                break;
            }
            case RHT2F_PASSWORD: {
                bStop = !hitag2_password(rx, rxlen, tx, &txlen, false);
                break;
            }
            case RHT2F_AUTHENTICATE: {
                bStop = !hitag2_authenticate(rx, rxlen, tx, &txlen);
                break;
            }
            case RHT2F_CRYPTO: {
                bStop = !hitag2_crypto(rx, rxlen, tx, &txlen, false);
                break;
            }
            case RHT2F_TEST_AUTH_ATTEMPTS: {
                bStop = !hitag2_test_auth_attempts(rx, rxlen, tx, &txlen);
                break;
            }
            case RHT2F_UID_ONLY: {
                bStop = !hitag2_read_uid(rx, rxlen, tx, &txlen);
                attempt_count++; //attempt 3 times to get uid then quit
                if (!bStop && attempt_count == 3)
                    bStop = true;

                break;
            }
            default: {
                DBG Dbprintf("Error, unknown function: %d", htf);
                goto out;
            }
        }

        // Wait for t_wait_2 carrier periods after the last tag bit before transmitting,
        lf_wait_periods(t_wait_2);
        command_start += t_wait_2;

        // Transmit the reader frame
        command_duration = hitag_reader_send_frame(tx, txlen);
        response_start = command_start + command_duration;

        // Let the antenna and ADC values settle
        // And find the position where edge sampling should start
        lf_wait_periods(t_wait_1 - t_wait_1_guard);
        response_start += t_wait_1 - t_wait_1_guard;

        // Keep administration of the first edge detection
        bool waiting_for_first_edge = true;

        // Did we detected any modulaiton at all
        bool detected_tag_modulation = false;

        // Use the current modulation state as starting point
        tag_modulation = lf_get_tag_modulation();

        // Reset the number of NRZ samples and use edge detection to detect them
        nrzs = 0;
        while (nrzs < max_nrzs) {
            // Get the timing of the next edge in number of wave periods
            periods = lf_count_edge_periods(128);

            // Are we dealing with the first incoming edge
            if (waiting_for_first_edge) {
                // Just break out of loop after an initial time-out (tag is probably not available)
                if (periods == 0) break;
                if (tag_modulation == 0) {
                    // hitag replies always start with 11111 == 1010101010, if we see 0
                    // it means we missed the first period, e.g. if the signal never crossed 0 since reader signal
                    // so let's add it:
                    nrz_samples[nrzs++] = tag_modulation ^ 1;
                    // Register the number of periods that have passed
                    // we missed the begin of response but we know it happened one period of 16 earlier
                    response_start += periods - 16;
                    response_duration = response_start;
                } else {
                    // Register the number of periods that have passed
                    response_start += periods;
                    response_duration = response_start;
                }
                // Indicate that we have dealt with the first edge
                waiting_for_first_edge = false;
                // The first edge is always a single NRZ bit, force periods on 16
                periods = 16;
                // We have received more than 0 periods, so we have detected a tag response
                detected_tag_modulation = true;
            } else {
                // The function lf_count_edge_periods() returns 0 when a time-out occurs
                if (periods == 0) {
                    DBG Dbprintf("Detected timeout after [%d] nrz samples", nrzs);
                    break;
                }
            }
            // Evaluate the number of periods before the next edge
            if (periods > 24 && periods <= 64) {
                // Detected two sequential equal bits and a modulation switch
                // NRZ modulation: (11 => --|) or (11 __|)
                nrz_samples[nrzs++] = tag_modulation;
                nrz_samples[nrzs++] = tag_modulation;
                response_duration += periods;
                // Invert tag modulation state
                tag_modulation ^= 1;
            } else if (periods > 0 && periods <= 24) {
                // Detected one bit and a modulation switch
                // NRZ modulation: (1 => -|) or (0 _|)
                nrz_samples[nrzs++] = tag_modulation;
                response_duration += periods;
                tag_modulation ^= 1;
            } else {
                // The function lf_count_edge_periods() returns > 64 periods, this is not a valid number periods
                DBG Dbprintf("Detected unexpected period count: %d", periods);
                break;
            }
        }

        // Store the TX frame, we do this now at this point, to avoid delay in processing
        // and to be able to overwrite the first samples with the trace (since they currently
        // still use the same memory space)
        if (txlen > 0) {
            LogTrace(tx, nbytes(txlen), command_start, command_start + command_duration, NULL, true);
        }

        // Reset values for receiving frames
        memset(rx, 0x00, sizeof(rx));
        rxlen = 0;

        // If there is no response, just repeat the loop
        if (!detected_tag_modulation) continue;

        // Make sure we always have an even number of samples. This fixes the problem
        // of ending the manchester decoding with a zero. See the example below where
        // the '|' character is end of modulation
        //  One at the end: ..._-|_____...
        // Zero at the end: ...-_|_____...
        // The last modulation change of a zero is not detected, but we should take
        // the half period in account, otherwise the demodulator will fail.
        if ((nrzs % 2) != 0) {
            nrz_samples[nrzs++] = tag_modulation;
        }

        LED_B_ON();

        // decode bitstream
        manrawdecode((uint8_t *)nrz_samples, &nrzs, true, 0);

        // decode frame

        // Verify if the header consists of five consecutive ones
        if (nrzs < 5) {
            DBG Dbprintf("Detected unexpected number of manchester decoded samples [%d]", nrzs);
            break;
        } else {
            size_t i;
            for (i = 0; i < 5; i++) {
                if (nrz_samples[i] != 1) {
                    DBG Dbprintf("Detected incorrect header, the bit [%d] is zero instead of one, abort", i);
                    break;
                }
            }
            if (i < 5) break;
        }

        // Pack the response into a byte array
        for (size_t i = 5; i < nrzs; i++) {
            uint8_t bit = nrz_samples[i];
            if (bit > 1) { // When Manchester detects impossible symbol it writes "7"
                DBG Dbprintf("Error in Manchester decoding, abort");
                break;
            }
            rx[rxlen / 8] |= bit << (7 - (rxlen % 8));
            rxlen++;
        }

        if (rxlen % 8 == 1) // skip spurious bit
            rxlen--;

        // Check if frame was captured and store it
        if (rxlen > 0) {

            LogTrace(rx, nbytes(rxlen), response_start, response_start + response_duration, NULL, false);

// TODO when using cumulative time for command_start, pm3 doesn't reply anymore, e.g. on lf hitag read 23 4F4E4D494B52
// Use delta time?
//            command_start = response_start + response_duration;
            command_start = 0;
            nrzs = 0;
        }
    }

out:
    lf_finalize();

    // release allocated memory from BigBuff.
    BigBuf_free();

    if (bSuccessful)
        reply_mix(CMD_ACK, bSuccessful, 0, 0, (uint8_t *)tag.sectors, tag_size);
    else
        reply_mix(CMD_ACK, bSuccessful, 0, 0, 0, 0);
}

void WriterHitag(hitag_function htf, hitag_data *htd, int page) {

    uint32_t command_start = 0;
    uint32_t command_duration = 0;
    uint32_t response_start = 0;
    uint32_t response_duration = 0; 
    uint8_t rx[HITAG_FRAME_LEN];
    size_t rxlen = 0;
    uint8_t txbuf[HITAG_FRAME_LEN];
    uint8_t *tx = txbuf;
    size_t txlen = 0;
    int t_wait_1;
    int t_wait_1_guard = 8;
    int t_wait_2;
    size_t tag_size;
    bool bStop = false;

    // Raw demodulation/decoding by sampling edge periods
    size_t periods = 0;

    // Reset the return status
    bSuccessful = false;
    bCrypto = false;

    // Clean up trace and prepare it for storing frames
    set_tracing(true);
    clear_trace();


    // Check configuration
    switch (htf) {
        case WHT2F_CRYPTO: {
            DbpString("Authenticating using key:");
            memcpy(key, htd->crypto.key, 6); //HACK; 4 or 6??  I read both in the code.
            memcpy(writedata, htd->crypto.data, 4);
            Dbhexdump(6, key, false);
            blocknr = page;
            bCrypto = false;
            bAuthenticating = false;
            writestate = WRITE_STATE_START;
        }
        break;
        case WHT2F_PASSWORD: {
            DbpString("Authenticating using password:");
            memcpy(password, htd->pwd.password, 4);
            memcpy(writedata, htd->crypto.data, 4);
            Dbhexdump(4, password, false);
            blocknr = page;
            bPwd = false;
            bAuthenticating = false;
            writestate = WRITE_STATE_START;
        }
        break;
        default: {
            Dbprintf("Error, unknown function: %d", htf);
            return;
        }
        break;
    }

    LED_D_ON();

    hitag2_init();

    // init as reader
    lf_init(true, false);

    // Tag specific configuration settings (sof, timings, etc.)
    if (htf < 10) {
        // hitagS settings
        t_wait_1 = 204;
        t_wait_2 = 128;
        /*tag_size = 256;*/
        flipped_bit = 0;
        tag_size = 8;
        DbpString("Configured for hitagS writer");
    } else if (htf < 20) {
       // hitag1 settings
        t_wait_1 = 204;
        t_wait_2 = 128;
        tag_size = 256;
        flipped_bit = 0;
        DbpString("Configured for hitag1 writer");
    } else if (htf < 30) {
       // hitag2 settings
        t_wait_1 = HITAG_T_WAIT_1_MIN;
        t_wait_2 = HITAG_T_WAIT_2_MIN;
        tag_size = 48;
        DbpString("Configured for hitag2 writer");
    } else {
        DBG Dbprintf("Error, unknown hitag writer type: %d", htf);
        return;
    }

    uint8_t tag_modulation;
    size_t max_nrzs = (8 * HITAG_FRAME_LEN + 5) * 2; // up to 2 nrzs per bit
    uint8_t nrz_samples[max_nrzs];
    size_t nrzs = 0;

    int16_t checked = 0;
    while (!bStop && !BUTTON_PRESS()) {

        // only every 1000th times, in order to save time when collecting samples.
        if (checked == 1000) {
            if (data_available()) {
                checked = -1;
                break;
            } else {
                checked = 0;
            }
        }
        ++checked;

        WDT_HIT();

        // By default reset the transmission buffer
        tx = txbuf;
        switch (htf) {
            case WHT2F_CRYPTO: {
                bStop = !hitag2_crypto(rx, rxlen, tx, &txlen, true);
                break;
            }
            case WHT2F_PASSWORD: {
                bStop = !hitag2_password(rx, rxlen, tx, &txlen, true);
                break;
            }
            default: {
                Dbprintf("Error, unknown function: %d", htf);
                goto out;
            }
        }

       // Wait for t_wait_2 carrier periods after the last tag bit before transmitting,
        lf_wait_periods(t_wait_2);
        command_start += t_wait_2;

        // Transmit the reader frame
        command_duration = hitag_reader_send_frame(tx, txlen);

        response_start = command_start + command_duration;

        // Let the antenna and ADC values settle
        // And find the position where edge sampling should start
        lf_wait_periods(t_wait_1 - t_wait_1_guard);
        response_start += t_wait_1 - t_wait_1_guard;

        // Keep administration of the first edge detection
        bool waiting_for_first_edge = true;

        // Did we detected any modulaiton at all
        bool detected_tag_modulation = false;

        // Use the current modulation state as starting point
        tag_modulation = lf_get_tag_modulation();

        // Reset the number of NRZ samples and use edge detection to detect them
        nrzs = 0;
        while (nrzs < max_nrzs) {
            // Get the timing of the next edge in number of wave periods
            periods = lf_count_edge_periods(128);

            // Are we dealing with the first incoming edge
            if (waiting_for_first_edge) {
                // Just break out of loop after an initial time-out (tag is probably not available)
                if (periods == 0) break;
                if (tag_modulation == 0) {
                    // hitag replies always start with 11111 == 1010101010, if we see 0
                    // it means we missed the first period, e.g. if the signal never crossed 0 since reader signal
                    // so let's add it:
                    nrz_samples[nrzs++] = tag_modulation ^ 1;
                    // Register the number of periods that have passed
                    // we missed the begin of response but we know it happened one period of 16 earlier
                    response_start += periods - 16;
                    response_duration = response_start;
                } else {
                    // Register the number of periods that have passed
                    response_start += periods;
                    response_duration = response_start;
                }
                // Indicate that we have dealt with the first edge
                waiting_for_first_edge = false;
                // The first edge is always a single NRZ bit, force periods on 16
                periods = 16;
                // We have received more than 0 periods, so we have detected a tag response
                detected_tag_modulation = true;
            } else {
                // The function lf_count_edge_periods() returns 0 when a time-out occurs
                if (periods == 0) {
                    //Dbprintf("Detected timeout after [%d] nrz samples", nrzs);
                    break;
                }
            }
            // Evaluate the number of periods before the next edge
            if (periods > 24 && periods <= 64) {
                // Detected two sequential equal bits and a modulation switch
                // NRZ modulation: (11 => --|) or (11 __|)
                nrz_samples[nrzs++] = tag_modulation;
                nrz_samples[nrzs++] = tag_modulation;
                response_duration += periods;
                // Invert tag modulation state
                tag_modulation ^= 1;
            } else if (periods > 0 && periods <= 24) {
                // Detected one bit and a modulation switch
                // NRZ modulation: (1 => -|) or (0 _|)
                nrz_samples[nrzs++] = tag_modulation;
                response_duration += periods;
                tag_modulation ^= 1;
            } else {
                // The function lf_count_edge_periods() returns > 64 periods, this is not a valid number periods
                //Dbprintf("Detected unexpected period count: %d", periods);
                break;
            }
        }

        // Wait some extra time for flash to be programmed
        //

        // Store the TX frame, we do this now at this point, to avoid delay in processing
        // and to be able to overwrite the first samples with the trace (since they currently
        // still use the same memory space)
        if (txlen > 0) {
            LogTrace(tx, nbytes(txlen), command_start, command_start + command_duration, NULL, true);
        }

        // Reset values for receiving frames
        memset(rx, 0x00, sizeof(rx));
        rxlen = 0;

        // If there is no response, just repeat the loop
        if (!detected_tag_modulation) continue;

        // Make sure we always have an even number of samples. This fixes the problem
        // of ending the manchester decoding with a zero. See the example below where
        // the '|' character is end of modulation
        //  One at the end: ..._-|_____...
        // Zero at the end: ...-_|_____...
        // The last modulation change of a zero is not detected, but we should take
        // the half period in account, otherwise the demodulator will fail.
        if ((nrzs % 2) != 0) {
            nrz_samples[nrzs++] = tag_modulation;
        }

        LED_B_ON();

        // decode bitstream
        manrawdecode((uint8_t *)nrz_samples, &nrzs, true, 0);

        // decode frame

        // Verify if the header consists of five consecutive ones
        if (nrzs < 5) {
            break;
        } else {
            size_t i;
            for (i = 0; i < 5; i++) {
                if (nrz_samples[i] != 1) {
                    Dbprintf("Detected incorrect header, the bit [%d] is zero instead of one, abort", i);
                    break;
                }
            }
            if (i < 5) break;
        }

        // Pack the response into a byte array
        for (size_t i = 5; i < nrzs; i++) {
            uint8_t bit = nrz_samples[i];
            if (bit > 1) { // When Manchester detects impossible symbol it writes "7"
                break;
            }
            rx[rxlen / 8] |= bit << (7 - (rxlen % 8));
            rxlen++;
        }

        if (rxlen % 8 == 1) // skip spurious bit
            rxlen--;

        // Check if frame was captured and store it
        if (rxlen > 0) {
            LogTrace(rx, nbytes(rxlen), response_start, response_start + response_duration, NULL, false);
            command_start = 0;
        }
    }


out:
    lf_finalize();

    // release allocated memory from BigBuff.
    BigBuf_free();

    reply_mix(CMD_ACK, bSuccessful, 0, 0, (uint8_t *)tag.sectors, tag_size);
}
