/* Copyright (c) 2005-2007 DN-Systems GmbH http://rfdump.org

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

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */

/* $Header: /cvs/rfdump/src/mifare.c,v 1.4 2007/10/31 21:59:27 dirk Exp $ */

/* TODO:
   - load keys from user prefs
   - store found keys in user prefs
   - resume crack run
*/

#include <ctype.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "main.h"
#include "tools.h"

static void incstr(char *s)
{
  while(*s)
    {
      switch(*s)
	{
	default: return;
	case '0': *s='1'; return;
	case '1': *s='2'; return;
	case '2': *s='3'; return;
	case '3': *s='4'; return;
	case '4': *s='5'; return;
	case '5': *s='6'; return;
	case '6': *s='7'; return;
	case '7': *s='8'; return;
	case '8': *s='9'; return;
	case '9': *s='A'; return;
	case 'a': case 'A': *s='B'; return;
	case 'b': case 'B': *s='C'; return;
	case 'c': case 'C': *s='D'; return;
	case 'd': case 'D': *s='E'; return;
	case 'e': case 'E': *s='F'; return;
	case 'f': case 'F': *s='0'; ++s;
	}
    }
}

static int crack_login(char *key, char *keytype)
{
  char cmd[256];
  int sectorNo=0;

  /* activate card */
  if(rfidSelect(fd, cmd, sizeof(cmd)) < 0)
    return -1;
  if(cmd[0] != 'M')
    return -1;

  /* check key */
  g_assert(RFID_CMD_LOGIN);
  snprintf(cmd, sizeof(cmd), "%s%02X%s%s", RFID_CMD_LOGIN, sectorNo, keytype, key);
  if(rfidWrite(fd, cmd) < 0)
    return -1;

  /* read response */
  if(rfidReadLine(fd, cmd, sizeof(cmd)) < 0)
    return -1;
  if(cmd[0]=='L')
    return 1;

  return 0;
}

static GtkWidget *progress;
static volatile int crack_run;



static void crack(char *title, char *keytype)
{
  int64_t i;

  /* search for keys in list */
#define KEY_FILE "mifare.txt"
  char *key_file[] = {
    KEY_FILE,
    "/usr/share/rfdump/" KEY_FILE,
    "/usr/local/share/rfdump/" KEY_FILE,
  };

  gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), 0);

  for(i=0; i<sizeof_array(key_file); ++i)
    {
      FILE *f=fopen(key_file[i], "r");
      if(f)
      {
	while(!feof(f))
	  {
	    char buf[128];
	    fgets(buf, sizeof(buf), f);
	    if(strlen(buf) == 12)
	      {
		int j;
		for(j=0; j<12; ++j)
		  if(!isxdigit(buf[j]))
		    goto nextline;

		gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), buf);
		poll_gtk();

		int res=crack_login(buf, "AA");
		if(res<0)
		  {
		    error_dialog("Communication error with tag.");
		    return;
		  }
		if(res)
		  {
		    char b[128];
		    snprintf(b, sizeof(b), "The Key A is: %s", buf);
		    fprintf(stderr, "%s\n", b);
		    error_dialog(b);
		    return;
		  }
		res=crack_login(buf, "BB");
		if(res<0)
		  {
		    error_dialog("Communication error with tag.");
		    return;
		  }
		if(res)
		  {
		    char b[128];
		    snprintf(b, sizeof(b), "The Key B is: %s", buf);
		    fprintf(stderr, "%s\n", b);
		    error_dialog(b);
		    return;
		  }
	      }
	  nextline: ;
	  }
	fclose(f);
	break;
      }
    }

  /* brute force crack */
  char key[] = "000000000000";
  const int64_t max_i=0x1000000000000ll;
  const time_t start_time=time(NULL);
  for(i=0; i<max_i && crack_run; ++i)
    {
      const gdouble frac=(gdouble)i/(gdouble)max_i;

      time_t delta_time=time(NULL)-start_time;
      if(delta_time<=0) delta_time=1;
      const int y=(int)(((gdouble)delta_time/frac)/86400.0) / 365;

      char buf[256];
      snprintf(buf, sizeof(buf), "%s: %s %i%% %ik/s ~%ia", title, key, (int)(frac*100.0), (int)(i/delta_time), y);
      gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), frac);
      gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), buf);
      poll_gtk();

      const int res=crack_login(key, keytype);
      if(res<0)
	{
	  error_dialog("Communication error with tag.");
	  return;
	}
      if(res)
	{
	  char b[128];
	  snprintf(b, sizeof(b), "The Key %c is: %s", keytype[0], key);
	  fprintf(stderr, "%s\n", b);
	  error_dialog(b);
	  return;
	}

      incstr(key);
    }
}

void on_mifare_crack_abort_clicked(GtkButton *button, gpointer user_data)
{
  crack_run=FALSE;
  gtk_widget_hide(windowMiFareCrack);
  gtk_widget_show(windowRFDump);
}

void on_crack_key_a1_activate(GtkMenuItem *menuitem, gpointer user_data)
{
  gtk_widget_show(windowMiFareCrack);
  gtk_widget_hide(windowRFDump);
  stop_scan();
  if(!progress)
    progress=GTK_WIDGET (gtk_builder_get_object (builder, "crack_progress"));
  crack_run=TRUE;
  crack("Crack MiFare Key A", "AA");
  on_mifare_crack_abort_clicked(NULL, NULL);
}

void on_crack_key_b1_activate(GtkMenuItem *menuitem, gpointer user_data)
{
  gtk_widget_show(windowMiFareCrack);
  gtk_widget_hide(windowRFDump);
  stop_scan();
  if(!progress)
    progress=GTK_WIDGET (gtk_builder_get_object (builder, "crack_progress"));
  crack_run=TRUE;
  crack("Crack MiFare Key B", "BB");
  on_mifare_crack_abort_clicked(NULL, NULL);
}
