//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
// main code for standalone HF Sniff (and ULC/NTAG/ULEV1 pwd storing)
//-----------------------------------------------------------------------------

/*
This can actually be used in two separate ways.
It can either be used to just HF 14a sniff on the go and/or grab the
authentication attempts for ULC/NTAG/ULEV1 into the flash mem (RDV4).

The retrieved sniffing session can be acquired by connecting the device
to a client that supports the reconnect capability and issue 'hf 14a list'.

In order to view the grabbed authentication attempts in the flash mem,
you can simply run 'script run read_pwd_mem' or just 'mem dump p l 256'
from the client to view the stored quadlets.
*/

#include "standalone.h" // standalone definitions
#include "proxmark3_arm.h"
#include "iso14443a.h"
#include "protocols.h"
#include "util.h"
#include "spiffs.h"
#include "appmain.h"
#include "fpgaloader.h"
#include "dbprint.h"
#include "ticks.h"
#include "BigBuf.h"
#include "string.h"

#define DELAY_READER_AIR2ARM_AS_SNIFFER (2 + 3 + 8)
#define DELAY_TAG_AIR2ARM_AS_SNIFFER (3 + 14 + 8)

// Maximum number of auth attempts per standalone session
#define MAX_PWDS_PER_SESSION 64

#define HF_BOG_LOGFILE "hf_bog.log"

// This is actually copied from SniffIso14443a
void RAMFUNC SniffAndStore(uint8_t param) {

    iso14443a_setup(FPGA_HF_ISO14443A_SNIFFER);

    // Allocate memory from BigBuf for some buffers
    // free all previous allocations first
    BigBuf_free();
    BigBuf_Clear_ext(false);
    clear_trace();
    set_tracing(true);

    // Array to store the authpwds
    uint8_t *capturedPwds = BigBuf_malloc(4 * MAX_PWDS_PER_SESSION);

    // The command (reader -> tag) that we're receiving.
    uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE);
    uint8_t *receivedCmdPar = BigBuf_malloc(MAX_PARITY_SIZE);

    // The response (tag -> reader) that we're receiving.
    uint8_t *receivedResp = BigBuf_malloc(MAX_FRAME_SIZE);
    uint8_t *receivedRespPar = BigBuf_malloc(MAX_PARITY_SIZE);

    // The DMA buffer, used to stream samples from the FPGA
    uint8_t *dmaBuf = BigBuf_malloc(DMA_BUFFER_SIZE);
    uint8_t *data = dmaBuf;

    uint8_t previous_data = 0;
    int dataLen;
    bool TagIsActive = false;
    bool ReaderIsActive = false;

    // Set up the demodulator for tag -> reader responses.
    Demod14aInit(receivedResp, receivedRespPar);

    // Set up the demodulator for the reader -> tag commands
    Uart14aInit(receivedCmd, receivedCmdPar);

    // Setup and start DMA.
    if (!FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE)) {
        if (DBGLEVEL > 1)
            Dbprintf("FpgaSetupSscDma failed. Exiting");
        return;
    }

    tUart14a *uart = GetUart14a();
    tDemod14a *demod = GetDemod14a();

    // We won't start recording the frames that we acquire until we trigger;
    // a good trigger condition to get started is probably when we see a
    // response from the tag.
    // triggered == false -- to wait first for card
    bool triggered = !(param & 0x03);

    uint32_t my_rsamples = 0;

    // Current captured passwords counter
    uint8_t auth_attempts = 0;

    SpinDelay(50);

    // loop and listen
    while (!BUTTON_PRESS()) {
        WDT_HIT();
        LED_A_ON();

        int register readBufDataP = data - dmaBuf;
        int register dmaBufDataP = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR;
        if (readBufDataP <= dmaBufDataP)
            dataLen = dmaBufDataP - readBufDataP;
        else
            dataLen = DMA_BUFFER_SIZE - readBufDataP + dmaBufDataP;

        // test for length of buffer
        if (dataLen > DMA_BUFFER_SIZE) { // TODO: Check if this works properly
            Dbprintf("[!] blew circular buffer! | datalen %u", dataLen);
            break;
        }
        if (dataLen < 1)
            continue;

        // primary buffer was stopped( <-- we lost data!
        if (!AT91C_BASE_PDC_SSC->PDC_RCR) {
            AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t)dmaBuf;
            AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE;
            // Dbprintf("[-] RxEmpty ERROR | data length %d", dataLen); // temporary
        }
        // secondary buffer sets as primary, secondary buffer was stopped
        if (!AT91C_BASE_PDC_SSC->PDC_RNCR) {
            AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t)dmaBuf;
            AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE;
        }

        LED_A_OFF();

        // Need two samples to feed Miller and Manchester-Decoder
        if (my_rsamples & 0x01) {

            if (!TagIsActive) { // no need to try decoding reader data if the tag is sending
                uint8_t readerdata = (previous_data & 0xF0) | (*data >> 4);
                if (MillerDecoding(readerdata, (my_rsamples - 1) * 4)) {
                    LED_C_ON();

                    // check - if there is a short 7bit request from reader
                    if ((!triggered) && (param & 0x02) && (uart->len == 1) && (uart->bitCount == 7))
                        triggered = true;

                    if (triggered) {
                        if ((receivedCmd) &&
                                ((receivedCmd[0] == MIFARE_ULEV1_AUTH) || (receivedCmd[0] == MIFARE_ULC_AUTH_1))) {
                            if (DBGLEVEL > 1)
                                Dbprintf("PWD-AUTH KEY: 0x%02x%02x%02x%02x", receivedCmd[1], receivedCmd[2],
                                         receivedCmd[3], receivedCmd[4]);

                            // temporarily save the captured pwd in our array
                            memcpy(&capturedPwds[4 * auth_attempts], receivedCmd + 1, 4);
                            auth_attempts++;
                        }

                        if (!LogTrace(receivedCmd, uart->len, uart->startTime * 16 - DELAY_READER_AIR2ARM_AS_SNIFFER,
                                      uart->endTime * 16 - DELAY_READER_AIR2ARM_AS_SNIFFER, uart->parity, true))
                            break;
                    }
                    /* ready to receive another command. */
                    Uart14aReset();
                    /* reset the demod code, which might have been */
                    /* false-triggered by the commands from the reader. */
                    Demod14aReset();
                    LED_B_OFF();
                }
                ReaderIsActive = (uart->state != STATE_14A_UNSYNCD);
            }

            // no need to try decoding tag data if the reader is sending - and we cannot afford the time
            if (!ReaderIsActive) {
                uint8_t tagdata = (previous_data << 4) | (*data & 0x0F);
                if (ManchesterDecoding(tagdata, 0, (my_rsamples - 1) * 4)) {
                    LED_B_ON();

                    if (!LogTrace(receivedResp, demod->len, demod->startTime * 16 - DELAY_TAG_AIR2ARM_AS_SNIFFER,
                                  demod->endTime * 16 - DELAY_TAG_AIR2ARM_AS_SNIFFER, demod->parity, false))
                        break;

                    if ((!triggered) && (param & 0x01))
                        triggered = true;

                    // ready to receive another response.
                    Demod14aReset();
                    // reset the Miller decoder including its (now outdated) input buffer
                    Uart14aReset();
                    // UartInit(receivedCmd, receivedCmdPar);
                    LED_C_OFF();
                }
                TagIsActive = (demod->state != DEMOD_14A_UNSYNCD);
            }
        }

        previous_data = *data;
        my_rsamples++;
        data++;
        if (data == dmaBuf + DMA_BUFFER_SIZE) {
            data = dmaBuf;
        }
    } // end main loop

    FpgaDisableSscDma();
    set_tracing(false);

    Dbprintf("Stopped sniffing");

    SpinDelay(200);

    // Write stuff to spiffs logfile
    if (auth_attempts > 0) {
        if (DBGLEVEL > 1)
            Dbprintf("[!] Authentication attempts = %u", auth_attempts);
        size_t size = 4 * auth_attempts;
        uint8_t *data = BigBuf_malloc(size);

        if (!exists_in_spiffs((char *)HF_BOG_LOGFILE)) {
            rdv40_spiffs_write((char *)HF_BOG_LOGFILE, (uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE);
        } else {
            rdv40_spiffs_append((char *)HF_BOG_LOGFILE, (uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE);
        }
    }

    if (DBGLEVEL > 1)
        Dbprintf("[!] Wrote %u Authentification attempts into logfile", auth_attempts);

    SpinErr(LED_A, 200, 5);
    SpinDelay(100);
}

void ModInfo(void) {
    DbpString("  HF 14a sniff standalone with ULC/ULEV1/NTAG auth storing in flashmem - aka BogitoRun (Bogito)");
}

void RunMod() {

    StandAloneMode();

    Dbprintf(">>  Bogiton 14a Sniff UL/UL-EV1/NTAG a.k.a BogitoRun Started  <<");
    Dbprintf("Starting to sniff");

    // param:
    // bit 0 - trigger from first card answer
    // bit 1 - trigger from first reader 7-bit request
    SniffAndStore(0);
    LEDsoff();
    SpinDelay(300);
    Dbprintf("- [ End ] -> You can take shell back ...");
    Dbprintf("- [  !  ] -> use 'script run read_pwd_mem' to print passwords");
}
