From 8df14408b88fcf99206738ef2b1cf4ff0e9e6a88 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 2 Jul 2020 11:47:46 +0200 Subject: [PATCH 001/139] fgpa changes from official repo. Had to split felica into its own image. Leading to three bit files created. --- fpga/Makefile | 11 ++- fpga/fpga_hf.v | 172 ++++++++++++++++++++------------------------ fpga/fpga_lf.v | 4 +- fpga/hi_flite.v | 18 +++-- fpga/hi_iso14443a.v | 57 +++++++-------- fpga/hi_simulate.v | 119 +++++++++++++++++------------- fpga/hi_sniffer.v | 13 +--- 7 files changed, 191 insertions(+), 203 deletions(-) diff --git a/fpga/Makefile b/fpga/Makefile index 0d2bb1625..9b1f7a5ca 100644 --- a/fpga/Makefile +++ b/fpga/Makefile @@ -5,17 +5,22 @@ RMDIR = rm -rf # rmdir only if dir is empty, tolerate failure RMDIR_SOFT = -rmdir # -all: fpga_lf.bit fpga_hf.bit +all: fpga_lf.bit fpga_hf.bit fpga_felica.bit clean: - $(Q)$(RM) *.bgn *.drc *.ncd *.ngd *_par.xrpt *-placed.* *-placed_pad.* *_usage.xml xst_hf.srp xst_lf.srp + $(Q)$(RM) *.bgn *.drc *.ncd *.ngd *_par.xrpt *-placed.* *-placed_pad.* *_usage.xml xst_hf.srp xst_lf.srp xst_felica.srp $(Q)$(RM) *.map *.ngc *.xrpt *.pcf *.rbt *.bld *.mrp *.ngm *.unroutes *_summary.xml netlist.lst $(Q)$(RMDIR) *_auto_* xst -fpga_hf.ngc: fpga_hf.v fpga.ucf xst_hf.scr util.v hi_simulate.v hi_read_tx.v hi_read_rx_xcorr.v hi_iso14443a.v hi_sniffer.v hi_flite.v hi_get_trace.v +#fpga_hf.ngc: fpga_hf.v fpga.ucf xst_hf.scr util.v hi_simulate.v hi_reader.v hi_iso14443a.v hi_sniffer.v hi_flite.v hi_get_trace.v +fpga_hf.ngc: fpga_hf.v fpga.ucf xst_hf.scr util.v hi_simulate.v hi_reader.v hi_iso14443a.v hi_sniffer.v hi_get_trace.v $(Q)$(RM) $@ $(info [-] XST $@) $(Q)$(XILINX_TOOLS_PREFIX)xst -ifn xst_hf.scr +fpga_felica.ngc: fpga_felica.v fpga.ucf xst_felica.scr util.v hi_simulate.v hi_reader.v hi_sniffer.v hi_flite.v hi_get_trace.v + $(Q)$(RM) $@ + $(info [-] XST $@) + $(Q)$(XILINX_TOOLS_PREFIX)xst -ifn xst_felica.scr fpga_lf.ngc: fpga_lf.v fpga.ucf xst_lf.scr util.v clk_divider.v lo_edge_detect.v lo_read.v lo_passthru.v lp20khz_1MSa_iir_filter.v min_max_tracker.v lf_edge_detect.v $(Q)$(RM) $@ diff --git a/fpga/fpga_hf.v b/fpga/fpga_hf.v index d23b64a08..5448219fc 100644 --- a/fpga/fpga_hf.v +++ b/fpga/fpga_hf.v @@ -23,23 +23,28 @@ `define FPGA_CMD_TRACE_ENABLE 2 // Major modes: -`define FPGA_MAJOR_MODE_HF_READER_TX 0 -`define FPGA_MAJOR_MODE_HF_READER_RX_XCORR 1 -`define FPGA_MAJOR_MODE_HF_SIMULATOR 2 -`define FPGA_MAJOR_MODE_HF_ISO14443A 3 -`define FPGA_MAJOR_MODE_HF_SNOOP 4 -`define FPGA_MAJOR_MODE_HF_ISO18092 5 -`define FPGA_MAJOR_MODE_HF_GET_TRACE 6 +`define FPGA_MAJOR_MODE_HF_READER 0 +`define FPGA_MAJOR_MODE_HF_SIMULATOR 1 +`define FPGA_MAJOR_MODE_HF_ISO14443A 2 +`define FPGA_MAJOR_MODE_HF_SNOOP 3 +`define FPGA_MAJOR_MODE_HF_ISO18092 4 +`define FPGA_MAJOR_MODE_HF_GET_TRACE 5 `define FPGA_MAJOR_MODE_OFF 7 // Options for the generic HF reader -// Options for the HF reader, tx to tag -`define FPGA_HF_READER_TX_SHALLOW_MOD 1 +`define FPGA_HF_READER_MODE_RECEIVE_IQ 0 +`define FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE 1 +`define FPGA_HF_READER_MODE_RECEIVE_PHASE 2 +`define FPGA_HF_READER_MODE_SEND_FULL_MOD 3 +`define FPGA_HF_READER_MODE_SEND_SHALLOW_MOD 4 +`define FPGA_HF_READER_MODE_SNIFF_IQ 5 +`define FPGA_HF_READER_MODE_SNIFF_AMPLITUDE 6 +`define FPGA_HF_READER_MODE_SNIFF_PHASE 7 +`define FPGA_HF_READER_MODE_SEND_JAM 8 -// Options for the HF reader, correlating against rx from tag -`define FPGA_HF_READER_RX_XCORR_848_KHZ 1 -`define FPGA_HF_READER_RX_XCORR_SNOOP 2 -`define FPGA_HF_READER_RX_XCORR_QUARTER 4 +`define FPGA_HF_READER_SUBCARRIER_848_KHZ 0 +`define FPGA_HF_READER_SUBCARRIER_424_KHZ 1 +`define FPGA_HF_READER_SUBCARRIER_212_KHZ 2 // Options for the HF simulated tag, how to modulate `define FPGA_HF_SIMULATOR_NO_MODULATION 0 @@ -60,13 +65,12 @@ `define FPGA_HF_ISO18092_FLAG_424K 2 // 0010 should enable 414k mode (untested). No autodetect `define FPGA_HF_ISO18092_FLAG_READER 4 // 0100 enables antenna power, to act as a reader instead of tag -`include "hi_read_tx.v" -`include "hi_read_rx_xcorr.v" +`include "hi_reader.v" `include "hi_simulate.v" `include "hi_iso14443a.v" `include "hi_sniffer.v" `include "util.v" -`include "hi_flite.v" +// `include "hi_flite.v" `include "hi_get_trace.v" module fpga_hf( @@ -105,14 +109,14 @@ bit | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -----+------------------------------------------- cmd | x x x x major| x x x -opt | x x +opt | x x x divi | x x x x x x x x thres| x x x x x x x x -----+------------------------------------------- */ reg [15:0] shift_reg; -reg [7:0] conf_word; +reg [8:0] conf_word; reg trace_enable; // We switch modes between transmitting to the 13.56 MHz tag and receiving @@ -121,7 +125,7 @@ reg trace_enable; always @(posedge ncs) begin case(shift_reg[15:12]) - `FPGA_CMD_SET_CONFREG: conf_word <= shift_reg[7:0]; + `FPGA_CMD_SET_CONFREG: conf_word <= shift_reg[8:0]; `FPGA_CMD_TRACE_ENABLE: trace_enable <= shift_reg[0]; endcase end @@ -135,25 +139,12 @@ begin end end -wire [2:0] major_mode = conf_word[7:5]; +// select module (outputs) based on major mode +wire [2:0] major_mode = conf_word[8:6]; -// For the high-frequency transmit configuration: modulation depth, either -// 100% (just quite driving antenna, steady LOW), or shallower (tri-state -// some fraction of the buffers) -wire hi_read_tx_shallow_modulation = conf_word[0]; - -// For the high-frequency receive correlator: frequency against which to -// correlate. -wire hi_read_rx_xcorr_848 = conf_word[0]; - -// and whether to drive the coil (reader) or just short it (snooper) -wire hi_read_rx_xcorr_snoop = conf_word[1]; - -// divide subcarrier frequency by 4 -wire hi_read_rx_xcorr_quarter = conf_word[2]; - -// For the high-frequency simulated tag: what kind of modulation to use. -wire [2:0] hi_simulate_mod_type = conf_word[2:0]; +// configuring the HF reader +wire [1:0] subcarrier_frequency = conf_word[5:4]; +wire [3:0] minor_mode = conf_word[3:0]; //----------------------------------------------------------------------------- // And then we instantiate the modules corresponding to each of the FPGA's @@ -161,95 +152,86 @@ wire [2:0] hi_simulate_mod_type = conf_word[2:0]; // the output pins. //----------------------------------------------------------------------------- -hi_read_tx ht( - pck0, ck_1356meg, ck_1356megb, - ht_pwr_lo, ht_pwr_hi, ht_pwr_oe1, ht_pwr_oe2, ht_pwr_oe3, ht_pwr_oe4, - adc_d, ht_adc_clk, - ht_ssp_frame, ht_ssp_din, ssp_dout, ht_ssp_clk, - cross_hi, cross_lo, - ht_dbg, - hi_read_tx_shallow_modulation -); - -hi_read_rx_xcorr hrxc( - pck0, ck_1356meg, ck_1356megb, - hrxc_pwr_lo, hrxc_pwr_hi, hrxc_pwr_oe1, hrxc_pwr_oe2, hrxc_pwr_oe3, hrxc_pwr_oe4, - adc_d, hrxc_adc_clk, - hrxc_ssp_frame, hrxc_ssp_din, ssp_dout, hrxc_ssp_clk, - cross_hi, cross_lo, - hrxc_dbg, - hi_read_rx_xcorr_848, hi_read_rx_xcorr_snoop, hi_read_rx_xcorr_quarter +// 000 - HF reader +hi_reader hr( + ck_1356megb, + hr_pwr_lo, hr_pwr_hi, hr_pwr_oe1, hr_pwr_oe2, hr_pwr_oe3, hr_pwr_oe4, + adc_d, hr_adc_clk, + hr_ssp_frame, hr_ssp_din, ssp_dout, hr_ssp_clk, + hr_dbg, + subcarrier_frequency, minor_mode ); +// 001 - HF simulated tag hi_simulate hs( - pck0, ck_1356meg, ck_1356megb, + ck_1356meg, hs_pwr_lo, hs_pwr_hi, hs_pwr_oe1, hs_pwr_oe2, hs_pwr_oe3, hs_pwr_oe4, adc_d, hs_adc_clk, hs_ssp_frame, hs_ssp_din, ssp_dout, hs_ssp_clk, - cross_hi, cross_lo, hs_dbg, - hi_simulate_mod_type + minor_mode ); +// 010 - HF ISO14443-A hi_iso14443a hisn( - pck0, ck_1356meg, ck_1356megb, + ck_1356meg, hisn_pwr_lo, hisn_pwr_hi, hisn_pwr_oe1, hisn_pwr_oe2, hisn_pwr_oe3, hisn_pwr_oe4, adc_d, hisn_adc_clk, hisn_ssp_frame, hisn_ssp_din, ssp_dout, hisn_ssp_clk, - cross_hi, cross_lo, hisn_dbg, - hi_simulate_mod_type + minor_mode ); +// 011 - HF sniff hi_sniffer he( - pck0, ck_1356meg, ck_1356megb, - he_pwr_lo, he_pwr_hi, he_pwr_oe1, he_pwr_oe2, he_pwr_oe3, he_pwr_oe4, - adc_d, he_adc_clk, - he_ssp_frame, he_ssp_din, ssp_dout, he_ssp_clk, - cross_hi, cross_lo, - he_dbg, - hi_read_rx_xcorr_848, hi_read_rx_xcorr_snoop, hi_read_rx_xcorr_quarter + ck_1356megb, + he_pwr_lo, he_pwr_hi, he_pwr_oe1, he_pwr_oe2, he_pwr_oe3, he_pwr_oe4, + adc_d, he_adc_clk, + he_ssp_frame, he_ssp_din, he_ssp_clk ); +// 100 - HF ISO18092 FeliCa +/* hi_flite hfl( - pck0, ck_1356meg, ck_1356megb, - hfl_pwr_lo, hfl_pwr_hi, hfl_pwr_oe1, hfl_pwr_oe2, hfl_pwr_oe3, hfl_pwr_oe4, - adc_d, hfl_adc_clk, - hfl_ssp_frame, hfl_ssp_din, ssp_dout, hfl_ssp_clk, - cross_hi, cross_lo, - hfl_dbg, - hi_simulate_mod_type + ck_1356megb, + hfl_pwr_lo, hfl_pwr_hi, hfl_pwr_oe1, hfl_pwr_oe2, hfl_pwr_oe3, hfl_pwr_oe4, + adc_d, hfl_adc_clk, + hfl_ssp_frame, hfl_ssp_din, ssp_dout, hfl_ssp_clk, + hfl_dbg, + minor_mode ); +*/ +// 101 - HF get trace hi_get_trace gt( ck_1356megb, adc_d, trace_enable, major_mode, gt_ssp_frame, gt_ssp_din, gt_ssp_clk ); - // Major modes: +// 000 -- HF reader; subcarrier frequency and modulation depth selectable +// 001 -- HF simulated tag +// 010 -- HF ISO14443-A +// 011 -- HF sniff +// 100 -- HF ISO18092 FeliCa +// 101 -- HF get trace +// 110 -- unused +// 111 -- FPGA_MAJOR_MODE_OFF -// 000 -- HF reader, transmitting to tag; modulation depth selectable -// 001 -- HF reader, receiving from tag, correlating as it goes; frequency selectable -// 010 -- HF simulated tag -// 011 -- HF ISO14443-A -// 100 -- HF Snoop -// 101 -- Felica modem, reusing HF reader -// 110 -- HF get trace -// 111 -- everything off +// 000 001 010 011 100 101 110 111 -mux8 mux_ssp_clk (major_mode, ssp_clk, ht_ssp_clk, hrxc_ssp_clk, hs_ssp_clk, hisn_ssp_clk, he_ssp_clk, hfl_ssp_clk, gt_ssp_clk, 1'b0); -mux8 mux_ssp_din (major_mode, ssp_din, ht_ssp_din, hrxc_ssp_din, hs_ssp_din, hisn_ssp_din, he_ssp_din, hfl_ssp_din, gt_ssp_din, 1'b0); -mux8 mux_ssp_frame (major_mode, ssp_frame, ht_ssp_frame, hrxc_ssp_frame, hs_ssp_frame, hisn_ssp_frame, he_ssp_frame, hfl_ssp_frame, gt_ssp_frame, 1'b0); -mux8 mux_pwr_oe1 (major_mode, pwr_oe1, ht_pwr_oe1, hrxc_pwr_oe1, hs_pwr_oe1, hisn_pwr_oe1, he_pwr_oe1, hfl_pwr_oe1, 1'b0, 1'b0); -mux8 mux_pwr_oe2 (major_mode, pwr_oe2, ht_pwr_oe2, hrxc_pwr_oe2, hs_pwr_oe2, hisn_pwr_oe2, he_pwr_oe2, hfl_pwr_oe2, 1'b0, 1'b0); -mux8 mux_pwr_oe3 (major_mode, pwr_oe3, ht_pwr_oe3, hrxc_pwr_oe3, hs_pwr_oe3, hisn_pwr_oe3, he_pwr_oe3, hfl_pwr_oe3, 1'b0, 1'b0); -mux8 mux_pwr_oe4 (major_mode, pwr_oe4, ht_pwr_oe4, hrxc_pwr_oe4, hs_pwr_oe4, hisn_pwr_oe4, he_pwr_oe4, hfl_pwr_oe4, 1'b0, 1'b0); -mux8 mux_pwr_lo (major_mode, pwr_lo, ht_pwr_lo, hrxc_pwr_lo, hs_pwr_lo, hisn_pwr_lo, he_pwr_lo, hfl_pwr_lo, 1'b0, 1'b0); -mux8 mux_pwr_hi (major_mode, pwr_hi, ht_pwr_hi, hrxc_pwr_hi, hs_pwr_hi, hisn_pwr_hi, he_pwr_hi, hfl_pwr_hi, 1'b0, 1'b0); -mux8 mux_adc_clk (major_mode, adc_clk, ht_adc_clk, hrxc_adc_clk, hs_adc_clk, hisn_adc_clk, he_adc_clk, hfl_adc_clk, 1'b0, 1'b0); -mux8 mux_dbg (major_mode, dbg, ht_dbg, hrxc_dbg, hs_dbg, hisn_dbg, he_dbg, hfl_dbg, 1'b0, 1'b0); +mux8 mux_ssp_clk (major_mode, ssp_clk, hr_ssp_clk, hs_ssp_clk, hisn_ssp_clk, he_ssp_clk, hfl_ssp_clk, gt_ssp_clk, 1'b0, 1'b0); +mux8 mux_ssp_din (major_mode, ssp_din, hr_ssp_din, hs_ssp_din, hisn_ssp_din, he_ssp_din, hfl_ssp_din, gt_ssp_din, 1'b0, 1'b0); +mux8 mux_ssp_frame (major_mode, ssp_frame, hr_ssp_frame, hs_ssp_frame, hisn_ssp_frame, he_ssp_frame, hfl_ssp_frame, gt_ssp_frame, 1'b0, 1'b0); +mux8 mux_pwr_oe1 (major_mode, pwr_oe1, hr_pwr_oe1, hs_pwr_oe1, hisn_pwr_oe1, he_pwr_oe1, hfl_pwr_oe1, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_oe2 (major_mode, pwr_oe2, hr_pwr_oe2, hs_pwr_oe2, hisn_pwr_oe2, he_pwr_oe2, hfl_pwr_oe2, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_oe3 (major_mode, pwr_oe3, hr_pwr_oe3, hs_pwr_oe3, hisn_pwr_oe3, he_pwr_oe3, hfl_pwr_oe3, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_oe4 (major_mode, pwr_oe4, hr_pwr_oe4, hs_pwr_oe4, hisn_pwr_oe4, he_pwr_oe4, hfl_pwr_oe4, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_lo (major_mode, pwr_lo, hr_pwr_lo, hs_pwr_lo, hisn_pwr_lo, he_pwr_lo, hfl_pwr_lo, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_hi (major_mode, pwr_hi, hr_pwr_hi, hs_pwr_hi, hisn_pwr_hi, he_pwr_hi, hfl_pwr_hi, 1'b0, 1'b0, 1'b0); +mux8 mux_adc_clk (major_mode, adc_clk, hr_adc_clk, hs_adc_clk, hisn_adc_clk, he_adc_clk, hfl_adc_clk, 1'b0, 1'b0, 1'b0); +mux8 mux_dbg (major_mode, dbg, hr_dbg, hs_dbg, hisn_dbg, he_dbg, hfl_dbg, 1'b0, 1'b0, 1'b0); // In all modes, let the ADC's outputs be enabled. assign adc_noe = 1'b0; diff --git a/fpga/fpga_lf.v b/fpga/fpga_lf.v index dd3fe3b0b..aa79da234 100644 --- a/fpga/fpga_lf.v +++ b/fpga/fpga_lf.v @@ -122,7 +122,7 @@ reg [7:0] divisor; reg [7:0] lf_ed_threshold; reg [11:0] conf_word; -wire [2:0] major_mode = conf_word[7:5]; +wire [2:0] major_mode = conf_word[8:6]; wire lf_field = conf_word[0]; wire lf_ed_toggle_mode = conf_word[1]; @@ -135,7 +135,7 @@ begin begin // 12 bit data conf_word <= shift_reg[11:0]; - if (shift_reg[7:5] == `FPGA_MAJOR_MODE_LF_EDGE_DETECT) + if (shift_reg[8:6] == `FPGA_MAJOR_MODE_LF_EDGE_DETECT) begin lf_ed_threshold <= 127; // default threshold end diff --git a/fpga/hi_flite.v b/fpga/hi_flite.v index 97d0154ea..6cb87825e 100644 --- a/fpga/hi_flite.v +++ b/fpga/hi_flite.v @@ -14,25 +14,23 @@ */ module hi_flite( - pck0, ck_1356meg, ck_1356megb, + ck_1356meg, pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, adc_d, adc_clk, ssp_frame, ssp_din, ssp_dout, ssp_clk, - cross_hi, cross_lo, dbg, mod_type - ); - input pck0, ck_1356meg, ck_1356megb; + input ck_1356meg; output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; input [7:0] adc_d; output adc_clk; input ssp_dout; output ssp_frame, ssp_din, ssp_clk; - input cross_hi, cross_lo; output dbg; - input [2:0] mod_type; -assign dbg=0; + input [3:0] mod_type; + +assign dbg = 0; wire power = mod_type[2]; wire speed = mod_type[1]; @@ -40,7 +38,7 @@ wire disabl = mod_type[0]; // Most off, oe4 for modulation; // Trying reader emulation (would presumably just require switching power on, but I am not sure) -assign pwr_lo = 1'b0; +assign pwr_lo = 1'b0; // 512x64/fc -wait before ts0, 32768 ticks // tslot: 256*64/fc @@ -347,11 +345,11 @@ reg pwr_oe4; wire mod = ((fccount >= bithalf) ^ dlay) & (~disabl); -always @(ck_1356megb or ssp_dout or power or disabl or mod) +always @(ck_1356meg or ssp_dout or power or disabl or mod) begin if (power) begin - pwr_hi <= ck_1356megb; + pwr_hi <= ck_1356meg; pwr_oe1 <= 1'b0;//mod; pwr_oe2 <= 1'b0;//mod; pwr_oe3 <= 1'b0;//mod; diff --git a/fpga/hi_iso14443a.v b/fpga/hi_iso14443a.v index 3bb550d73..6b09847ec 100644 --- a/fpga/hi_iso14443a.v +++ b/fpga/hi_iso14443a.v @@ -3,31 +3,22 @@ // Gerhard de Koning Gans, April 2008 //----------------------------------------------------------------------------- -// constants for the different modes: -`define SNIFFER 3'b000 -`define TAGSIM_LISTEN 3'b001 -`define TAGSIM_MOD 3'b010 -`define READER_LISTEN 3'b011 -`define READER_MOD 3'b100 - module hi_iso14443a( - pck0, ck_1356meg, ck_1356megb, + ck_1356meg, pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, adc_d, adc_clk, ssp_frame, ssp_din, ssp_dout, ssp_clk, - cross_hi, cross_lo, dbg, mod_type ); - input pck0, ck_1356meg, ck_1356megb; + input ck_1356meg; output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; input [7:0] adc_d; output adc_clk; input ssp_dout; output ssp_frame, ssp_din, ssp_clk; - input cross_hi, cross_lo; output dbg; - input [2:0] mod_type; + input [3:0] mod_type; wire adc_clk = ck_1356meg; @@ -151,7 +142,7 @@ begin end // adjust internal timer counter if necessary: - if (negedge_cnt[3:0] == 4'd13 && (mod_type == `SNIFFER || mod_type == `TAGSIM_LISTEN) && deep_modulation) + if (negedge_cnt[3:0] == 4'd13 && (mod_type == `FPGA_HF_ISO14443A_SNIFFER || mod_type == `FPGA_HF_ISO14443A_TAGSIM_LISTEN) && deep_modulation) begin if (reader_falling_edge_time == 4'd1) // reader signal changes right after sampling. Better sample earlier next time. begin @@ -185,7 +176,7 @@ reg [3:0] mod_detect_reset_time; always @(negedge adc_clk) begin - if (mod_type == `READER_LISTEN) + if (mod_type == `FPGA_HF_ISO14443A_READER_LISTEN) // (our) reader signal changes at negedge_cnt[3:0]=9, tag response expected to start n*16+4 ticks later, further delayed by // 3 ticks ADC conversion. The maximum filter output (edge detected) will be detected after subcarrier zero crossing (+7 ticks). // To allow some timing variances, we want to have the maximum filter outputs well within the detection window, i.e. @@ -195,7 +186,7 @@ begin mod_detect_reset_time <= 4'd4; end else - if (mod_type == `SNIFFER) + if (mod_type == `FPGA_HF_ISO14443A_SNIFFER) begin // detect a rising edge of reader's signal and sync modulation detector to the tag's answer: if (~pre_after_hysteresis && after_hysteresis && deep_modulation) @@ -320,7 +311,7 @@ reg [3:0] sub_carrier_cnt; // response window of 1128 - 774 = 354 ticks. // reset on a pause in listen mode. I.e. the counter starts when the pause is over: -assign fdt_reset = ~after_hysteresis && mod_type == `TAGSIM_LISTEN; +assign fdt_reset = ~after_hysteresis && mod_type == `FPGA_HF_ISO14443A_TAGSIM_LISTEN; always @(negedge adc_clk) begin @@ -363,7 +354,7 @@ reg mod_sig_coil; always @(negedge adc_clk) begin - if (mod_type == `TAGSIM_MOD) // need to take care of proper fdt timing + if (mod_type == `FPGA_HF_ISO14443A_TAGSIM_MOD) // need to take care of proper fdt timing begin if(fdt_counter == `FDT_COUNT) begin @@ -438,7 +429,7 @@ always @(negedge adc_clk) begin if (negedge_cnt[5:0] == 6'd63) // fill the buffer begin - if (mod_type == `SNIFFER) + if (mod_type == `FPGA_HF_ISO14443A_SNIFFER) begin if(deep_modulation) // a reader is sending (or there's no field at all) begin @@ -455,7 +446,7 @@ begin end end - if(negedge_cnt[2:0] == 3'b000 && mod_type == `SNIFFER) // shift at double speed + if(negedge_cnt[2:0] == 3'b000 && mod_type == `FPGA_HF_ISO14443A_SNIFFER) // shift at double speed begin // Don't shift if we just loaded new data, obviously. if(negedge_cnt[5:0] != 6'd0) @@ -464,7 +455,7 @@ begin end end - if(negedge_cnt[3:0] == 4'b0000 && mod_type != `SNIFFER) + if(negedge_cnt[3:0] == 4'b0000 && mod_type != `FPGA_HF_ISO14443A_SNIFFER) begin // Don't shift if we just loaded new data, obviously. if(negedge_cnt[6:0] != 7'd0) @@ -484,8 +475,8 @@ reg ssp_frame; always @(negedge adc_clk) begin - if(mod_type == `SNIFFER) - // SNIFFER mode (ssp_clk = adc_clk / 8, ssp_frame clock = adc_clk / 64)): + if(mod_type == `FPGA_HF_ISO14443A_SNIFFER) + // FPGA_HF_ISO14443A_SNIFFER mode (ssp_clk = adc_clk / 8, ssp_frame clock = adc_clk / 64)): begin if(negedge_cnt[2:0] == 3'd0) ssp_clk <= 1'b1; @@ -505,7 +496,7 @@ begin if(negedge_cnt[3:0] == 4'd8) ssp_clk <= 1'b0; - if(negedge_cnt[6:0] == 7'd7) // ssp_frame rising edge indicates start of frame + if(negedge_cnt[6:0] == 7'd7) // ssp_frame rising edge indicates start of frame, sampled on falling edge of ssp_clk ssp_frame <= 1'b1; if(negedge_cnt[6:0] == 7'd23) ssp_frame <= 1'b0; @@ -525,23 +516,23 @@ begin if(negedge_cnt[3:0] == 4'd0) begin // What do we communicate to the ARM - if(mod_type == `TAGSIM_LISTEN) + if(mod_type == `FPGA_HF_ISO14443A_TAGSIM_LISTEN) sendbit = after_hysteresis; - else if(mod_type == `TAGSIM_MOD) + else if(mod_type == `FPGA_HF_ISO14443A_TAGSIM_MOD) /* if(fdt_counter > 11'd772) sendbit = mod_sig_coil; // huh? else */ sendbit = fdt_indicator; - else if (mod_type == `READER_LISTEN) + else if (mod_type == `FPGA_HF_ISO14443A_READER_LISTEN) sendbit = curbit; else sendbit = 1'b0; end - if(mod_type == `SNIFFER) + if(mod_type == `FPGA_HF_ISO14443A_SNIFFER) // send sampled reader and tag data: bit_to_arm = to_arm[7]; - else if (mod_type == `TAGSIM_MOD && fdt_elapsed && temp_buffer_reset) + else if (mod_type == `FPGA_HF_ISO14443A_TAGSIM_MOD && fdt_elapsed && temp_buffer_reset) // send timing information: bit_to_arm = to_arm[7]; else @@ -554,22 +545,22 @@ end assign ssp_din = bit_to_arm; -// Subcarrier (adc_clk/16, for TAGSIM_MOD only). +// Subcarrier (adc_clk/16, for FPGA_HF_ISO14443A_TAGSIM_MOD only). wire sub_carrier; assign sub_carrier = ~sub_carrier_cnt[3]; -// in READER_MOD: drop carrier for mod_sig_coil==1 (pause); in READER_LISTEN: carrier always on; in other modes: carrier always off -assign pwr_hi = (ck_1356megb & (((mod_type == `READER_MOD) & ~mod_sig_coil) || (mod_type == `READER_LISTEN))); +// in FPGA_HF_ISO14443A_READER_MOD: drop carrier for mod_sig_coil==1 (pause); in FPGA_HF_ISO14443A_READER_LISTEN: carrier always on; in other modes: carrier always off +assign pwr_hi = (ck_1356meg & (((mod_type == `FPGA_HF_ISO14443A_READER_MOD) & ~mod_sig_coil) || (mod_type == `FPGA_HF_ISO14443A_READER_LISTEN))); // Enable HF antenna drivers: assign pwr_oe1 = 1'b0; assign pwr_oe3 = 1'b0; -// TAGSIM_MOD: short circuit antenna with different resistances (modulated by sub_carrier modulated by mod_sig_coil) +// FPGA_HF_ISO14443A_TAGSIM_MOD: short circuit antenna with different resistances (modulated by sub_carrier modulated by mod_sig_coil) // for pwr_oe4 = 1 (tristate): antenna load = 10k || 33 = 32,9 Ohms // for pwr_oe4 = 0 (active): antenna load = 10k || 33 || 33 = 16,5 Ohms -assign pwr_oe4 = mod_sig_coil & sub_carrier & (mod_type == `TAGSIM_MOD); +assign pwr_oe4 = mod_sig_coil & sub_carrier & (mod_type == `FPGA_HF_ISO14443A_TAGSIM_MOD); // This is all LF, so doesn't matter. assign pwr_oe2 = 1'b0; diff --git a/fpga/hi_simulate.v b/fpga/hi_simulate.v index d336a5ddf..737dd4586 100644 --- a/fpga/hi_simulate.v +++ b/fpga/hi_simulate.v @@ -17,82 +17,105 @@ //----------------------------------------------------------------------------- module hi_simulate( - pck0, ck_1356meg, ck_1356megb, + ck_1356meg, pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, adc_d, adc_clk, ssp_frame, ssp_din, ssp_dout, ssp_clk, - cross_hi, cross_lo, dbg, mod_type ); - input pck0, ck_1356meg, ck_1356megb; + input ck_1356meg; output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; input [7:0] adc_d; output adc_clk; input ssp_dout; output ssp_frame, ssp_din, ssp_clk; - input cross_hi, cross_lo; output dbg; - input [2:0] mod_type; + input [3:0] mod_type; // Power amp goes between LOW and tri-state, so pwr_hi (and pwr_lo) can // always be low. -assign pwr_hi = 1'b0; -assign pwr_lo = 1'b0; +assign pwr_hi = 1'b0; // HF antenna connected to GND +assign pwr_lo = 1'b0; // LF antenna connected to GND + +// This one is all LF, so doesn't matter +assign pwr_oe2 = 1'b0; + +assign adc_clk = ck_1356meg; +assign dbg = ssp_frame; // The comparator with hysteresis on the output from the peak detector. reg after_hysteresis; -assign adc_clk = ck_1356meg; +reg [11:0] has_been_low_for; always @(negedge adc_clk) begin - if(& adc_d[7:5]) after_hysteresis = 1'b1; - else if(~(| adc_d[7:5])) after_hysteresis = 1'b0; + if (& adc_d[7:5]) after_hysteresis <= 1'b1; // if (adc_d >= 224) + else if (~(| adc_d[7:5])) after_hysteresis <= 1'b0; // if (adc_d <= 31) + + if (adc_d >= 224) + begin + has_been_low_for <= 12'd0; + end + else + begin + if (has_been_low_for == 12'd4095) + begin + has_been_low_for <= 12'd0; + after_hysteresis <= 1'b1; + end + else + begin + has_been_low_for <= has_been_low_for + 1; + end + end end // Divide 13.56 MHz to produce various frequencies for SSP_CLK -// and modulation. 11 bits allow for factors of up to /128. -reg [10:0] ssp_clk_divider; +// and modulation. +reg [8:0] ssp_clk_divider; -always @(posedge adc_clk) +always @(negedge adc_clk) ssp_clk_divider <= (ssp_clk_divider + 1); reg ssp_clk; always @(negedge adc_clk) begin - if(mod_type == 3'b101) - // Get bit every at 53kHz (every 8th carrier bit of 424kHz) - ssp_clk <= ssp_clk_divider[7]; - else if(mod_type == 3'b010) + if (mod_type == `FPGA_HF_SIMULATOR_MODULATE_424K_8BIT) + // Get bit every at 53KHz (every 8th carrier bit of 424kHz) + ssp_clk <= ~ssp_clk_divider[7]; + else if (mod_type == `FPGA_HF_SIMULATOR_MODULATE_212K) // Get next bit at 212kHz - ssp_clk <= ssp_clk_divider[5]; + ssp_clk <= ~ssp_clk_divider[5]; else // Get next bit at 424kHz - ssp_clk <= ssp_clk_divider[4]; + ssp_clk <= ~ssp_clk_divider[4]; end -// Divide SSP_CLK by 8 to produce the byte framing signal; the phase of -// this is arbitrary, because it's just a bitstream. -// One nasty issue, though: I can't make it work with both rx and tx at -// once. The phase wrt ssp_clk must be changed. TODO to find out why -// that is and make a better fix. -reg [2:0] ssp_frame_divider_to_arm; -always @(posedge ssp_clk) - ssp_frame_divider_to_arm <= (ssp_frame_divider_to_arm + 1); -reg [2:0] ssp_frame_divider_from_arm; -always @(negedge ssp_clk) - ssp_frame_divider_from_arm <= (ssp_frame_divider_from_arm + 1); - - +// Produce the byte framing signal; the phase of this signal +// is arbitrary, because it's just a bit stream in this module. reg ssp_frame; -always @(ssp_frame_divider_to_arm or ssp_frame_divider_from_arm or mod_type) - if(mod_type == 3'b000) // not modulating, so listening, to ARM - ssp_frame = (ssp_frame_divider_to_arm == 3'b000); +always @(negedge adc_clk) +begin + if (mod_type == `FPGA_HF_SIMULATOR_MODULATE_212K) + begin + if (ssp_clk_divider[8:5] == 4'd1) + ssp_frame <= 1'b1; + if (ssp_clk_divider[8:5] == 4'd5) + ssp_frame <= 1'b0; + end else - ssp_frame = (ssp_frame_divider_from_arm == 3'b000); + begin + if (ssp_clk_divider[7:4] == 4'd1) + ssp_frame <= 1'b1; + if (ssp_clk_divider[7:4] == 4'd5) + ssp_frame <= 1'b0; + end +end + // Synchronize up the after-hysteresis signal, to produce DIN. reg ssp_din; @@ -101,29 +124,25 @@ always @(posedge ssp_clk) // Modulating carrier frequency is fc/64 (212kHz) to fc/16 (848kHz). Reuse ssp_clk divider for that. reg modulating_carrier; -always @(mod_type or ssp_clk or ssp_dout) - if(mod_type == 3'b000) +always @(*) + if(mod_type == `FPGA_HF_SIMULATOR_NO_MODULATION) modulating_carrier <= 1'b0; // no modulation - else if(mod_type == 3'b001) + else if(mod_type == `FPGA_HF_SIMULATOR_MODULATE_BPSK) modulating_carrier <= ssp_dout ^ ssp_clk_divider[3]; // XOR means BPSK - else if(mod_type == 3'b010) + else if(mod_type == `FPGA_HF_SIMULATOR_MODULATE_212K) modulating_carrier <= ssp_dout & ssp_clk_divider[5]; // switch 212kHz subcarrier on/off - else if(mod_type == 3'b100 || mod_type == 3'b101) + else if(mod_type == `FPGA_HF_SIMULATOR_MODULATE_424K || mod_type == `FPGA_HF_SIMULATOR_MODULATE_424K_8BIT) modulating_carrier <= ssp_dout & ssp_clk_divider[4]; // switch 424kHz modulation on/off else modulating_carrier <= 1'b0; // yet unused -// This one is all LF, so doesn't matter -assign pwr_oe2 = modulating_carrier; -// Toggle only one of these, since we are already producing much deeper + +// Load modulation. Toggle only one of these, since we are already producing much deeper // modulation than a real tag would. -assign pwr_oe1 = modulating_carrier; -assign pwr_oe4 = modulating_carrier; - +assign pwr_oe1 = 1'b0; // 33 Ohms Load +assign pwr_oe4 = modulating_carrier; // 33 Ohms Load // This one is always on, so that we can watch the carrier. -assign pwr_oe3 = 1'b0; - -assign dbg = ssp_din; +assign pwr_oe3 = 1'b0; // 10k Load endmodule diff --git a/fpga/hi_sniffer.v b/fpga/hi_sniffer.v index 3a989ce62..c2dc844a7 100644 --- a/fpga/hi_sniffer.v +++ b/fpga/hi_sniffer.v @@ -1,21 +1,14 @@ module hi_sniffer( - pck0, ck_1356meg, ck_1356megb, + ck_1356meg, pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, adc_d, adc_clk, - ssp_frame, ssp_din, ssp_dout, ssp_clk, - cross_hi, cross_lo, - dbg, - xcorr_is_848, snoop, xcorr_quarter_freq // not used. + ssp_frame, ssp_din, ssp_clk ); - input pck0, ck_1356meg, ck_1356megb; + input ck_1356meg; output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; input [7:0] adc_d; output adc_clk; - input ssp_dout; output ssp_frame, ssp_din, ssp_clk; - input cross_hi, cross_lo; - output dbg; - input xcorr_is_848, snoop, xcorr_quarter_freq; // not used. // We are only snooping, all off. assign pwr_hi = 1'b0; From 41dde3281d0da555b42eb50ab0a4ff6d4850e9ba Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 2 Jul 2020 11:49:22 +0200 Subject: [PATCH 002/139] add files --- fpga/fpga_felica.v | 228 ++++++++++++++++++++++++++++++ fpga/hi_reader.v | 335 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 563 insertions(+) create mode 100644 fpga/fpga_felica.v create mode 100644 fpga/hi_reader.v diff --git a/fpga/fpga_felica.v b/fpga/fpga_felica.v new file mode 100644 index 000000000..461d90b02 --- /dev/null +++ b/fpga/fpga_felica.v @@ -0,0 +1,228 @@ +//----------------------------------------------------------------------------- +// The FPGA is responsible for interfacing between the A/D, the coil drivers, +// and the ARM. In the low-frequency modes it passes the data straight +// through, so that the ARM gets raw A/D samples over the SSP. In the high- +// frequency modes, the FPGA might perform some demodulation first, to +// reduce the amount of data that we must send to the ARM. +// +// I am not really an FPGA/ASIC designer, so I am sure that a lot of this +// could be improved. +// +// Jonathan Westhues, March 2006 +// Added ISO14443-A support by Gerhard de Koning Gans, April 2008 +// iZsh , June 2014 +// Piwi, Feb 2019 +//----------------------------------------------------------------------------- + + +// Defining commands, modes and options. This must be aligned to the definitions in fpgaloader.h +// Note: the definitions here are without shifts + +// Commands: +`define FPGA_CMD_SET_CONFREG 1 +`define FPGA_CMD_TRACE_ENABLE 2 + +// Major modes: +`define FPGA_MAJOR_MODE_HF_READER 0 +`define FPGA_MAJOR_MODE_HF_SIMULATOR 1 +`define FPGA_MAJOR_MODE_HF_ISO14443A 2 +`define FPGA_MAJOR_MODE_HF_SNOOP 3 +`define FPGA_MAJOR_MODE_HF_ISO18092 4 +`define FPGA_MAJOR_MODE_HF_GET_TRACE 5 +`define FPGA_MAJOR_MODE_OFF 7 + +// Options for the generic HF reader +`define FPGA_HF_READER_MODE_RECEIVE_IQ 0 +`define FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE 1 +`define FPGA_HF_READER_MODE_RECEIVE_PHASE 2 +`define FPGA_HF_READER_MODE_SEND_FULL_MOD 3 +`define FPGA_HF_READER_MODE_SEND_SHALLOW_MOD 4 +`define FPGA_HF_READER_MODE_SNIFF_IQ 5 +`define FPGA_HF_READER_MODE_SNIFF_AMPLITUDE 6 +`define FPGA_HF_READER_MODE_SNIFF_PHASE 7 +`define FPGA_HF_READER_MODE_SEND_JAM 8 + +`define FPGA_HF_READER_SUBCARRIER_848_KHZ 0 +`define FPGA_HF_READER_SUBCARRIER_424_KHZ 1 +`define FPGA_HF_READER_SUBCARRIER_212_KHZ 2 + +// Options for the HF simulated tag, how to modulate +`define FPGA_HF_SIMULATOR_NO_MODULATION 0 +`define FPGA_HF_SIMULATOR_MODULATE_BPSK 1 +`define FPGA_HF_SIMULATOR_MODULATE_212K 2 +`define FPGA_HF_SIMULATOR_MODULATE_424K 4 +`define FPGA_HF_SIMULATOR_MODULATE_424K_8BIT 5 + +// Options for ISO14443A +`define FPGA_HF_ISO14443A_SNIFFER 0 +`define FPGA_HF_ISO14443A_TAGSIM_LISTEN 1 +`define FPGA_HF_ISO14443A_TAGSIM_MOD 2 +`define FPGA_HF_ISO14443A_READER_LISTEN 3 +`define FPGA_HF_ISO14443A_READER_MOD 4 + +//options for ISO18092 / Felica +`define FPGA_HF_ISO18092_FLAG_NOMOD 1 // 0001 disable modulation module +`define FPGA_HF_ISO18092_FLAG_424K 2 // 0010 should enable 414k mode (untested). No autodetect +`define FPGA_HF_ISO18092_FLAG_READER 4 // 0100 enables antenna power, to act as a reader instead of tag + +`include "hi_reader.v" +`include "hi_simulate.v" +//`include "hi_iso14443a.v" +`include "hi_sniffer.v" +`include "util.v" +`include "hi_flite.v" +`include "hi_get_trace.v" + +module fpga_felica( + input spck, output miso, input mosi, input ncs, + input pck0, input ck_1356meg, input ck_1356megb, + output pwr_lo, output pwr_hi, + output pwr_oe1, output pwr_oe2, output pwr_oe3, output pwr_oe4, + input [7:0] adc_d, output adc_clk, output adc_noe, + output ssp_frame, output ssp_din, input ssp_dout, output ssp_clk, + input cross_hi, input cross_lo, + output dbg +); + +//----------------------------------------------------------------------------- +// The SPI receiver. This sets up the configuration word, which the rest of +// the logic looks at to determine how to connect the A/D and the coil +// drivers (i.e., which section gets it). Also assign some symbolic names +// to the configuration bits, for use below. +//----------------------------------------------------------------------------- + +/* + Attempt to write up how its hooked up. Iceman 2020. + + Communication between ARM / FPGA is done inside armsrc/fpgaloader.c see: function FpgaSendCommand() + Send 16 bit command / data pair to FPGA + The bit format is: C3 C2 C1 C0 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 + where + C is 4bit command + D is 12bit data + + shift_reg receive this 16bit frame + + +-----+--------- frame layout -------------------- +bit | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +-----+------------------------------------------- +cmd | x x x x +major| x x x +opt | x x x +divi | x x x x x x x x +thres| x x x x x x x x +-----+------------------------------------------- +*/ + +reg [15:0] shift_reg; +reg [8:0] conf_word; +reg trace_enable; + +// We switch modes between transmitting to the 13.56 MHz tag and receiving +// from it, which means that we must make sure that we can do so without +// glitching, or else we will glitch the transmitted carrier. +always @(posedge ncs) +begin + case(shift_reg[15:12]) + `FPGA_CMD_SET_CONFREG: conf_word <= shift_reg[8:0]; + `FPGA_CMD_TRACE_ENABLE: trace_enable <= shift_reg[0]; + endcase +end + +always @(posedge spck) +begin + if(~ncs) + begin + shift_reg[15:1] <= shift_reg[14:0]; + shift_reg[0] <= mosi; + end +end + +// select module (outputs) based on major mode +wire [2:0] major_mode = conf_word[8:6]; + +// configuring the HF reader +wire [1:0] subcarrier_frequency = conf_word[5:4]; +wire [3:0] minor_mode = conf_word[3:0]; + +//----------------------------------------------------------------------------- +// And then we instantiate the modules corresponding to each of the FPGA's +// major modes, and use muxes to connect the outputs of the active mode to +// the output pins. +//----------------------------------------------------------------------------- + +// 000 - HF reader +hi_reader hr( + ck_1356megb, + hr_pwr_lo, hr_pwr_hi, hr_pwr_oe1, hr_pwr_oe2, hr_pwr_oe3, hr_pwr_oe4, + adc_d, hr_adc_clk, + hr_ssp_frame, hr_ssp_din, ssp_dout, hr_ssp_clk, + hr_dbg, + subcarrier_frequency, minor_mode +); + +// 001 - HF simulated tag +hi_simulate hs( + ck_1356meg, + hs_pwr_lo, hs_pwr_hi, hs_pwr_oe1, hs_pwr_oe2, hs_pwr_oe3, hs_pwr_oe4, + adc_d, hs_adc_clk, + hs_ssp_frame, hs_ssp_din, ssp_dout, hs_ssp_clk, + hs_dbg, + minor_mode +); + +// 011 - HF sniff +hi_sniffer he( + ck_1356megb, + he_pwr_lo, he_pwr_hi, he_pwr_oe1, he_pwr_oe2, he_pwr_oe3, he_pwr_oe4, + adc_d, he_adc_clk, + he_ssp_frame, he_ssp_din, he_ssp_clk +); + +// 100 - HF ISO18092 FeliCa +/* +hi_flite hfl( + ck_1356megb, + hfl_pwr_lo, hfl_pwr_hi, hfl_pwr_oe1, hfl_pwr_oe2, hfl_pwr_oe3, hfl_pwr_oe4, + adc_d, hfl_adc_clk, + hfl_ssp_frame, hfl_ssp_din, ssp_dout, hfl_ssp_clk, + hfl_dbg, + minor_mode +); +*/ + +// 101 - HF get trace +hi_get_trace gt( + ck_1356megb, + adc_d, trace_enable, major_mode, + gt_ssp_frame, gt_ssp_din, gt_ssp_clk +); + +// Major modes: +// 000 -- HF reader; subcarrier frequency and modulation depth selectable +// 001 -- HF simulated tag +// 010 -- HF ISO14443-A - removed for space... +// 011 -- HF sniff +// 100 -- HF ISO18092 FeliCa +// 101 -- HF get trace +// 110 -- unused +// 111 -- FPGA_MAJOR_MODE_OFF + +// 000 001 010 011 100 101 110 111 +mux8 mux_ssp_clk (major_mode, ssp_clk, hr_ssp_clk, hs_ssp_clk, 1'b0, he_ssp_clk, hfl_ssp_clk, gt_ssp_clk, 1'b0, 1'b0); +mux8 mux_ssp_din (major_mode, ssp_din, hr_ssp_din, hs_ssp_din, 1'b0, he_ssp_din, hfl_ssp_din, gt_ssp_din, 1'b0, 1'b0); +mux8 mux_ssp_frame (major_mode, ssp_frame, hr_ssp_frame, hs_ssp_frame, 1'b0, he_ssp_frame, hfl_ssp_frame, gt_ssp_frame, 1'b0, 1'b0); +mux8 mux_pwr_oe1 (major_mode, pwr_oe1, hr_pwr_oe1, hs_pwr_oe1, 1'b0, he_pwr_oe1, hfl_pwr_oe1, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_oe2 (major_mode, pwr_oe2, hr_pwr_oe2, hs_pwr_oe2, 1'b0, he_pwr_oe2, hfl_pwr_oe2, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_oe3 (major_mode, pwr_oe3, hr_pwr_oe3, hs_pwr_oe3, 1'b0, he_pwr_oe3, hfl_pwr_oe3, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_oe4 (major_mode, pwr_oe4, hr_pwr_oe4, hs_pwr_oe4, 1'b0, he_pwr_oe4, hfl_pwr_oe4, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_lo (major_mode, pwr_lo, hr_pwr_lo, hs_pwr_lo, 1'b0, he_pwr_lo, hfl_pwr_lo, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_hi (major_mode, pwr_hi, hr_pwr_hi, hs_pwr_hi, 1'b0, he_pwr_hi, hfl_pwr_hi, 1'b0, 1'b0, 1'b0); +mux8 mux_adc_clk (major_mode, adc_clk, hr_adc_clk, hs_adc_clk, 1'b0, he_adc_clk, hfl_adc_clk, 1'b0, 1'b0, 1'b0); +mux8 mux_dbg (major_mode, dbg, hr_dbg, hs_dbg, 1'b0, he_dbg, hfl_dbg, 1'b0, 1'b0, 1'b0); + +// In all modes, let the ADC's outputs be enabled. +assign adc_noe = 1'b0; + +endmodule diff --git a/fpga/hi_reader.v b/fpga/hi_reader.v new file mode 100644 index 000000000..ab03f7ba4 --- /dev/null +++ b/fpga/hi_reader.v @@ -0,0 +1,335 @@ +//----------------------------------------------------------------------------- +// +// Jonathan Westhues, April 2006 +//----------------------------------------------------------------------------- + +module hi_reader( + ck_1356meg, + pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, + adc_d, adc_clk, + ssp_frame, ssp_din, ssp_dout, ssp_clk, + dbg, + subcarrier_frequency, minor_mode +); + input ck_1356meg; + output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; + input [7:0] adc_d; + output adc_clk; + input ssp_dout; + output ssp_frame, ssp_din, ssp_clk; + output dbg; + input [1:0] subcarrier_frequency; + input [3:0] minor_mode; + +assign adc_clk = ck_1356meg; // sample frequency is 13,56 MHz + +// When we're a reader, we just need to do the BPSK demod; but when we're an +// eavesdropper, we also need to pick out the commands sent by the reader, +// using AM. Do this the same way that we do it for the simulated tag. +reg after_hysteresis, after_hysteresis_prev, after_hysteresis_prev_prev; +reg [11:0] has_been_low_for; +always @(negedge adc_clk) +begin + if (& adc_d[7:0]) after_hysteresis <= 1'b1; + else if (~(| adc_d[7:0])) after_hysteresis <= 1'b0; + + if (after_hysteresis) + begin + has_been_low_for <= 12'd0; + end + else + begin + if (has_been_low_for == 12'd4095) + begin + has_been_low_for <= 12'd0; + after_hysteresis <= 1'b1; + end + else + has_been_low_for <= has_been_low_for + 1; + end +end + + +// Let us report a correlation every 64 samples. I.e. +// one Q/I pair after 4 subcarrier cycles for the 848kHz subcarrier, +// one Q/I pair after 2 subcarrier cycles for the 424kHz subcarriers, +// one Q/I pair for each subcarrier cyle for the 212kHz subcarrier. +// We need a 6-bit counter for the timing. +reg [5:0] corr_i_cnt; +always @(negedge adc_clk) +begin + corr_i_cnt <= corr_i_cnt + 1; +end + + +// A couple of registers in which to accumulate the correlations. From the 64 samples +// we would add at most 32 times the difference between unmodulated and modulated signal. It should +// be safe to assume that a tag will not be able to modulate the carrier signal by more than 25%. +// 32 * 255 * 0,25 = 2040, which can be held in 11 bits. Add 1 bit for sign. +// Temporary we might need more bits. For the 212kHz subcarrier we could possible add 32 times the +// maximum signal value before a first subtraction would occur. 32 * 255 = 8160 can be held in 13 bits. +// Add one bit for sign -> need 14 bit registers but final result will fit into 12 bits. +reg signed [13:0] corr_i_accum; +reg signed [13:0] corr_q_accum; +// we will report maximum 8 significant bits +reg signed [7:0] corr_i_out; +reg signed [7:0] corr_q_out; + + +// the amplitude of the subcarrier is sqrt(ci^2 + cq^2). +// approximate by amplitude = max(|ci|,|cq|) + 1/2*min(|ci|,|cq|) +reg [13:0] corr_amplitude, abs_ci, abs_cq, max_ci_cq; +reg [12:0] min_ci_cq_2; // min_ci_cq / 2 + +always @(*) +begin + if (corr_i_accum[13] == 1'b0) + abs_ci <= corr_i_accum; + else + abs_ci <= -corr_i_accum; + + if (corr_q_accum[13] == 1'b0) + abs_cq <= corr_q_accum; + else + abs_cq <= -corr_q_accum; + + if (abs_ci > abs_cq) + begin + max_ci_cq <= abs_ci; + min_ci_cq_2 <= abs_cq / 2; + end + else + begin + max_ci_cq <= abs_cq; + min_ci_cq_2 <= abs_ci / 2; + end + + corr_amplitude <= max_ci_cq + min_ci_cq_2; + +end + + +// The subcarrier reference signals +reg subcarrier_I; +reg subcarrier_Q; + +always @(*) +begin + if (subcarrier_frequency == `FPGA_HF_READER_SUBCARRIER_848_KHZ) + begin + subcarrier_I = ~corr_i_cnt[3]; + subcarrier_Q = ~(corr_i_cnt[3] ^ corr_i_cnt[2]); + end + else if (subcarrier_frequency == `FPGA_HF_READER_SUBCARRIER_212_KHZ) + begin + subcarrier_I = ~corr_i_cnt[5]; + subcarrier_Q = ~(corr_i_cnt[5] ^ corr_i_cnt[4]); + end + else + begin // 424 kHz + subcarrier_I = ~corr_i_cnt[4]; + subcarrier_Q = ~(corr_i_cnt[4] ^ corr_i_cnt[3]); + end +end + + +// ADC data appears on the rising edge, so sample it on the falling edge +always @(negedge adc_clk) +begin + // These are the correlators: we correlate against in-phase and quadrature + // versions of our reference signal, and keep the (signed) results or the + // resulting amplitude to send out later over the SSP. + if (corr_i_cnt == 6'd0) + begin + if (minor_mode == `FPGA_HF_READER_MODE_SNIFF_AMPLITUDE) + begin + // send amplitude plus 2 bits reader signal + corr_i_out <= corr_amplitude[13:6]; + corr_q_out <= {corr_amplitude[5:0], after_hysteresis_prev_prev, after_hysteresis_prev}; + end + else if (minor_mode == `FPGA_HF_READER_MODE_SNIFF_IQ) + begin + + // Send 7 most significant bits of in phase tag signal (signed), plus 1 bit reader signal + if (corr_i_accum[13:11] == 3'b000 || corr_i_accum[13:11] == 3'b111) + corr_i_out <= {corr_i_accum[11:5], after_hysteresis_prev_prev}; + else // truncate to maximum value + if (corr_i_accum[13] == 1'b0) + corr_i_out <= {7'b0111111, after_hysteresis_prev_prev}; + else + corr_i_out <= {7'b1000000, after_hysteresis_prev_prev}; + + // Send 7 most significant bits of quadrature phase tag signal (signed), plus 1 bit reader signal + if (corr_q_accum[13:11] == 3'b000 || corr_q_accum[13:11] == 3'b111) + corr_q_out <= {corr_q_accum[11:5], after_hysteresis_prev}; + else // truncate to maximum value + if (corr_q_accum[13] == 1'b0) + corr_q_out <= {7'b0111111, after_hysteresis_prev}; + else + corr_q_out <= {7'b1000000, after_hysteresis_prev}; + end + else if (minor_mode == `FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE) + begin + // send amplitude + corr_i_out <= {2'b00, corr_amplitude[13:8]}; + corr_q_out <= corr_amplitude[7:0]; + end + else if (minor_mode == `FPGA_HF_READER_MODE_RECEIVE_IQ) + begin + + // Send 8 bits of in phase tag signal + if (corr_i_accum[13:11] == 3'b000 || corr_i_accum[13:11] == 3'b111) + corr_i_out <= corr_i_accum[11:4]; + else // truncate to maximum value + if (corr_i_accum[13] == 1'b0) + corr_i_out <= 8'b01111111; + else + corr_i_out <= 8'b10000000; + + // Send 8 bits of quadrature phase tag signal + if (corr_q_accum[13:11] == 3'b000 || corr_q_accum[13:11] == 3'b111) + corr_q_out <= corr_q_accum[11:4]; + else // truncate to maximum value + if (corr_q_accum[13] == 1'b0) + corr_q_out <= 8'b01111111; + else + corr_q_out <= 8'b10000000; + end + + // for each Q/I pair report two reader signal samples when sniffing. Store the 1st. + after_hysteresis_prev_prev <= after_hysteresis; + + // Initialize next correlation. + // Both I and Q reference signals are high when corr_i_nct == 0. Therefore need to accumulate. + corr_i_accum <= $signed({1'b0, adc_d}); + corr_q_accum <= $signed({1'b0, adc_d}); + end + else + begin + if (subcarrier_I) + corr_i_accum <= corr_i_accum + $signed({1'b0, adc_d}); + else + corr_i_accum <= corr_i_accum - $signed({1'b0, adc_d}); + + if (subcarrier_Q) + corr_q_accum <= corr_q_accum + $signed({1'b0, adc_d}); + else + corr_q_accum <= corr_q_accum - $signed({1'b0, adc_d}); + end + + // for each Q/I pair report two reader signal samples when sniffing. Store the 2nd. + if (corr_i_cnt == 6'd32) + after_hysteresis_prev <= after_hysteresis; + + // Then the result from last time is serialized and send out to the ARM. + // We get one report each cycle, and each report is 16 bits, so the + // ssp_clk should be the adc_clk divided by 64/16 = 4. + // ssp_clk frequency = 13,56MHz / 4 = 3.39MHz + + if (corr_i_cnt[1:0] == 2'b00) + begin + // Don't shift if we just loaded new data, obviously. + if (corr_i_cnt != 6'd0) + begin + corr_i_out[7:0] <= {corr_i_out[6:0], corr_q_out[7]}; + corr_q_out[7:1] <= corr_q_out[6:0]; + end + end + +end + + +// ssp clock and frame signal for communication to and from ARM +// _____ _____ _____ _ +// ssp_clk | |_____| |_____| |_____| +// _____ +// ssp_frame ___| |____________________________ +// ___________ ___________ ___________ _ +// ssp_d_in X___________X___________X___________X_ +// +// corr_i_cnt 0 1 2 3 4 5 6 7 8 9 10 11 12 ... +// + +reg ssp_clk; +reg ssp_frame; + +always @(negedge adc_clk) +begin + if (corr_i_cnt[1:0] == 2'b00) + ssp_clk <= 1'b1; + + if (corr_i_cnt[1:0] == 2'b10) + ssp_clk <= 1'b0; + + // set ssp_frame signal for corr_i_cnt = 1..3 + // (send one frame with 16 Bits) + if (corr_i_cnt == 6'd1) + ssp_frame <= 1'b1; + + if (corr_i_cnt == 6'd3) + ssp_frame <= 1'b0; +end + + +assign ssp_din = corr_i_out[7]; + + +// a jamming signal +reg jam_signal; +reg [3:0] jam_counter; + +always @(negedge adc_clk) +begin + if (corr_i_cnt == 6'd0) + begin + jam_counter <= jam_counter + 1; + jam_signal <= jam_counter[1] ^ jam_counter[3]; + end +end + +// Antenna drivers +reg pwr_hi, pwr_oe4; + +always @(*) +begin + if (minor_mode == `FPGA_HF_READER_MODE_SEND_SHALLOW_MOD) + begin + pwr_hi = ck_1356meg; + pwr_oe4 = ssp_dout; + end + else if (minor_mode == `FPGA_HF_READER_MODE_SEND_FULL_MOD) + begin + pwr_hi = ck_1356meg & ~ssp_dout; + pwr_oe4 = 1'b0; + end + else if (minor_mode == `FPGA_HF_READER_MODE_SEND_JAM) + begin + pwr_hi = ck_1356meg & jam_signal; + pwr_oe4 = 1'b0; + end + else if (minor_mode == `FPGA_HF_READER_MODE_SNIFF_IQ + || minor_mode == `FPGA_HF_READER_MODE_SNIFF_AMPLITUDE + || minor_mode == `FPGA_HF_READER_MODE_SNIFF_PHASE) + begin // all off + pwr_hi = 1'b0; + pwr_oe4 = 1'b0; + end + else // receiving from tag + begin + pwr_hi = ck_1356meg; + pwr_oe4 = 1'b0; + end +end + +// always on +assign pwr_oe1 = 1'b0; +assign pwr_oe3 = 1'b0; + +// Unused. +assign pwr_lo = 1'b0; +assign pwr_oe2 = 1'b0; + +// Debug Output +assign dbg = corr_i_cnt[3]; + +endmodule From 081f397ed7bfdc6326592c5d2da153982203d5bc Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 2 Jul 2020 11:49:46 +0200 Subject: [PATCH 003/139] remove --- fpga/xst_nfc.scr | 1 - 1 file changed, 1 deletion(-) delete mode 100644 fpga/xst_nfc.scr diff --git a/fpga/xst_nfc.scr b/fpga/xst_nfc.scr deleted file mode 100644 index 68f0d01e9..000000000 --- a/fpga/xst_nfc.scr +++ /dev/null @@ -1 +0,0 @@ -run -ifn fpga_nfc.v -ifmt Verilog -ofn fpga_nfc.ngc -ofmt NGC -p xc2s30-5-vq100 -top fpga_nfc -opt_mode area -opt_level 2 -resource_sharing yes -fsm_style bram -fsm_encoding compact From 59836bbb3b07082af0dbe48646bc86aca98a1460 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 2 Jul 2020 11:50:01 +0200 Subject: [PATCH 004/139] remove --- fpga/hi_read_rx_xcorr.v | 197 ---------------------------------------- fpga/hi_read_tx.v | 78 ---------------- 2 files changed, 275 deletions(-) delete mode 100644 fpga/hi_read_rx_xcorr.v delete mode 100644 fpga/hi_read_tx.v diff --git a/fpga/hi_read_rx_xcorr.v b/fpga/hi_read_rx_xcorr.v deleted file mode 100644 index be70b97b0..000000000 --- a/fpga/hi_read_rx_xcorr.v +++ /dev/null @@ -1,197 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Jonathan Westhues, April 2006 -//----------------------------------------------------------------------------- - -module hi_read_rx_xcorr( - pck0, ck_1356meg, ck_1356megb, - pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, - adc_d, adc_clk, - ssp_frame, ssp_din, ssp_dout, ssp_clk, - cross_hi, cross_lo, - dbg, - xcorr_is_848, snoop, xcorr_quarter_freq -); - input pck0, ck_1356meg, ck_1356megb; - output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; - input [7:0] adc_d; - output adc_clk; - input ssp_dout; - output ssp_frame, ssp_din, ssp_clk; - input cross_hi, cross_lo; - output dbg; - input xcorr_is_848, snoop, xcorr_quarter_freq; - -// Carrier is steady on through this, unless we're snooping. -assign pwr_hi = ck_1356megb & (~snoop); -assign pwr_oe1 = 1'b0; -assign pwr_oe3 = 1'b0; -assign pwr_oe4 = 1'b0; - -reg [2:0] fc_div; -always @(negedge ck_1356megb) - fc_div <= fc_div + 1; - -(* clock_signal = "yes" *) reg adc_clk; // sample frequency, always 16 * fc -always @(ck_1356megb, xcorr_is_848, xcorr_quarter_freq, fc_div) - if (xcorr_is_848 & ~xcorr_quarter_freq) // fc = 847.5 kHz, standard ISO14443B - adc_clk <= ck_1356megb; - else if (~xcorr_is_848 & ~xcorr_quarter_freq) // fc = 423.75 kHz - adc_clk <= fc_div[0]; - else if (xcorr_is_848 & xcorr_quarter_freq) // fc = 211.875 kHz - adc_clk <= fc_div[1]; - else // fc = 105.9375 kHz - adc_clk <= fc_div[2]; - -// When we're a reader, we just need to do the BPSK demod; but when we're an -// eavesdropper, we also need to pick out the commands sent by the reader, -// using AM. Do this the same way that we do it for the simulated tag. -reg after_hysteresis, after_hysteresis_prev, after_hysteresis_prev_prev; -reg [11:0] has_been_low_for; -always @(negedge adc_clk) -begin - if(& adc_d[7:0]) after_hysteresis <= 1'b1; - else if(~(| adc_d[7:0])) after_hysteresis <= 1'b0; - - if(after_hysteresis) - begin - has_been_low_for <= 7'b0; - end - else - begin - if(has_been_low_for == 12'd4095) - begin - has_been_low_for <= 12'd0; - after_hysteresis <= 1'b1; - end - else - has_been_low_for <= has_been_low_for + 1; - end -end - -// Let us report a correlation every 4 subcarrier cycles, or 4*16=64 samples, -// so we need a 6-bit counter. -reg [5:0] corr_i_cnt; - -// And a couple of registers in which to accumulate the correlations. Since -// load modulation saturates the ADC we have to use a large enough register -// 32 * 255 = 8160, which can be held in 13 bits. Add 1 bit for sign. -// -// The initial code assumed a phase shift of up to 25% and the accumulators were -// 11 bits (32 * 255 * 0,25 = 2040), we will pack all bits exceeding 11 bits into -// MSB. This prevents under/-overflows but preserves sensitivity on the lower end. -reg signed [13:0] corr_i_accum; -reg signed [13:0] corr_q_accum; - -// we will report maximum 8 significant bits -reg signed [7:0] corr_i_out; -reg signed [7:0] corr_q_out; - -// clock and frame signal for communication to ARM -reg ssp_clk; -reg ssp_frame; - - -always @(negedge adc_clk) -begin - corr_i_cnt <= corr_i_cnt + 1; -end - - -// ADC data appears on the rising edge, so sample it on the falling edge -always @(negedge adc_clk) -begin - // These are the correlators: we correlate against in-phase and quadrature - // versions of our reference signal, and keep the (signed) result to - // send out later over the SSP. - if(corr_i_cnt == 6'd0) - begin - // send 10 bits of tag signal, 4 MSBs are stuffed into 2 MSB - if(~corr_i_accum[13]) - corr_i_out <= {corr_i_accum[13], - corr_i_accum[12] | corr_i_accum[11] | corr_i_accum[10], - corr_i_accum[12] | corr_i_accum[11] | corr_i_accum[9], - corr_i_accum[8:4]}; - else - corr_i_out <= {corr_i_accum[13], - corr_i_accum[12] & corr_i_accum[11] & corr_i_accum[10], - corr_i_accum[12] & corr_i_accum[11] & corr_i_accum[9], - corr_i_accum[8:4]}; - - if(~corr_q_accum[13]) - corr_q_out <= {corr_q_accum[13], - corr_q_accum[12] | corr_q_accum[11] | corr_q_accum[10], - corr_q_accum[12] | corr_q_accum[11] | corr_q_accum[9], - corr_q_accum[8:4]}; - else - corr_q_out <= {corr_q_accum[13], - corr_q_accum[12] & corr_q_accum[11] & corr_q_accum[10], - corr_q_accum[12] & corr_q_accum[11] & corr_q_accum[9], - corr_q_accum[8:4]}; - - if(snoop) - begin - // replace LSB with 1 bit reader signal - corr_i_out[0] <= after_hysteresis_prev_prev; - corr_q_out[0] <= after_hysteresis_prev; - after_hysteresis_prev_prev <= after_hysteresis; - end - - corr_i_accum <= adc_d; - corr_q_accum <= adc_d; - end - else - begin - if(corr_i_cnt[3]) - corr_i_accum <= corr_i_accum - adc_d; - else - corr_i_accum <= corr_i_accum + adc_d; - - if(corr_i_cnt[3] == corr_i_cnt[2]) // phase shifted by pi/2 - corr_q_accum <= corr_q_accum + adc_d; - else - corr_q_accum <= corr_q_accum - adc_d; - - end - - // The logic in hi_simulate.v reports 4 samples per bit. We report two - // (I, Q) pairs per bit, so we should do 2 samples per pair. - if(corr_i_cnt == 6'd32) - after_hysteresis_prev <= after_hysteresis; - - // Then the result from last time is serialized and send out to the ARM. - // We get one report each cycle, and each report is 16 bits, so the - // ssp_clk should be the adc_clk divided by 64/16 = 4. - - if(corr_i_cnt[1:0] == 2'b10) - ssp_clk <= 1'b0; - - if(corr_i_cnt[1:0] == 2'b00) - begin - ssp_clk <= 1'b1; - // Don't shift if we just loaded new data, obviously. - if(corr_i_cnt != 6'd0) - begin - corr_i_out[7:0] <= {corr_i_out[6:0], corr_q_out[7]}; - corr_q_out[7:1] <= corr_q_out[6:0]; - end - end - - // set ssp_frame signal for corr_i_cnt = 0..3 and corr_i_cnt = 32..35 - // (send two frames with 8 Bits each) - if(corr_i_cnt[5:2] == 4'b0000 || corr_i_cnt[5:2] == 4'b1000) - ssp_frame = 1'b1; - else - ssp_frame = 1'b0; - -end - -assign ssp_din = corr_i_out[7]; - -assign dbg = corr_i_cnt[3]; - -// Unused. -assign pwr_lo = 1'b0; -assign pwr_oe2 = 1'b0; - -endmodule diff --git a/fpga/hi_read_tx.v b/fpga/hi_read_tx.v deleted file mode 100644 index f9550ed24..000000000 --- a/fpga/hi_read_tx.v +++ /dev/null @@ -1,78 +0,0 @@ -//----------------------------------------------------------------------------- -// The way that we connect things when transmitting a command to an ISO -// 15693 tag, using 100% modulation only for now. -// -// Jonathan Westhues, April 2006 -//----------------------------------------------------------------------------- - -module hi_read_tx( - pck0, ck_1356meg, ck_1356megb, - pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, - adc_d, adc_clk, - ssp_frame, ssp_din, ssp_dout, ssp_clk, - cross_hi, cross_lo, - dbg, - shallow_modulation -); - input pck0, ck_1356meg, ck_1356megb; - output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; - input [7:0] adc_d; - output adc_clk; - input ssp_dout; - output ssp_frame, ssp_din, ssp_clk; - input cross_hi, cross_lo; - output dbg; - input shallow_modulation; - -// low frequency outputs, not relevant -assign pwr_lo = 1'b0; -assign pwr_oe2 = 1'b0; - -// The high-frequency stuff. For now, for testing, just bring out the carrier, -// and allow the ARM to modulate it over the SSP. -reg pwr_hi; -reg pwr_oe1; -reg pwr_oe3; -reg pwr_oe4; - -always @(ck_1356megb or ssp_dout or shallow_modulation) -begin - if(shallow_modulation) - begin - pwr_hi <= ck_1356megb; - pwr_oe1 <= 1'b0; - pwr_oe3 <= 1'b0; - pwr_oe4 <= ~ssp_dout; - end - else - begin - pwr_hi <= ck_1356megb & ssp_dout; - pwr_oe1 <= 1'b0; - pwr_oe3 <= 1'b0; - pwr_oe4 <= 1'b0; - end -end - - -// Then just divide the 13.56 MHz clock down to produce appropriate clocks -// for the synchronous serial port. - -reg [6:0] hi_div_by_128; - -always @(posedge ck_1356meg) - hi_div_by_128 <= hi_div_by_128 + 1; - -assign ssp_clk = hi_div_by_128[6]; - -reg [2:0] hi_byte_div; - -always @(negedge ssp_clk) - hi_byte_div <= hi_byte_div + 1; - -assign ssp_frame = (hi_byte_div == 3'b000); - -assign ssp_din = 1'b0; - -assign dbg = ssp_frame; - -endmodule From da947affeb6a9b3d420c6a97a5731472edd02e13 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 2 Jul 2020 11:51:07 +0200 Subject: [PATCH 005/139] felica fpga scr file --- fpga/xst_felica.scr | 1 + 1 file changed, 1 insertion(+) create mode 100644 fpga/xst_felica.scr diff --git a/fpga/xst_felica.scr b/fpga/xst_felica.scr new file mode 100644 index 000000000..b069fab13 --- /dev/null +++ b/fpga/xst_felica.scr @@ -0,0 +1 @@ +run -ifn fpga_felica.v -ifmt Verilog -ofn fpga_felica.ngc -ofmt NGC -p xc2s30-5-vq100 -top fpga_felica -opt_mode area -opt_level 2 -resource_sharing yes -fsm_style bram -fsm_encoding compact From fdd0487c37d2993df1a7448efadba1ec00d9f20b Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 2 Jul 2020 11:51:52 +0200 Subject: [PATCH 006/139] updated bit files --- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/fpga_lf.bit | Bin 42175 -> 42175 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 9a054eec60c9bb30acbe343f4c6685039cd9d260..7d20e220fd35db4ff4743ce9abe5571984253151 100644 GIT binary patch literal 42175 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 diff --git a/fpga/fpga_lf.bit b/fpga/fpga_lf.bit index ba6b4322e6d470175b46d7116b49ddceaa3408f3..dca02afcc2e275d3dcb454e6c3629be3fb1af94a 100644 GIT binary patch literal 42175 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 BZ7Ki& literal 42175 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 Mz;F;TEd^Bx03$fU!2kdN From e2f0f08202d463bad9162e368f922e06e776bb64 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 2 Jul 2020 11:52:16 +0200 Subject: [PATCH 007/139] added felica bit file --- fpga/fpga_felica.bit | Bin 0 -> 42179 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 fpga/fpga_felica.bit diff --git a/fpga/fpga_felica.bit b/fpga/fpga_felica.bit new file mode 100644 index 0000000000000000000000000000000000000000..dc68b740b025bee0adeee9d72a787e1216b84b5c GIT binary patch literal 42179 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 literal 0 HcmV?d00001 From e144c793f012ed72ee6b15ceccf8808978c3da85 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 2 Jul 2020 12:32:55 +0200 Subject: [PATCH 008/139] chg: scc frames 16 or 8 bit wide --- armsrc/fpgaloader.c | 18 +++++++++------- armsrc/fpgaloader.h | 50 +++++++++++++++++++++++++++------------------ 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index f1f157eec..8313404bf 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -134,7 +134,7 @@ void SetupSpi(int mode) { // Set up the synchronous serial port with the set of options that fits // the FPGA mode. Both RX and TX are always enabled. //----------------------------------------------------------------------------- -void FpgaSetupSsc(void) { +void FpgaSetupSsc(uint16_t fpga_mode) { // First configure the GPIOs, and get ourselves a clock. AT91C_BASE_PIOA->PIO_ASR = GPIO_SSC_FRAME | @@ -152,12 +152,16 @@ void FpgaSetupSsc(void) { // data and frame signal is sampled on falling edge of RK AT91C_BASE_SSC->SSC_RCMR = SSC_CLOCK_MODE_SELECT(1) | SSC_CLOCK_MODE_START(1); - // 8 bits per transfer, no loopback, MSB first, 1 transfer per sync + // 8, 16 or 32 bits per transfer, no loopback, MSB first, 1 transfer per sync // pulse, no output sync - AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + if ((fpga_mode & FPGA_MAJOR_MODE_MASK) == FPGA_MAJOR_MODE_HF_READER && FpgaGetCurrent() == FPGA_BITSTREAM_HF) { + AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(16) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + } else { + AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + } - // TX clock comes from TK pin, no clock output, outputs change on falling - // edge of TK, frame sync is sampled on rising edge of TK, start TX on rising edge of TF + // TX clock comes from TK pin, no clock output, outputs change on rising edge of TK, + // TF (frame sync) is sampled on falling edge of TK, start TX on rising edge of TF AT91C_BASE_SSC->SSC_TCMR = SSC_CLOCK_MODE_SELECT(2) | SSC_CLOCK_MODE_START(5); // tx framing is the same as the rx framing @@ -171,7 +175,7 @@ void FpgaSetupSsc(void) { // a single buffer as a circular buffer (so that we just chain back to // ourselves, not to another buffer). //----------------------------------------------------------------------------- -bool FpgaSetupSscDma(uint8_t *buf, int len) { +bool FpgaSetupSscDma(uint8_t *buf, uint16_t len) { if (buf == NULL) return false; FpgaDisableSscDma(); @@ -439,7 +443,7 @@ void FpgaDownloadAndGo(int bitstream_version) { // The bit format is: C3 C2 C1 C0 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 // where C is the 4 bit command and D is the 12 bit data // -// @params cmd and v gets or over eachother. Take careful note of overlapping bits. +// @params cmd and v gets OR:ED over each other. Take careful note of overlapping bits. //----------------------------------------------------------------------------- void FpgaSendCommand(uint16_t cmd, uint16_t v) { SetupSpi(SPI_FPGA_MODE); diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h index 636c5b7b7..474c08e1a 100644 --- a/armsrc/fpgaloader.h +++ b/armsrc/fpgaloader.h @@ -52,23 +52,26 @@ thres| x x x x x x x x #define FPGA_CMD_TRACE_ENABLE (2<<12) // C // Definitions for the FPGA configuration word. +#define FPGA_MAJOR_MODE_MASK 0x01C0 +#define FPGA_MINOR_MODE_MASK 0x003F + // LF -#define FPGA_MAJOR_MODE_LF_READER (0<<5) -#define FPGA_MAJOR_MODE_LF_EDGE_DETECT (1<<5) -#define FPGA_MAJOR_MODE_LF_PASSTHRU (2<<5) -#define FPGA_MAJOR_MODE_LF_ADC (3<<5) +#define FPGA_MAJOR_MODE_LF_READER (0<<6) +#define FPGA_MAJOR_MODE_LF_EDGE_DETECT (1<<6) +#define FPGA_MAJOR_MODE_LF_PASSTHRU (2<<6) +#define FPGA_MAJOR_MODE_LF_ADC (3<<6) // HF -#define FPGA_MAJOR_MODE_HF_READER_TX (0<<5) // D -#define FPGA_MAJOR_MODE_HF_READER_RX_XCORR (1<<5) // D -#define FPGA_MAJOR_MODE_HF_SIMULATOR (2<<5) // D -#define FPGA_MAJOR_MODE_HF_ISO14443A (3<<5) // D -#define FPGA_MAJOR_MODE_HF_SNOOP (4<<5) // D -#define FPGA_MAJOR_MODE_HF_ISO18092 (5<<5) // D -#define FPGA_MAJOR_MODE_HF_GET_TRACE (6<<5) // D +#define FPGA_MAJOR_MODE_HF_READER (0<<6) // D +#define FPGA_MAJOR_MODE_HF_SIMULATOR (1<<6) // D +#define FPGA_MAJOR_MODE_HF_ISO14443A (2<<6) // D +#define FPGA_MAJOR_MODE_HF_SNOOP (3<<6) // D +#define FPGA_MAJOR_MODE_HF_ISO18092 (4<<6) // D +#define FPGA_MAJOR_MODE_HF_GET_TRACE (5<<6) // D // BOTH HF / LF -#define FPGA_MAJOR_MODE_OFF (7<<5) // D +#define FPGA_MAJOR_MODE_OFF (7<<6) // D + // Options for LF_READER #define FPGA_LF_ADC_READER_FIELD 0x1 @@ -78,13 +81,20 @@ thres| x x x x x x x x #define FPGA_LF_EDGE_DETECT_READER_FIELD 0x1 #define FPGA_LF_EDGE_DETECT_TOGGLE_MODE 0x2 -// Options for the HF reader, tx to tag -#define FPGA_HF_READER_TX_SHALLOW_MOD 0x1 +// Options for the HF reader +#define FPGA_HF_READER_MODE_RECEIVE_IQ (0<<0) +#define FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE (1<<0) +#define FPGA_HF_READER_MODE_RECEIVE_PHASE (2<<0) +#define FPGA_HF_READER_MODE_SEND_FULL_MOD (3<<0) +#define FPGA_HF_READER_MODE_SEND_SHALLOW_MOD (4<<0) +#define FPGA_HF_READER_MODE_SNOOP_IQ (5<<0) +#define FPGA_HF_READER_MODE_SNOOP_AMPLITUDE (6<<0) +#define FPGA_HF_READER_MODE_SNOOP_PHASE (7<<0) +#define FPGA_HF_READER_MODE_SEND_JAM (8<<0) -// Options for the HF reader, correlating against rx from tag -#define FPGA_HF_READER_RX_XCORR_848_KHZ 0x1 -#define FPGA_HF_READER_RX_XCORR_SNOOP 0x2 -#define FPGA_HF_READER_RX_XCORR_QUARTER 0x4 +#define FPGA_HF_READER_SUBCARRIER_848_KHZ (0<<4) +#define FPGA_HF_READER_SUBCARRIER_424_KHZ (1<<4) +#define FPGA_HF_READER_SUBCARRIER_212_KHZ (2<<4) // Options for the HF simulated tag, how to modulate #define FPGA_HF_SIMULATOR_NO_MODULATION 0x0 // 0000 @@ -112,9 +122,9 @@ void FpgaEnableTracing(void); void FpgaDisableTracing(void); void FpgaDownloadAndGo(int bitstream_version); // void FpgaGatherVersion(int bitstream_version, char *dst, int len); -void FpgaSetupSsc(void); +void FpgaSetupSsc(uint16_t fpga_mode); void SetupSpi(int mode); -bool FpgaSetupSscDma(uint8_t *buf, int len); +bool FpgaSetupSscDma(uint8_t *buf, uint16_t len); void Fpga_print_status(void); int FpgaGetCurrent(void); void SetAdcMuxFor(uint32_t whichGpio); From 2f4335d31e3031a82e809d6fd253237889c2a800 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 2 Jul 2020 12:33:23 +0200 Subject: [PATCH 009/139] chg: ssp clock changes --- armsrc/ticks.c | 56 +++++++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/armsrc/ticks.c b/armsrc/ticks.c index 0ce55e417..e9cbea219 100644 --- a/armsrc/ticks.c +++ b/armsrc/ticks.c @@ -117,47 +117,61 @@ uint32_t RAMFUNC GetCountUS(void) { void StartCountSspClk(void) { AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1) | (1 << AT91C_ID_TC2); // Enable Clock to all timers AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_TIOA1 // XC0 Clock = TIOA1 - | AT91C_TCB_TC1XC1S_NONE // XC1 Clock = none - | AT91C_TCB_TC2XC2S_TIOA0; // XC2 Clock = TIOA0 + | AT91C_TCB_TC1XC1S_NONE // XC1 Clock = none + | AT91C_TCB_TC2XC2S_TIOA0; // XC2 Clock = TIOA0 // configure TC1 to create a short pulse on TIOA1 when a rising edge on TIOB1 (= ssp_clk from FPGA) occurs: AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // disable TC1 AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK // TC1 Clock = MCK(48MHz)/2 = 24MHz - | AT91C_TC_CPCSTOP // Stop clock on RC compare - | AT91C_TC_EEVTEDG_RISING // Trigger on rising edge of Event - | AT91C_TC_EEVT_TIOB // Event-Source: TIOB1 (= ssp_clk from FPGA = 13,56MHz/16) - | AT91C_TC_ENETRG // Enable external trigger event - | AT91C_TC_WAVESEL_UP // Upmode without automatic trigger on RC compare - | AT91C_TC_WAVE // Waveform Mode - | AT91C_TC_AEEVT_SET // Set TIOA1 on external event - | AT91C_TC_ACPC_CLEAR; // Clear TIOA1 on RC Compare - AT91C_BASE_TC1->TC_RC = 0x04; // RC Compare value = 0x04 + | AT91C_TC_CPCSTOP // Stop clock on RC compare + | AT91C_TC_EEVTEDG_RISING // Trigger on rising edge of Event + | AT91C_TC_EEVT_TIOB // Event-Source: TIOB1 (= ssp_clk from FPGA = 13,56MHz/16) + | AT91C_TC_ENETRG // Enable external trigger event + | AT91C_TC_WAVESEL_UP // Upmode without automatic trigger on RC compare + | AT91C_TC_WAVE // Waveform Mode + | AT91C_TC_AEEVT_SET // Set TIOA1 on external event + | AT91C_TC_ACPC_CLEAR; // Clear TIOA1 on RC Compare + AT91C_BASE_TC1->TC_RC = 0x01; // RC Compare value = 0x01, pulse width to TC0 // use TC0 to count TIOA1 pulses AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // disable TC0 AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_XC0 // TC0 clock = XC0 clock = TIOA1 - | AT91C_TC_WAVE // Waveform Mode - | AT91C_TC_WAVESEL_UP // just count - | AT91C_TC_ACPA_CLEAR // Clear TIOA0 on RA Compare - | AT91C_TC_ACPC_SET; // Set TIOA0 on RC Compare + | AT91C_TC_WAVE // Waveform Mode + | AT91C_TC_WAVESEL_UP // just count + | AT91C_TC_ACPA_CLEAR // Clear TIOA0 on RA Compare + | AT91C_TC_ACPC_SET; // Set TIOA0 on RC Compare AT91C_BASE_TC0->TC_RA = 1; // RA Compare value = 1; pulse width to TC2 AT91C_BASE_TC0->TC_RC = 0; // RC Compare value = 0; increment TC2 on overflow // use TC2 to count TIOA0 pulses (giving us a 32bit counter (TC0/TC2) clocked by ssp_clk) AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS; // disable TC2 AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_XC2 // TC2 clock = XC2 clock = TIOA0 - | AT91C_TC_WAVE // Waveform Mode - | AT91C_TC_WAVESEL_UP; // just count + | AT91C_TC_WAVE // Waveform Mode + | AT91C_TC_WAVESEL_UP; // just count AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // enable and reset TC0 AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // enable and reset TC1 AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // enable and reset TC2 + // // synchronize the counter with the ssp_frame signal. - // Note: FPGA must be in any iso14443 mode, otherwise the frame signal would not be present - while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME)); // wait for ssp_frame to go high (start of frame) - while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME); // wait for ssp_frame to be low - while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high + // Note: FPGA must be in a FPGA mode with SSC transfer, otherwise SSC_FRAME and SSC_CLK signals would not be present + // + while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME); // wait for ssp_frame to be low + while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME)); // wait for ssp_frame to go high (start of frame) + while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high; 1st ssp_clk after start of frame + while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK); // wait for ssp_clk to go low; + while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high; 2nd ssp_clk after start of frame + if ((AT91C_BASE_SSC->SSC_RFMR & SSC_FRAME_MODE_BITS_IN_WORD(32)) == SSC_FRAME_MODE_BITS_IN_WORD(16)) { // 16bit frame + while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK); // wait for ssp_clk to go low; + while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high; 3rd ssp_clk after start of frame + while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK); // wait for ssp_clk to go low; + while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high; 4th ssp_clk after start of frame + while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK); // wait for ssp_clk to go low; + while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high; 5th ssp_clk after start of frame + while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK); // wait for ssp_clk to go low; + while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high; 6th ssp_clk after start of frame + } // note: up to now two ssp_clk rising edges have passed since the rising edge of ssp_frame // it is now safe to assert a sync signal. This sets all timers to 0 on next active clock edge From 9130474818cae82596c10ba81979af9436b69eb3 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 2 Jul 2020 12:33:53 +0200 Subject: [PATCH 010/139] adaptations to new code --- armsrc/em4x50.c | 2 +- armsrc/lfadc.c | 2 +- armsrc/thinfilm.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/armsrc/em4x50.c b/armsrc/em4x50.c index ddc297591..93da6425f 100644 --- a/armsrc/em4x50.c +++ b/armsrc/em4x50.c @@ -192,7 +192,7 @@ static void em4x50_setup_read(void) { // 50ms for the resonant antenna to settle. SpinDelay(50); // Now set up the SSC to get the ADC samples that are now streaming at us. - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER); // start a 1.5ticks is 1us StartTicks(); diff --git a/armsrc/lfadc.c b/armsrc/lfadc.c index 8c453bdc9..2c46f5581 100644 --- a/armsrc/lfadc.c +++ b/armsrc/lfadc.c @@ -181,7 +181,7 @@ void lf_init(bool reader, bool simulate) { SetAdcMuxFor(GPIO_MUXSEL_LOPKD); // Now set up the SSC to get the ADC samples that are now streaming at us. - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER); // When in reader mode, give the field a bit of time to settle. // 313T0 = 313 * 8us = 2504us = 2.5ms Hitag2 tags needs to be fully powered. diff --git a/armsrc/thinfilm.c b/armsrc/thinfilm.c index a77a70744..16c91c8bf 100644 --- a/armsrc/thinfilm.c +++ b/armsrc/thinfilm.c @@ -122,7 +122,7 @@ void SimulateThinFilm(uint8_t *data, size_t len) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); From 85053d106f0f58a808ad0ec6bedf985a9afa2384 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 2 Jul 2020 12:34:48 +0200 Subject: [PATCH 011/139] adapt to fpga changes. And bug hunting COTAG functionality that doesnt work --- armsrc/lfsampling.c | 50 +++++++++++++++------------------------------ 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index f77084b3d..aaf7ec86c 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -51,11 +51,11 @@ void printConfig(void) { } void printSamples(void) { - DbpString(_CYAN_("LF Sampling memory")); - Dbprintf(" decimation counter.....%d ", samples.dec_counter); - Dbprintf(" sum.....%u ", samples.sum); - Dbprintf(" counter.....%u ", samples.counter); - Dbprintf(" total saved.....%u ", samples.total_saved); + DbpString(_CYAN_("LF Sampling memory usage")); +// Dbprintf(" decimation counter...%d", samples.dec_counter); +// Dbprintf(" sum..................%u", samples.sum); + Dbprintf(" counter.............." _YELLOW_("%u"), samples.counter); + Dbprintf(" total saved.........." _YELLOW_("%u"), samples.total_saved); print_stack_usage(); } @@ -241,7 +241,7 @@ void LFSetupFPGAForADC(int divisor, bool reader_field) { SpinDelay(50); // Now set up the SSC to get the ADC samples that are now streaming at us. - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER); // start a 1.5ticks is 1us StartTicks(); @@ -480,19 +480,19 @@ void doT55x7Acquisition(size_t sample_size) { #define COTAG_T1 384 #define COTAG_T2 (COTAG_T1>>1) -#define COTAG_ONE_THRESHOLD 128+10 -#define COTAG_ZERO_THRESHOLD 128-10 +#define COTAG_ONE_THRESHOLD 128+5 +#define COTAG_ZERO_THRESHOLD 128-5 #ifndef COTAG_BITS #define COTAG_BITS 264 #endif -void doCotagAcquisition(size_t sample_size) { +void doCotagAcquisition() { uint8_t *dest = BigBuf_get_addr(); - uint16_t bufsize = MIN(sample_size, BigBuf_max_traceLen()); + uint16_t bufsize = BigBuf_max_traceLen(); dest[0] = 0; uint8_t firsthigh = 0, firstlow = 0; - uint16_t i = 0, noise_counter = 0, checker = 0; + uint16_t i = 0, noise_counter = 0; if (DBGLEVEL >= DBG_DEBUG) { Dbprintf("doCotagAcquisition - after init"); @@ -504,21 +504,8 @@ void doCotagAcquisition(size_t sample_size) { if (BUTTON_PRESS()) break; - if (checker == 4000) { - if (data_available()) - break; - else - checker = 0; - } else { - ++checker; - } - WDT_HIT(); - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { - LED_D_ON(); - } - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; @@ -551,9 +538,13 @@ void doCotagAcquisition(size_t sample_size) { } } + Dbprintf("doCotagAcquisition - %u high %u == 1 low %u == 1", i, firsthigh, firstlow); + // Ensure that DC offset removal and noise check is performed for any device-side processing removeSignalOffset(dest, bufsize); + printSamples(); computeSignalProperties(dest, bufsize); + printSamples(); } uint32_t doCotagAcquisitionManchester(void) { @@ -565,7 +556,7 @@ uint32_t doCotagAcquisitionManchester(void) { uint8_t firsthigh = 0, firstlow = 0; uint8_t curr = 0, prev = 0; uint16_t sample_counter = 0, period = 0; - uint16_t noise_counter = 0, checker = 0; + uint16_t noise_counter = 0; if (DBGLEVEL >= DBG_DEBUG) { Dbprintf("doCotagAcquisitionManchester - after init"); @@ -576,15 +567,6 @@ uint32_t doCotagAcquisitionManchester(void) { if (BUTTON_PRESS()) break; - - if (checker == 4000) { - if ( data_available()) - break; - else - checker = 0; - } else { - ++checker; - } WDT_HIT(); From ee69987210cf53731b944e20766289b8ab98e273 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 2 Jul 2020 12:35:24 +0200 Subject: [PATCH 012/139] chg: third fpga image for felica --- armsrc/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/Makefile b/armsrc/Makefile index 1bbbf2ba7..44ca3ab6b 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -79,7 +79,7 @@ endif include Standalone/Makefile.inc #the FPGA bitstream files. Note: order matters! -FPGA_BITSTREAMS = fpga_lf.bit fpga_hf.bit +FPGA_BITSTREAMS = fpga_lf.bit fpga_hf.bit fpga_felica.bit #the lz4 source files required for decompressing the fpga config at run time SRC_LZ4 = lz4.c From ecdd91392bb79da1e7e8192baf46c593eab236d9 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 2 Jul 2020 12:35:33 +0200 Subject: [PATCH 013/139] adapt to fpga changes. And bug hunting COTAG functionality that doesnt work --- armsrc/lfsampling.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/lfsampling.h b/armsrc/lfsampling.h index 9b8c4c6b1..28721f6ca 100644 --- a/armsrc/lfsampling.h +++ b/armsrc/lfsampling.h @@ -21,7 +21,7 @@ typedef struct { * acquisition of Cotag LF signal. Similar to other LF, since the Cotag has such long datarate RF/384 * and is Manchester?, we directly gather the manchester data into bigbuff **/ -void doCotagAcquisition(size_t sample_size); +void doCotagAcquisition(void); uint32_t doCotagAcquisitionManchester(void); /** From 4043832c623829e00cdba59518dd2bb611b42a70 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 2 Jul 2020 12:35:54 +0200 Subject: [PATCH 014/139] adapt to fpga changes. And bug hunting COTAG functionality that doesnt work --- armsrc/lfops.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 7e907da09..4a9373cec 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -678,7 +678,7 @@ void AcquireTiType(void) { AT91C_BASE_SSC->SSC_TCMR = 0; // Transmit Frame Mode Register AT91C_BASE_SSC->SSC_TFMR = 0; - // iceman, FpgaSetupSsc() ?? the code above? can it be replaced? + // iceman, FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER) ?? the code above? can it be replaced? LED_D_ON(); // modulate antenna @@ -721,7 +721,7 @@ void AcquireTiType(void) { } // reset SSC - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER); } // arguments: 64bit data split into 32bit idhi:idlo and optional 16bit crc @@ -2597,24 +2597,35 @@ void Cotag(uint32_t arg0) { LFSetupFPGAForADC(LF_FREQ2DIV(132), true); //clear buffer now so it does not interfere with timing later + BigBuf_free(); BigBuf_Clear_ext(false); //send COTAG start pulse +/* ON(740) OFF(2035) ON(3330) OFF(2035) ON(740) OFF(2035) ON(1000) +*/ + + ON(800) OFF(2200) + ON(3600) OFF(2200) + ON(800) OFF(2200) + ON(3400) + + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_FREQ2DIV(125)); switch (rawsignal) { case 0: - doCotagAcquisition(40000); + doCotagAcquisition(); break; case 1: doCotagAcquisitionManchester(); break; - case 2: + case 2: { DoAcquisition_config(false, 0); break; + } } // Turn the field off From 837efdf5c4e92a8789aaa51f43d2de169ae5dd16 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 2 Jul 2020 12:36:24 +0200 Subject: [PATCH 015/139] chg: adapt to fpga changes --- armsrc/legicrf.c | 16 +++++----------- armsrc/legicrfsim.c | 5 ++--- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index 21b0c0a42..1d2693347 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -152,7 +152,7 @@ static void tx_bit(bool bit) { //----------------------------------------------------------------------------- static void tx_frame(uint32_t frame, uint8_t len) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_FULL_MOD); // wait for next tx timeslot last_frame_end += RWD_FRAME_WAIT; @@ -180,9 +180,7 @@ static void tx_frame(uint32_t frame, uint8_t len) { } static uint32_t rx_frame(uint8_t len) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR - | FPGA_HF_READER_RX_XCORR_848_KHZ - | FPGA_HF_READER_RX_XCORR_QUARTER); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_212_KHZ | FPGA_HF_READER_MODE_RECEIVE_IQ); // hold sampling until card is expected to respond last_frame_end += TAG_FRAME_WAIT; @@ -210,9 +208,7 @@ static uint32_t rx_frame(uint8_t len) { static bool rx_ack(void) { // change fpga into rx mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR - | FPGA_HF_READER_RX_XCORR_848_KHZ - | FPGA_HF_READER_RX_XCORR_QUARTER); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_212_KHZ | FPGA_HF_READER_MODE_RECEIVE_IQ); // hold sampling until card is expected to respond last_frame_end += TAG_FRAME_WAIT; @@ -279,14 +275,12 @@ static int init_card(uint8_t cardtype, legic_card_select_t *p_card) { static void init_reader(bool clear_mem) { // configure FPGA FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR - | FPGA_HF_READER_RX_XCORR_848_KHZ - | FPGA_HF_READER_RX_XCORR_QUARTER); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_212_KHZ | FPGA_HF_READER_MODE_RECEIVE_IQ); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); LED_A_ON(); // configure SSC with defaults - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // re-claim GPIO_SSC_DOUT as GPIO and enable output AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; diff --git a/armsrc/legicrfsim.c b/armsrc/legicrfsim.c index 35eefc56c..2ca65f54a 100644 --- a/armsrc/legicrfsim.c +++ b/armsrc/legicrfsim.c @@ -303,12 +303,11 @@ static int32_t init_card(uint8_t cardtype, legic_card_select_t *p_card) { static void init_tag(void) { // configure FPGA FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR - | FPGA_HF_SIMULATOR_MODULATE_212K); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_212K); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // configure SSC with defaults - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); // first pull output to low to prevent glitches then re-claim GPIO_SSC_DOUT LOW(GPIO_SSC_DOUT); From fce4c1d13f208ce112d508b5cea44928f7db097f Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 2 Jul 2020 12:36:49 +0200 Subject: [PATCH 016/139] chg: adapt to fpga changes --- armsrc/appmain.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 95b42982a..a252bbe8e 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -213,7 +213,7 @@ static void MeasureAntennaTuning(void) { LED_A_ON(); // Let the FPGA drive the high-frequency antenna around 13.56 MHz. FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); SpinDelay(50); #if defined RDV4 @@ -1616,7 +1616,7 @@ static void PacketReceived(PacketCommandNG *packet) { case 1: // MEASURE_ANTENNA_TUNING_HF_START // Let the FPGA drive the high-frequency antenna around 13.56 MHz. FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); reply_ng(CMD_MEASURE_ANTENNA_TUNING_HF, PM3_SUCCESS, NULL, 0); break; case 2: From 80cda059b17c8ce7c01435a0a011c1b007c960eb Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 2 Jul 2020 12:37:07 +0200 Subject: [PATCH 017/139] chg: adapt to fpga changes --- armsrc/iso15693.c | 2003 +++++++++++++++++++++++++++++++-------------- armsrc/iso15693.h | 21 +- 2 files changed, 1411 insertions(+), 613 deletions(-) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index d88bae1f0..a82bb9a07 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -3,24 +3,22 @@ // Modified by Greg Jones, Jan 2009 // Modified by Adrian Dabrowski "atrox", Mar-Sept 2010,Oct 2011 // Modified by Christian Herrmann "iceman", 2017 +// Modified by piwi, Oct 2018 // // 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. //----------------------------------------------------------------------------- // Routines to support ISO 15693. This includes both the reader software and -// the `fake tag' modes, but at the moment I've implemented only the reader -// stuff, and that barely. -// Modified to perform modulation onboard in arm rather than on PC -// Also added additional reader commands (SELECT, READ etc.) +// the `fake tag' modes. //----------------------------------------------------------------------------- -// The ISO 15693 describes two transmission modes from reader to tag, and 4 -// transmission modes from tag to reader. As of Mar 2010 this code only -// supports one of each: "1of4" mode from reader to tag, and the highspeed -// variant with one subcarrier from card to reader. -// As long, as the card fully support ISO 15693 this is no problem, since the -// reader chooses both data rates, but some non-standard tags do not. Further for -// the simulation to work, we will need to support all data rates. + +// The ISO 15693 describes two transmission modes from reader to tag, and four +// transmission modes from tag to reader. As of Oct 2018 this code supports +// both reader modes and the high speed variant with one subcarrier from card to reader. +// As long as the card fully support ISO 15693 this is no problem, since the +// reader chooses both data rates, but some non-standard tags do not. +// For card simulation, the code supports both high and low speed modes with one subcarrier. // // VCD (reader) -> VICC (tag) // 1 out of 256: @@ -47,15 +45,11 @@ // *) UID is always used "transmission order" (LSB), which is reverse to display order // TODO / BUGS / ISSUES: -// *) writing to tags takes longer: we miss the answer from the tag in most cases -// -> tweak the read-timeout times -// *) signal decoding from the card is still a bit shaky. -// *) signal decoding is unable to detect collissions. -// *) add anti-collission support for inventory-commands +// *) signal decoding is unable to detect collisions. +// *) add anti-collision support for inventory-commands // *) read security status of a block -// *) sniffing and simulation do only support one transmission mode. need to support -// all 8 transmission combinations -// *) remove or refactor code under "depricated" +// *) sniffing and simulation do not support two subcarrier modes. +// *) remove or refactor code under "deprecated" // *) document all the functions #include "iso15693.h" @@ -72,12 +66,36 @@ #include "ticks.h" #include "BigBuf.h" #include "crc16.h" + +// Delays in SSP_CLK ticks. +// SSP_CLK runs at 13,56MHz / 32 = 423.75kHz when simulating a tag +#define DELAY_READER_TO_ARM 8 +#define DELAY_ARM_TO_READER 0 + +//SSP_CLK runs at 13.56MHz / 4 = 3,39MHz when acting as reader. All values should be multiples of 16 +#define DELAY_ARM_TO_TAG 16 +#define DELAY_TAG_TO_ARM 32 + +//SSP_CLK runs at 13.56MHz / 4 = 3,39MHz when snooping. All values should be multiples of 16 +#define DELAY_TAG_TO_ARM_SNIFF 32 +#define DELAY_READER_TO_ARM_SNIFF 32 + +// times in samples @ 212kHz when acting as reader +#define ISO15693_READER_TIMEOUT 330 // 330/212kHz = 1558us, should be even enough for iClass tags responding to ACTALL +#define ISO15693_READER_TIMEOUT_WRITE 4700 // 4700/212kHz = 22ms, nominal 20ms + /////////////////////////////////////////////////////////////////////// // ISO 15693 Part 2 - Air Interface -// This section basicly contains transmission and receiving of bits +// This section basically contains transmission and receiving of bits /////////////////////////////////////////////////////////////////////// +// buffers +#define ISO15693_DMA_BUFFER_SIZE 256 // must be a power of 2 +#define ISO15693_MAX_RESPONSE_LENGTH 36 // allows read single block with the maximum block size of 256bits. Read multiple blocks not supported yet +#define ISO15693_MAX_COMMAND_LENGTH 45 // allows write single block with the maximum block size of 256bits. Write multiple blocks not supported yet + + // 32 + 2 crc + 1 #define ISO15_MAX_FRAME 35 #define CMD_ID_RESP 5 @@ -93,397 +111,981 @@ #define CheckCrc15(data, len) check_crc(CRC_15693, (data), (len)) #define AddCrc15(data, len) compute_crc(CRC_15693, (data), (len), (data)+(len), (data)+(len)+1) -static void BuildIdentifyRequest(uint8_t *cmdout); -//static void BuildReadBlockRequest(uint8_t *cmdout, uint8_t *uid, uint8_t blockNumber ); -static void BuildInventoryResponse(uint8_t *cmdout, uint8_t *uid); +static void BuildIdentifyRequest(uint8_t *cmd); +static void BuildInventoryResponse(uint8_t *uid); // --------------------------- // Signal Processing // --------------------------- // prepare data using "1 out of 4" code for later transmission -// resulting data rate is 26,48 kbit/s (fc/512) +// resulting data rate is 26.48 kbit/s (fc/512) // cmd ... data // n ... length of data static void CodeIso15693AsReader(uint8_t *cmd, int n) { - int i, j; - ToSendReset(); + ToSendReset(); - // Give it a bit of slack at the beginning - for (i = 0; i < 24; i++) - ToSendStuffBit(1); + // SOF for 1of4 + ToSend[++ToSendMax] = 0x84; //10000100 - // SOF for 1of4 - ToSendStuffBit(0); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(0); - ToSendStuffBit(1); - ToSendStuffBit(1); - for (i = 0; i < n; i++) { - for (j = 0; j < 8; j += 2) { - int these = (cmd[i] >> j) & 3; - switch (these) { - case 0: - ToSendStuffBit(1); - ToSendStuffBit(0); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - break; - case 1: - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(0); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - break; - case 2: - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(0); - ToSendStuffBit(1); - ToSendStuffBit(1); - break; - case 3: - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(0); - break; - } - } - } - // EOF - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(0); - ToSendStuffBit(1); + // data + for (int i = 0; i < n; i++) { + for (int j = 0; j < 8; j += 2) { + int these = (cmd[i] >> j) & 0x03; + switch(these) { + case 0: + ToSend[++ToSendMax] = 0x40; //01000000 + break; + case 1: + ToSend[++ToSendMax] = 0x10; //00010000 + break; + case 2: + ToSend[++ToSendMax] = 0x04; //00000100 + break; + case 3: + ToSend[++ToSendMax] = 0x01; //00000001 + break; + } + } + } - // And slack at the end, too. - for (i = 0; i < 24; i++) - ToSendStuffBit(1); + // EOF + ToSend[++ToSendMax] = 0x20; //0010 + 0000 padding + + ToSendMax++; } -// encode data using "1 out of 256" sheme +// Encode EOF only +static void CodeIso15693AsReaderEOF(void) { + ToSendReset(); + ToSend[++ToSendMax] = 0x20; + ToSendMax++; +} + + +// encode data using "1 out of 256" scheme // data rate is 1,66 kbit/s (fc/8192) // is designed for more robust communication over longer distances static void CodeIso15693AsReader256(uint8_t *cmd, int n) { - int i, j; - ToSendReset(); + ToSendReset(); - // Give it a bit of slack at the beginning - for (i = 0; i < 24; i++) - ToSendStuffBit(1); + // SOF for 1of256 + ToSend[++ToSendMax] = 0x81; //10000001 - // SOF for 1of256 - ToSendStuffBit(0); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(0); + // data + for(int i = 0; i < n; i++) { + for (int j = 0; j <= 255; j++) { + if (cmd[i] == j) { + ToSendStuffBit(0); + ToSendStuffBit(1); + } else { + ToSendStuffBit(0); + ToSendStuffBit(0); + } + } + } - for (i = 0; i < n; i++) { - for (j = 0; j <= 255; j++) { - if (cmd[i] == j) { - ToSendStuffBit(1); - ToSendStuffBit(0); - } else { - ToSendStuffBit(1); - ToSendStuffBit(1); - } - } - } - // EOF - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(0); - ToSendStuffBit(1); + // EOF + ToSend[++ToSendMax] = 0x20; //0010 + 0000 padding - // And slack at the end, too. - for (i = 0; i < 24; i++) - ToSendStuffBit(1); + ToSendMax++; } -// Transmit the command (to the tag) that was placed in ToSend[]. -static void TransmitTo15693Tag(const uint8_t *cmd, int len, int *samples, int *wait) { +static const uint8_t encode_4bits[16] = { 0xaa, 0x6a, 0x9a, 0x5a, 0xa6, 0x66, 0x96, 0x56, 0xa9, 0x69, 0x99, 0x59, 0xa5, 0x65, 0x95, 0x55 }; - int c; - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); +static void CodeIso15693AsTag(uint8_t *cmd, size_t len) { + /* + * SOF comprises 3 parts; + * * An unmodulated time of 56.64 us + * * 24 pulses of 423.75 kHz (fc/32) + * * A logic 1, which starts with an unmodulated time of 18.88us + * followed by 8 pulses of 423.75kHz (fc/32) + * + * EOF comprises 3 parts: + * - A logic 0 (which starts with 8 pulses of fc/32 followed by an unmodulated + * time of 18.88us. + * - 24 pulses of fc/32 + * - An unmodulated time of 56.64 us + * + * A logic 0 starts with 8 pulses of fc/32 + * followed by an unmodulated time of 256/fc (~18,88us). + * + * A logic 0 starts with unmodulated time of 256/fc (~18,88us) followed by + * 8 pulses of fc/32 (also 18.88us) + * + * A bit here becomes 8 pulses of fc/32. Therefore: + * The SOF can be written as 00011101 = 0x1D + * The EOF can be written as 10111000 = 0xb8 + * A logic 1 is 01 + * A logic 0 is 10 + * + * */ - if (wait) { - for (c = 0; c < *wait;) { - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x00; // For exact timing! - ++c; - } - WDT_HIT(); - } - } + ToSendReset(); - c = 0; - for (;;) { - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = cmd[c]; - if (++c >= len) break; - } - WDT_HIT(); - } + // SOF + ToSend[++ToSendMax] = 0x1D; // 00011101 - if (samples) { - if (wait) - *samples = (c + *wait) << 3; - else - *samples = c << 3; - } + // data + for (int i = 0; i < len; i++) { + ToSend[++ToSendMax] = encode_4bits[cmd[i] & 0xF]; + ToSend[++ToSendMax] = encode_4bits[cmd[i] >> 4]; + } + + // EOF + ToSend[++ToSendMax] = 0xB8; // 10111000 + + ToSendMax++; +} + +// Transmit the command (to the tag) that was placed in cmd[]. +static void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time) { + + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_FULL_MOD); + + if (*start_time < DELAY_ARM_TO_TAG) { + *start_time = DELAY_ARM_TO_TAG; + } + + *start_time = (*start_time - DELAY_ARM_TO_TAG) & 0xfffffff0; + + if (GetCountSspClk() > *start_time) { // we may miss the intended time + *start_time = (GetCountSspClk() + 16) & 0xfffffff0; // next possible time + } + + while (GetCountSspClk() < *start_time) + /* wait */ ; + + LED_B_ON(); + for (int c = 0; c < len; c++) { + uint8_t data = cmd[c]; + for (int i = 0; i < 8; i++) { + uint16_t send_word = (data & 0x80) ? 0xffff : 0x0000; + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + AT91C_BASE_SSC->SSC_THR = send_word; + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + AT91C_BASE_SSC->SSC_THR = send_word; + data <<= 1; + } + WDT_HIT(); + } + LED_B_OFF(); + + *start_time = *start_time + DELAY_ARM_TO_TAG; } //----------------------------------------------------------------------------- -// Transmit the command (to the reader) that was placed in ToSend[]. +// Transmit the command (to the reader) that was placed in cmd[]. //----------------------------------------------------------------------------- -static void TransmitTo15693Reader(const uint8_t *cmd, int len, int *samples, int *wait) { - int c = 0; - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_424K); +static void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, uint32_t slot_time, bool slow) { - if (wait) { - for (c = 0; c < *wait;) { - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x00; // For exact timing! - ++c; - } - WDT_HIT(); - } - } + // don't use the FPGA_HF_SIMULATOR_MODULATE_424K_8BIT minor mode. It would spoil GetCountSspClk() + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_424K); - c = 0; - for (;;) { - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = cmd[c]; - if (++c >= len) break; - } - WDT_HIT(); - } - if (samples) { - if (wait) - *samples = (c + *wait) << 3; - else - *samples = c << 3; - } + uint32_t modulation_start_time = *start_time - DELAY_ARM_TO_READER + 3 * 8; // no need to transfer the unmodulated start of SOF + + while (GetCountSspClk() > (modulation_start_time & 0xfffffff8) + 3) { // we will miss the intended time + if (slot_time) { + modulation_start_time += slot_time; // use next available slot + } else { + modulation_start_time = (modulation_start_time & 0xfffffff8) + 8; // next possible time + } + } + + while (GetCountSspClk() < (modulation_start_time & 0xfffffff8)) + /* wait */ ; + + uint8_t shift_delay = modulation_start_time & 0x00000007; + + *start_time = modulation_start_time + DELAY_ARM_TO_READER - 3 * 8; + + LED_C_ON(); + uint8_t bits_to_shift = 0x00; + uint8_t bits_to_send = 0x00; + for (size_t c = 0; c < len; c++) { + for (int i = (c==0?4:7); i >= 0; i--) { + uint8_t cmd_bits = ((cmd[c] >> i) & 0x01) ? 0xff : 0x00; + for (int j = 0; j < (slow?4:1); ) { + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { + bits_to_send = bits_to_shift << (8 - shift_delay) | cmd_bits >> shift_delay; + AT91C_BASE_SSC->SSC_THR = bits_to_send; + bits_to_shift = cmd_bits; + j++; + } + } + } + WDT_HIT(); + } + // send the remaining bits, padded with 0: + bits_to_send = bits_to_shift << (8 - shift_delay); + for ( ; ; ) { + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { + AT91C_BASE_SSC->SSC_THR = bits_to_send; + break; + } + } + LED_C_OFF(); } +//============================================================================= +// An ISO 15693 decoder for tag responses (one subcarrier only). +// Uses cross correlation to identify each bit and EOF. +// This function is called 8 times per bit (every 2 subcarrier cycles). +// Subcarrier frequency fs is 424kHz, 1/fs = 2,36us, +// i.e. function is called every 4,72us +// LED handling: +// LED C -> ON once we have received the SOF and are expecting the rest. +// LED C -> OFF once we have received EOF or are unsynced +// +// Returns: true if we received a EOF +// false if we are still waiting for some more +//============================================================================= + +#define NOISE_THRESHOLD 80 // don't try to correlate noise +#define MAX_PREVIOUS_AMPLITUDE (-1 - NOISE_THRESHOLD) + +typedef struct DecodeTag { + enum { + STATE_TAG_SOF_LOW, + STATE_TAG_SOF_RISING_EDGE, + STATE_TAG_SOF_HIGH, + STATE_TAG_SOF_HIGH_END, + STATE_TAG_RECEIVING_DATA, + STATE_TAG_EOF, + STATE_TAG_EOF_TAIL + } state; + int bitCount; + int posCount; + enum { + LOGIC0, + LOGIC1, + SOF_PART1, + SOF_PART2 + } lastBit; + uint16_t shiftReg; + uint16_t max_len; + uint8_t *output; + int len; + int sum1, sum2; + int threshold_sof; + int threshold_half; + uint16_t previous_amplitude; +} DecodeTag_t; + //----------------------------------------------------------------------------- // DEMODULATE tag answer //----------------------------------------------------------------------------- -static int DemodAnswer(uint8_t *received, uint8_t *dest, uint16_t samplecount) { +static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *DecodeTag) { + switch (DecodeTag->state) { + case STATE_TAG_SOF_LOW: + // waiting for a rising edge + if (amplitude > NOISE_THRESHOLD + DecodeTag->previous_amplitude) { + if (DecodeTag->posCount > 10) { + DecodeTag->threshold_sof = amplitude - DecodeTag->previous_amplitude; // to be divided by 2 + DecodeTag->threshold_half = 0; + DecodeTag->state = STATE_TAG_SOF_RISING_EDGE; + } else { + DecodeTag->posCount = 0; + } + } else { + DecodeTag->posCount++; + DecodeTag->previous_amplitude = amplitude; + } + break; - int i, j; - int max = 0, maxPos = 0, skip = 4; - int k = 0; // this will be our return value + case STATE_TAG_SOF_RISING_EDGE: + if (amplitude > DecodeTag->threshold_sof + DecodeTag->previous_amplitude) { // edge still rising + if (amplitude > DecodeTag->threshold_sof + DecodeTag->threshold_sof) { // steeper edge, take this as time reference + DecodeTag->posCount = 1; + } else { + DecodeTag->posCount = 2; + } + DecodeTag->threshold_sof = (amplitude - DecodeTag->previous_amplitude) / 2; + } else { + DecodeTag->posCount = 2; + DecodeTag->threshold_sof = DecodeTag->threshold_sof/2; + } + // DecodeTag->posCount = 2; + DecodeTag->state = STATE_TAG_SOF_HIGH; + break; - // First, correlate for SOF - for (i = 0; i < samplecount; i++) { - int corr = 0; - for (j = 0; j < ARRAYLEN(FrameSOF); j += skip) { - corr += FrameSOF[j] * dest[i + (j / skip)]; - } - if (corr > max) { - max = corr; - maxPos = i; - } - } - // DbpString("SOF at %d, correlation %d", maxPos,max/(ARRAYLEN(FrameSOF)/skip)); + case STATE_TAG_SOF_HIGH: + // waiting for 10 times high. Take average over the last 8 + if (amplitude > DecodeTag->threshold_sof) { + DecodeTag->posCount++; + if (DecodeTag->posCount > 2) { + DecodeTag->threshold_half += amplitude; // keep track of average high value + } + if (DecodeTag->posCount == 10) { + DecodeTag->threshold_half >>= 2; // (4 times 1/2 average) + DecodeTag->state = STATE_TAG_SOF_HIGH_END; + } + } else { // high phase was too short + DecodeTag->posCount = 1; + DecodeTag->previous_amplitude = amplitude; + DecodeTag->state = STATE_TAG_SOF_LOW; + } + break; - // greg - If correlation is less than 1 then there's little point in continuing - if ((max / (ARRAYLEN(FrameSOF) / skip)) < 1) - return k; + case STATE_TAG_SOF_HIGH_END: + // check for falling edge + if (DecodeTag->posCount == 13 && amplitude < DecodeTag->threshold_sof) { + DecodeTag->lastBit = SOF_PART1; // detected 1st part of SOF (12 samples low and 12 samples high) + DecodeTag->shiftReg = 0; + DecodeTag->bitCount = 0; + DecodeTag->len = 0; + DecodeTag->sum1 = amplitude; + DecodeTag->sum2 = 0; + DecodeTag->posCount = 2; + DecodeTag->state = STATE_TAG_RECEIVING_DATA; + // FpgaDisableTracing(); // DEBUGGING + // Dbprintf("amplitude = %d, threshold_sof = %d, threshold_half/4 = %d, previous_amplitude = %d", + // amplitude, + // DecodeTag->threshold_sof, + // DecodeTag->threshold_half/4, + // DecodeTag->previous_amplitude); // DEBUGGING + LED_C_ON(); + } else { + DecodeTag->posCount++; + if (DecodeTag->posCount > 13) { // high phase too long + DecodeTag->posCount = 0; + DecodeTag->previous_amplitude = amplitude; + DecodeTag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } + } + break; - i = maxPos + ARRAYLEN(FrameSOF) / skip; + case STATE_TAG_RECEIVING_DATA: + // FpgaDisableTracing(); // DEBUGGING + // Dbprintf("amplitude = %d, threshold_sof = %d, threshold_half/4 = %d, previous_amplitude = %d", + // amplitude, + // DecodeTag->threshold_sof, + // DecodeTag->threshold_half/4, + // DecodeTag->previous_amplitude); // DEBUGGING + if (DecodeTag->posCount == 1) { + DecodeTag->sum1 = 0; + DecodeTag->sum2 = 0; + } + if (DecodeTag->posCount <= 4) { + DecodeTag->sum1 += amplitude; + } else { + DecodeTag->sum2 += amplitude; + } + if (DecodeTag->posCount == 8) { + if (DecodeTag->sum1 > DecodeTag->threshold_half && DecodeTag->sum2 > DecodeTag->threshold_half) { // modulation in both halves + if (DecodeTag->lastBit == LOGIC0) { // this was already part of EOF + DecodeTag->state = STATE_TAG_EOF; + } else { + DecodeTag->posCount = 0; + DecodeTag->previous_amplitude = amplitude; + DecodeTag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } + } else if (DecodeTag->sum1 < DecodeTag->threshold_half && DecodeTag->sum2 > DecodeTag->threshold_half) { // modulation in second half + // logic 1 + if (DecodeTag->lastBit == SOF_PART1) { // still part of SOF + DecodeTag->lastBit = SOF_PART2; // SOF completed + } else { + DecodeTag->lastBit = LOGIC1; + DecodeTag->shiftReg >>= 1; + DecodeTag->shiftReg |= 0x80; + DecodeTag->bitCount++; + if (DecodeTag->bitCount == 8) { + DecodeTag->output[DecodeTag->len] = DecodeTag->shiftReg; + DecodeTag->len++; + // if (DecodeTag->shiftReg == 0x12 && DecodeTag->len == 1) FpgaDisableTracing(); // DEBUGGING + if (DecodeTag->len > DecodeTag->max_len) { + // buffer overflow, give up + LED_C_OFF(); + return true; + } + DecodeTag->bitCount = 0; + DecodeTag->shiftReg = 0; + } + } + } else if (DecodeTag->sum1 > DecodeTag->threshold_half && DecodeTag->sum2 < DecodeTag->threshold_half) { // modulation in first half + // logic 0 + if (DecodeTag->lastBit == SOF_PART1) { // incomplete SOF + DecodeTag->posCount = 0; + DecodeTag->previous_amplitude = amplitude; + DecodeTag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } else { + DecodeTag->lastBit = LOGIC0; + DecodeTag->shiftReg >>= 1; + DecodeTag->bitCount++; + if (DecodeTag->bitCount == 8) { + DecodeTag->output[DecodeTag->len] = DecodeTag->shiftReg; + DecodeTag->len++; + // if (DecodeTag->shiftReg == 0x12 && DecodeTag->len == 1) FpgaDisableTracing(); // DEBUGGING + if (DecodeTag->len > DecodeTag->max_len) { + // buffer overflow, give up + DecodeTag->posCount = 0; + DecodeTag->previous_amplitude = amplitude; + DecodeTag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } + DecodeTag->bitCount = 0; + DecodeTag->shiftReg = 0; + } + } + } else { // no modulation + if (DecodeTag->lastBit == SOF_PART2) { // only SOF (this is OK for iClass) + LED_C_OFF(); + return true; + } else { + DecodeTag->posCount = 0; + DecodeTag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } + } + DecodeTag->posCount = 0; + } + DecodeTag->posCount++; + break; - uint8_t outBuf[ISO15_MAX_FRAME]; - memset(outBuf, 0, sizeof(outBuf)); - uint8_t mask = 0x01; - for (;;) { - int corr0 = 0, corr1 = 0, corrEOF = 0; - for (j = 0; j < ARRAYLEN(Logic0); j += skip) { - corr0 += Logic0[j] * dest[i + (j / skip)]; - } - for (j = 0; j < ARRAYLEN(Logic1); j += skip) { - corr1 += Logic1[j] * dest[i + (j / skip)]; - } - for (j = 0; j < ARRAYLEN(FrameEOF); j += skip) { - corrEOF += FrameEOF[j] * dest[i + (j / skip)]; - } - // Even things out by the length of the target waveform. - corr0 *= 4; - corr1 *= 4; - // if (DBGLEVEL >= DBG_EXTENDED) - // Dbprintf("Corr1 %d, Corr0 %d, CorrEOF %d", corr1, corr0, corrEOF); + case STATE_TAG_EOF: + if (DecodeTag->posCount == 1) { + DecodeTag->sum1 = 0; + DecodeTag->sum2 = 0; + } + if (DecodeTag->posCount <= 4) { + DecodeTag->sum1 += amplitude; + } else { + DecodeTag->sum2 += amplitude; + } + if (DecodeTag->posCount == 8) { + if (DecodeTag->sum1 > DecodeTag->threshold_half && DecodeTag->sum2 < DecodeTag->threshold_half) { // modulation in first half + DecodeTag->posCount = 0; + DecodeTag->state = STATE_TAG_EOF_TAIL; + } else { + DecodeTag->posCount = 0; + DecodeTag->previous_amplitude = amplitude; + DecodeTag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } + } + DecodeTag->posCount++; + break; - if (corrEOF > corr1 && corrEOF > corr0) - break; + case STATE_TAG_EOF_TAIL: + if (DecodeTag->posCount == 1) { + DecodeTag->sum1 = 0; + DecodeTag->sum2 = 0; + } + if (DecodeTag->posCount <= 4) { + DecodeTag->sum1 += amplitude; + } else { + DecodeTag->sum2 += amplitude; + } + if (DecodeTag->posCount == 8) { + if (DecodeTag->sum1 < DecodeTag->threshold_half && DecodeTag->sum2 < DecodeTag->threshold_half) { // no modulation in both halves + LED_C_OFF(); + return true; + } else { + DecodeTag->posCount = 0; + DecodeTag->previous_amplitude = amplitude; + DecodeTag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } + } + DecodeTag->posCount++; + break; + } - if (corr1 > corr0) { - i += ARRAYLEN(Logic1) / skip; - outBuf[k] |= mask; - } else { - i += ARRAYLEN(Logic0) / skip; - } - - mask <<= 1; - - if (mask == 0) { - k++; - mask = 0x01; - } - - if ((i + (int)ARRAYLEN(FrameEOF)) >= samplecount - 1) { - //Dbprintf("[!] ran off end! %d | %d",( i + (int)ARRAYLEN(FrameEOF)), samplecount-1); - break; - } - } - - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("ice: demod bytes %u", k); - - if (mask != 0x01) { // this happens, when we miss the EOF - - // TODO: for some reason this happens quite often - if (DBGLEVEL >= DBG_ERROR && k != 0) Dbprintf("[!] error, uneven octet! (extra bits!) mask %02x", mask); - //if (mask < 0x08) k--; // discard the last uneven octet; - // 0x08 is an assumption - but works quite often - } - - for (i = 0; i < k; i++) - received[i] = outBuf[i]; - - // return the number of bytes demodulated - return k; + return false; } -// Read from Tag -// Parameters: -// received -// samples -// elapsed -// returns: -// number of decoded bytes -// logging enabled -#define SIGNAL_BUFF_SIZE 20000 +static void DecodeTagInit(DecodeTag_t *DecodeTag, uint8_t *data, uint16_t max_len) { + DecodeTag->previous_amplitude = MAX_PREVIOUS_AMPLITUDE; + DecodeTag->posCount = 0; + DecodeTag->state = STATE_TAG_SOF_LOW; + DecodeTag->output = data; + DecodeTag->max_len = max_len; +} -static int GetIso15693AnswerFromTag(uint8_t *received, int *elapsed) { - - // get current clock - uint32_t time_0 = GetCountSspClk(); - uint32_t time_stop = 0; - bool getNext = false; - int counter = 0, ci, cq = 0; - uint8_t *buf = BigBuf_malloc(SIGNAL_BUFF_SIZE); - - if (elapsed) *elapsed = 0; - - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - - for (;;) { - WDT_HIT(); - - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x00; //0x43; - // To make use of exact timing of next command from reader!! - if (elapsed)(*elapsed)++; - } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - - ci = (int8_t)AT91C_BASE_SSC->SSC_RHR; - ci = ABS(ci); - - // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - // iceman 2016, amplitude sqrt(abs(i) + abs(q)) - if (getNext) { - - buf[counter++] = (uint8_t)(MAX(ci, cq) + (MIN(ci, cq) >> 1)); - - if (counter >= SIGNAL_BUFF_SIZE) - break; - } else { - cq = ci; - } - getNext = !getNext; - } - } - time_stop = GetCountSspClk(); - int len = DemodAnswer(received, buf, counter); - LogTrace(received, len, time_0 << 4, time_stop << 4, NULL, false); - BigBuf_free(); - return len; +static void DecodeTagReset(DecodeTag_t *DecodeTag) { + DecodeTag->posCount = 0; + DecodeTag->state = STATE_TAG_SOF_LOW; + DecodeTag->previous_amplitude = MAX_PREVIOUS_AMPLITUDE; } -// Now the GetISO15693 message from sniffing command -// logging enable, -static int GetIso15693AnswerFromSniff(uint8_t *received, int *samples, int *elapsed) { +/* + * Receive and decode the tag response, also log to tracebuffer + */ +static int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeout, uint32_t *eof_time) { - bool getNext = false; - int counter = 0, ci, cq = 0; - uint32_t time_0 = 0, time_stop = 0; - uint8_t *buf = BigBuf_malloc(SIGNAL_BUFF_SIZE); + int samples = 0; + int ret = 0; - // get current clock - time_0 = GetCountSspClk(); + uint16_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + // the Decoder data structure + DecodeTag_t DecodeTag = { 0 }; + DecodeTagInit(&DecodeTag, response, max_len); - for (;;) { - WDT_HIT(); + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + // And put the FPGA in the appropriate mode + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_424_KHZ | FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE); - ci = (int8_t)AT91C_BASE_SSC->SSC_RHR; - ci = ABS(ci); + // Setup and start DMA. + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + FpgaSetupSscDma((uint8_t*) dmaBuf, ISO15693_DMA_BUFFER_SIZE); + uint32_t dma_start_time = 0; + uint16_t *upTo = dmaBuf; - // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - if (getNext) { + for(;;) { + uint16_t behindBy = ((uint16_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (ISO15693_DMA_BUFFER_SIZE-1); - buf[counter++] = (uint8_t)(MAX(ci, cq) + (MIN(ci, cq) >> 1)); + if (behindBy == 0) continue; - if (counter >= 20000) - break; - } else { - cq = ci; - } - getNext = !getNext; - } - } + samples++; + if (samples == 1) { + // DMA has transferred the very first data + dma_start_time = GetCountSspClk() & 0xfffffff0; + } - time_stop = GetCountSspClk(); - int k = DemodAnswer(received, buf, counter); - LogTrace(received, k, time_0 << 4, time_stop << 4, NULL, false); - BigBuf_free(); - return k; + uint16_t tagdata = *upTo++; + + if(upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning + if (behindBy > (9*ISO15693_DMA_BUFFER_SIZE/10)) { + Dbprintf("About to blow circular buffer - aborted! behindBy=%d", behindBy); + ret = -1; + break; + } + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO15693_DMA_BUFFER_SIZE; // DMA Next Counter registers + } + + if (Handle15693SamplesFromTag(tagdata, &DecodeTag)) { + *eof_time = dma_start_time + samples*16 - DELAY_TAG_TO_ARM; // end of EOF + if (DecodeTag.lastBit == SOF_PART2) { + *eof_time -= 8*16; // needed 8 additional samples to confirm single SOF (iCLASS) + } + if (DecodeTag.len > DecodeTag.max_len) { + ret = -2; // buffer overflow + } + break; + } + + if (samples > timeout && DecodeTag.state < STATE_TAG_RECEIVING_DATA) { + ret = -1; // timeout + break; + } + + } + + FpgaDisableSscDma(); + + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("samples = %d, ret = %d, Decoder: state = %d, lastBit = %d, len = %d, bitCount = %d, posCount = %d", + samples, ret, DecodeTag.state, DecodeTag.lastBit, DecodeTag.len, DecodeTag.bitCount, DecodeTag.posCount); + + if (ret < 0) { + return ret; + } + + uint32_t sof_time = *eof_time + - DecodeTag.len * 8 * 8 * 16 // time for byte transfers + - 32 * 16 // time for SOF transfer + - (DecodeTag.lastBit != SOF_PART2?32*16:0); // time for EOF transfer + + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("timing: sof_time = %d, eof_time = %d", sof_time, *eof_time); + + LogTrace(DecodeTag.output, DecodeTag.len, sof_time*4, *eof_time*4, NULL, false); + + return DecodeTag.len; +} + + +//============================================================================= +// An ISO15693 decoder for reader commands. +// +// This function is called 4 times per bit (every 2 subcarrier cycles). +// Subcarrier frequency fs is 848kHz, 1/fs = 1,18us, i.e. function is called every 2,36us +// LED handling: +// LED B -> ON once we have received the SOF and are expecting the rest. +// LED B -> OFF once we have received EOF or are in error state or unsynced +// +// Returns: true if we received a EOF +// false if we are still waiting for some more +//============================================================================= + +typedef struct DecodeReader { + enum { + STATE_READER_UNSYNCD, + STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF, + STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF, + STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF, + STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF, + STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4, + STATE_READER_RECEIVE_DATA_1_OUT_OF_4, + STATE_READER_RECEIVE_DATA_1_OUT_OF_256, + STATE_READER_RECEIVE_JAMMING + } state; + enum { + CODING_1_OUT_OF_4, + CODING_1_OUT_OF_256 + } Coding; + uint8_t shiftReg; + uint8_t bitCount; + int byteCount; + int byteCountMax; + int posCount; + int sum1, sum2; + uint8_t *output; + uint8_t jam_search_len; + uint8_t *jam_search_string; +} DecodeReader_t; + +static void DecodeReaderInit(DecodeReader_t* DecodeReader, uint8_t *data, uint16_t max_len, uint8_t jam_search_len, uint8_t *jam_search_string) { + DecodeReader->output = data; + DecodeReader->byteCountMax = max_len; + DecodeReader->state = STATE_READER_UNSYNCD; + DecodeReader->byteCount = 0; + DecodeReader->bitCount = 0; + DecodeReader->posCount = 1; + DecodeReader->shiftReg = 0; + DecodeReader->jam_search_len = jam_search_len; + DecodeReader->jam_search_string = jam_search_string; +} + +static void DecodeReaderReset(DecodeReader_t* DecodeReader) { + DecodeReader->state = STATE_READER_UNSYNCD; +} + +static RAMFUNC int Handle15693SampleFromReader(bool bit, DecodeReader_t *DecodeReader) { + switch (DecodeReader->state) { + case STATE_READER_UNSYNCD: + // wait for unmodulated carrier + if (bit) { + DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + } + break; + + case STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF: + if (!bit) { + // we went low, so this could be the beginning of a SOF + DecodeReader->posCount = 1; + DecodeReader->state = STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF; + } + break; + + case STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF: + DecodeReader->posCount++; + if (bit) { // detected rising edge + if (DecodeReader->posCount < 4) { // rising edge too early (nominally expected at 5) + DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + } else { // SOF + DecodeReader->state = STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF; + } + } else { + if (DecodeReader->posCount > 5) { // stayed low for too long + DecodeReaderReset(DecodeReader); + } else { + // do nothing, keep waiting + } + } + break; + + case STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF: + DecodeReader->posCount++; + if (!bit) { // detected a falling edge + if (DecodeReader->posCount < 20) { // falling edge too early (nominally expected at 21 earliest) + DecodeReaderReset(DecodeReader); + } else if (DecodeReader->posCount < 23) { // SOF for 1 out of 4 coding + DecodeReader->Coding = CODING_1_OUT_OF_4; + DecodeReader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; + } else if (DecodeReader->posCount < 28) { // falling edge too early (nominally expected at 29 latest) + DecodeReaderReset(DecodeReader); + } else { // SOF for 1 out of 256 coding + DecodeReader->Coding = CODING_1_OUT_OF_256; + DecodeReader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; + } + } else { + if (DecodeReader->posCount > 29) { // stayed high for too long + DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + } else { + // do nothing, keep waiting + } + } + break; + + case STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF: + DecodeReader->posCount++; + if (bit) { // detected rising edge + if (DecodeReader->Coding == CODING_1_OUT_OF_256) { + if (DecodeReader->posCount < 32) { // rising edge too early (nominally expected at 33) + DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + } else { + DecodeReader->posCount = 1; + DecodeReader->bitCount = 0; + DecodeReader->byteCount = 0; + DecodeReader->sum1 = 1; + DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_256; + LED_B_ON(); + } + } else { // CODING_1_OUT_OF_4 + if (DecodeReader->posCount < 24) { // rising edge too early (nominally expected at 25) + DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + } else { + DecodeReader->posCount = 1; + DecodeReader->state = STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4; + } + } + } else { + if (DecodeReader->Coding == CODING_1_OUT_OF_256) { + if (DecodeReader->posCount > 34) { // signal stayed low for too long + DecodeReaderReset(DecodeReader); + } else { + // do nothing, keep waiting + } + } else { // CODING_1_OUT_OF_4 + if (DecodeReader->posCount > 26) { // signal stayed low for too long + DecodeReaderReset(DecodeReader); + } else { + // do nothing, keep waiting + } + } + } + break; + + case STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4: + DecodeReader->posCount++; + if (bit) { + if (DecodeReader->posCount == 9) { + DecodeReader->posCount = 1; + DecodeReader->bitCount = 0; + DecodeReader->byteCount = 0; + DecodeReader->sum1 = 1; + DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_4; + LED_B_ON(); + } else { + // do nothing, keep waiting + } + } else { // unexpected falling edge + DecodeReaderReset(DecodeReader); + } + break; + + case STATE_READER_RECEIVE_DATA_1_OUT_OF_4: + DecodeReader->posCount++; + if (DecodeReader->posCount == 1) { + DecodeReader->sum1 = bit?1:0; + } else if (DecodeReader->posCount <= 4) { + if (bit) DecodeReader->sum1++; + } else if (DecodeReader->posCount == 5) { + DecodeReader->sum2 = bit?1:0; + } else { + if (bit) DecodeReader->sum2++; + } + if (DecodeReader->posCount == 8) { + DecodeReader->posCount = 0; + if (DecodeReader->sum1 <= 1 && DecodeReader->sum2 >= 3) { // EOF + LED_B_OFF(); // Finished receiving + DecodeReaderReset(DecodeReader); + if (DecodeReader->byteCount != 0) { + return true; + } + } else if (DecodeReader->sum1 >= 3 && DecodeReader->sum2 <= 1) { // detected a 2bit position + DecodeReader->shiftReg >>= 2; + DecodeReader->shiftReg |= (DecodeReader->bitCount << 6); + } + if (DecodeReader->bitCount == 15) { // we have a full byte + DecodeReader->output[DecodeReader->byteCount++] = DecodeReader->shiftReg; + if (DecodeReader->byteCount > DecodeReader->byteCountMax) { + // buffer overflow, give up + LED_B_OFF(); + DecodeReaderReset(DecodeReader); + } + DecodeReader->bitCount = 0; + DecodeReader->shiftReg = 0; + if (DecodeReader->byteCount == DecodeReader->jam_search_len) { + if (!memcmp(DecodeReader->output, DecodeReader->jam_search_string, DecodeReader->jam_search_len)) { + LED_D_ON(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_JAM); + DecodeReader->state = STATE_READER_RECEIVE_JAMMING; + } + } + } else { + DecodeReader->bitCount++; + } + } + break; + + case STATE_READER_RECEIVE_DATA_1_OUT_OF_256: + DecodeReader->posCount++; + if (DecodeReader->posCount == 1) { + DecodeReader->sum1 = bit?1:0; + } else if (DecodeReader->posCount <= 4) { + if (bit) DecodeReader->sum1++; + } else if (DecodeReader->posCount == 5) { + DecodeReader->sum2 = bit?1:0; + } else if (bit) { + DecodeReader->sum2++; + } + if (DecodeReader->posCount == 8) { + DecodeReader->posCount = 0; + if (DecodeReader->sum1 <= 1 && DecodeReader->sum2 >= 3) { // EOF + LED_B_OFF(); // Finished receiving + DecodeReaderReset(DecodeReader); + if (DecodeReader->byteCount != 0) { + return true; + } + } else if (DecodeReader->sum1 >= 3 && DecodeReader->sum2 <= 1) { // detected the bit position + DecodeReader->shiftReg = DecodeReader->bitCount; + } + if (DecodeReader->bitCount == 255) { // we have a full byte + DecodeReader->output[DecodeReader->byteCount++] = DecodeReader->shiftReg; + if (DecodeReader->byteCount > DecodeReader->byteCountMax) { + // buffer overflow, give up + LED_B_OFF(); + DecodeReaderReset(DecodeReader); + } + if (DecodeReader->byteCount == DecodeReader->jam_search_len) { + if (!memcmp(DecodeReader->output, DecodeReader->jam_search_string, DecodeReader->jam_search_len)) { + LED_D_ON(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_JAM); + DecodeReader->state = STATE_READER_RECEIVE_JAMMING; + } + } + } + DecodeReader->bitCount++; + } + break; + + case STATE_READER_RECEIVE_JAMMING: + DecodeReader->posCount++; + if (DecodeReader->Coding == CODING_1_OUT_OF_4) { + if (DecodeReader->posCount == 7*16) { // 7 bits jammed + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNOOP_AMPLITUDE); // stop jamming + // FpgaDisableTracing(); + LED_D_OFF(); + } else if (DecodeReader->posCount == 8*16) { + DecodeReader->posCount = 0; + DecodeReader->output[DecodeReader->byteCount++] = 0x00; + DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_4; + } + } else { + if (DecodeReader->posCount == 7*256) { // 7 bits jammend + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNOOP_AMPLITUDE); // stop jamming + LED_D_OFF(); + } else if (DecodeReader->posCount == 8*256) { + DecodeReader->posCount = 0; + DecodeReader->output[DecodeReader->byteCount++] = 0x00; + DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_256; + } + } + break; + + default: + LED_B_OFF(); + DecodeReaderReset(DecodeReader); + break; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Receive a command (from the reader to us, where we are the simulated tag), +// and store it in the given buffer, up to the given maximum length. Keeps +// spinning, waiting for a well-framed command, until either we get one +// (returns len) or someone presses the pushbutton on the board (returns -1). +// +// Assume that we're called with the SSC (to the FPGA) and ADC path set +// correctly. +//----------------------------------------------------------------------------- + +static int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time) { + int samples = 0; + bool gotFrame = false; + uint8_t b; + + uint8_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; + + // the decoder data structure + DecodeReader_t DecodeReader = {0}; + DecodeReaderInit(&DecodeReader, received, max_len, 0, NULL); + + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); + + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); + + // clear receive register and wait for next transfer + uint32_t temp = AT91C_BASE_SSC->SSC_RHR; + (void) temp; + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) ; + + uint32_t dma_start_time = GetCountSspClk() & 0xfffffff8; + + // Setup and start DMA. + FpgaSetupSscDma(dmaBuf, ISO15693_DMA_BUFFER_SIZE); + uint8_t *upTo = dmaBuf; + + for (;;) { + uint16_t behindBy = ((uint8_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (ISO15693_DMA_BUFFER_SIZE-1); + + if (behindBy == 0) continue; + + b = *upTo++; + if (upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning + if (behindBy > (9*ISO15693_DMA_BUFFER_SIZE/10)) { + Dbprintf("About to blow circular buffer - aborted! behindBy=%d", behindBy); + break; + } + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO15693_DMA_BUFFER_SIZE; // DMA Next Counter registers + } + + for (int i = 7; i >= 0; i--) { + if (Handle15693SampleFromReader((b >> i) & 0x01, &DecodeReader)) { + *eof_time = dma_start_time + samples - DELAY_READER_TO_ARM; // end of EOF + gotFrame = true; + break; + } + samples++; + } + + if (gotFrame) { + break; + } + + if (BUTTON_PRESS()) { + DecodeReader.byteCount = -1; + break; + } + + WDT_HIT(); + } + + FpgaDisableSscDma(); + + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("samples = %d, gotFrame = %d, Decoder: state = %d, len = %d, bitCount = %d, posCount = %d", + samples, gotFrame, DecodeReader.state, DecodeReader.byteCount, DecodeReader.bitCount, DecodeReader.posCount); + + if (DecodeReader.byteCount > 0) { + uint32_t sof_time = *eof_time + - DecodeReader.byteCount * (DecodeReader.Coding==CODING_1_OUT_OF_4?128:2048) // time for byte transfers + - 32 // time for SOF transfer + - 16; // time for EOF transfer + LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time*32, *eof_time*32, NULL, true); + } + + return DecodeReader.byteCount; } //----------------------------------------------------------------------------- @@ -492,133 +1094,231 @@ static int GetIso15693AnswerFromSniff(uint8_t *received, int *samples, int *elap // so that it can be downloaded to a PC and processed there. //----------------------------------------------------------------------------- void AcquireRawAdcSamplesIso15693(void) { - int c = 0, getNext = false; - int ci, cq = 0; + LED_A_ON(); - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + //iceman: needs malloc + uint8_t *dest = BigBuf_get_addr(); - // Now send the command - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); - SpinDelay(200); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); + LED_D_ON(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - uint8_t *buf = BigBuf_get_addr(); + uint8_t cmd[5]; + BuildIdentifyRequest(cmd); + CodeIso15693AsReader(cmd, sizeof(cmd)); - uint32_t time_start = GetCountSspClk(); - uint8_t cmd[CMD_ID_RESP] = {0}; - BuildIdentifyRequest(cmd); + // Give the tags time to energize + SpinDelay(100); - // sending command - c = 0; - for (;;) { - WDT_HIT(); + // Now send the command + uint32_t start_time = 0; + TransmitTo15693Tag(ToSend, ToSendMax, &start_time); - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = ToSend[c]; - c++; - if (c == ToSendMax + 3) { - break; - } - } - } + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) ; - LogTrace(cmd, CMD_ID_RESP, time_start << 4, GetCountSspClk() << 4, NULL, true); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_424_KHZ | FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + for(int c = 0; c < 4000; ) { + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + uint16_t r = AT91C_BASE_SSC->SSC_RHR; + dest[c++] = r >> 5; + } + } - c = 0; - for (;;) { - WDT_HIT(); - - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - - ci = (int8_t)AT91C_BASE_SSC->SSC_RHR; - ci = ABS(ci); - - // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - // iceman 2016, amplitude sqrt(abs(i) + abs(q)) - if (getNext) { - - buf[c++] = (uint8_t)(MAX(ci, cq) + (MIN(ci, cq) >> 1)); - - if (c >= 7000) break; - - } else { - cq = ci; - } - getNext = !getNext; - } - } + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); } -// switch_off, initreader, no logging -void RecordRawAdcSamplesIso15693(void) { +void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { - int c = 0, getNext = false; - int ci, cq = 0; + LED_A_ON(); - Iso15693InitReader(); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - uint8_t *buf = BigBuf_get_addr(); + clear_trace(); + set_tracing(true); - for (;;) { - WDT_HIT(); + // The DMA buffer, used to stream samples from the FPGA + uint16_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + // Count of samples received so far, so that we can include timing + // information in the trace buffer. + int samples = 0; - ci = (int8_t)AT91C_BASE_SSC->SSC_RHR; - ci = ABS(ci); - // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - if (getNext) { + DecodeTag_t DecodeTag = {0}; + uint8_t response[ISO15693_MAX_RESPONSE_LENGTH]; + DecodeTagInit(&DecodeTag, response, sizeof(response)); - buf[c++] = (uint8_t)(MAX(ci, cq) + (MIN(ci, cq) >> 1)); + DecodeReader_t DecodeReader = {0}; + uint8_t cmd[ISO15693_MAX_COMMAND_LENGTH]; + DecodeReaderInit(&DecodeReader, cmd, sizeof(cmd), jam_search_len, jam_search_string); - if (c >= 7000) - break; - } else { - cq = ci; - } + // Print some debug information about the buffer sizes + if (DBGLEVEL >= DBG_EXTENDED) { + Dbprintf("Sniffing buffers initialized:"); + Dbprintf(" Trace: %i bytes", BigBuf_max_traceLen()); + Dbprintf(" Reader -> tag: %i bytes", ISO15693_MAX_COMMAND_LENGTH); + Dbprintf(" tag -> Reader: %i bytes", ISO15693_MAX_RESPONSE_LENGTH); + Dbprintf(" DMA: %i bytes", ISO15693_DMA_BUFFER_SIZE * sizeof(uint16_t)); + } - getNext = !getNext; - } - } + Dbprintf("Sniff started. Press PM3 Button to stop."); - Dbprintf("done"); - switch_off(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNOOP_AMPLITUDE); + LED_D_OFF(); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + StartCountSspClk(); + FpgaSetupSscDma((uint8_t*) dmaBuf, ISO15693_DMA_BUFFER_SIZE); + + bool TagIsActive = false; + bool ReaderIsActive = false; + bool ExpectTagAnswer = false; + uint32_t dma_start_time = 0; + uint16_t *upTo = dmaBuf; + + uint16_t max_behindBy = 0; + + // And now we loop, receiving samples. + for(;;) { + uint16_t behindBy = ((uint16_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (ISO15693_DMA_BUFFER_SIZE-1); + if (behindBy > max_behindBy) { + max_behindBy = behindBy; + } + + if (behindBy == 0) continue; + + samples++; + if (samples == 1) { + // DMA has transferred the very first data + dma_start_time = GetCountSspClk() & 0xfffffff0; + } + + uint16_t snoopdata = *upTo++; + + if (upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning + if (behindBy > (9*ISO15693_DMA_BUFFER_SIZE/10)) { + + Dbprintf("About to blow circular buffer - aborted! behindBy=%d, samples=%d", behindBy, samples); + break; + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO15693_DMA_BUFFER_SIZE; // DMA Next Counter registers + WDT_HIT(); + if (BUTTON_PRESS()) { + DbpString("Sniff stopped."); + break; + } + } + } + + if (!TagIsActive) { // no need to try decoding reader data if the tag is sending + if (Handle15693SampleFromReader(snoopdata & 0x02, &DecodeReader)) { + + uint32_t eof_time = dma_start_time + samples*16 + 8 - DELAY_READER_TO_ARM_SNIFF; // end of EOF + if (DecodeReader.byteCount > 0) { + uint32_t sof_time = eof_time + - DecodeReader.byteCount * (DecodeReader.Coding==CODING_1_OUT_OF_4?128*16:2048*16) // time for byte transfers + - 32*16 // time for SOF transfer + - 16*16; // time for EOF transfer + LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time*4, eof_time*4, NULL, true); + } + // And ready to receive another command. + DecodeReaderReset(&DecodeReader); + // And also reset the demod code, which might have been + // false-triggered by the commands from the reader. + DecodeTagReset(&DecodeTag); + ReaderIsActive = false; + ExpectTagAnswer = true; + + } else if (Handle15693SampleFromReader(snoopdata & 0x01, &DecodeReader)) { + + uint32_t eof_time = dma_start_time + samples*16 + 16 - DELAY_READER_TO_ARM_SNIFF; // end of EOF + if (DecodeReader.byteCount > 0) { + uint32_t sof_time = eof_time + - DecodeReader.byteCount * (DecodeReader.Coding==CODING_1_OUT_OF_4?128*16:2048*16) // time for byte transfers + - 32*16 // time for SOF transfer + - 16*16; // time for EOF transfer + LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time*4, eof_time*4, NULL, true); + } + // And ready to receive another command + DecodeReaderReset(&DecodeReader); + + // And also reset the demod code, which might have been + // false-triggered by the commands from the reader. + DecodeTagReset(&DecodeTag); + ReaderIsActive = false; + ExpectTagAnswer = true; + + } else { + ReaderIsActive = (DecodeReader.state >= STATE_READER_RECEIVE_DATA_1_OUT_OF_4); + } + } + + if (!ReaderIsActive && ExpectTagAnswer) { // no need to try decoding tag data if the reader is currently sending or no answer expected yet + if (Handle15693SamplesFromTag(snoopdata >> 2, &DecodeTag)) { + + uint32_t eof_time = dma_start_time + samples*16 - DELAY_TAG_TO_ARM_SNIFF; // end of EOF + if (DecodeTag.lastBit == SOF_PART2) { + eof_time -= 8*16; // needed 8 additional samples to confirm single SOF (iCLASS) + } + uint32_t sof_time = eof_time + - DecodeTag.len * 8 * 8 * 16 // time for byte transfers + - 32 * 16 // time for SOF transfer + - (DecodeTag.lastBit != SOF_PART2?32*16:0); // time for EOF transfer + LogTrace(DecodeTag.output, DecodeTag.len, sof_time*4, eof_time*4, NULL, false); + // And ready to receive another response. + DecodeTagReset(&DecodeTag); + DecodeReaderReset(&DecodeReader); + ExpectTagAnswer = false; + TagIsActive = false; + } else { + TagIsActive = (DecodeTag.state >= STATE_TAG_RECEIVING_DATA); + } + } + + } + + FpgaDisableSscDma(); + + DbpString("Sniff statistics:"); + Dbprintf(" ExpectTagAnswer: %d, TagIsActive: %d, ReaderIsActive: %d", ExpectTagAnswer, TagIsActive, ReaderIsActive); + Dbprintf(" DecodeTag State: %d", DecodeTag.state); + Dbprintf(" DecodeTag byteCnt: %d", DecodeTag.len); + Dbprintf(" DecodeTag posCount: %d", DecodeTag.posCount); + Dbprintf(" DecodeReader State: %d", DecodeReader.state); + Dbprintf(" DecodeReader byteCnt: %d", DecodeReader.byteCount); + Dbprintf(" DecodeReader posCount: %d", DecodeReader.posCount); + Dbprintf(" Trace length: %d", BigBuf_get_traceLen()); + Dbprintf(" Max behindBy: %d", max_behindBy); } // Initialize the proxmark as iso15k reader // (this might produces glitches that confuse some tags void Iso15693InitReader(void) { - LEDsoff(); - clear_trace(); - set_tracing(true); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Start from off (no field generated) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); SpinDelay(10); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + // switch field on + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); + LED_D_ON(); + + // initialize SSC and select proper AD input + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); - - // Give the tags time to energize - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - SpinDelay(200); - - // Start the timer - StartCountSspClk(); - - LED_A_ON(); + // give tags some time to energize + SpinDelay(250); } /////////////////////////////////////////////////////////////////////// @@ -626,75 +1326,44 @@ void Iso15693InitReader(void) { // This section basicly contains transmission and receiving of bits /////////////////////////////////////////////////////////////////////// -// Encode (into the ToSend buffers) an identify request, which is the first +// Encode an identify request, which is the first // thing that you must send to a tag to get a response. // It expects "cmdout" to be at least CMD_ID_RESP large -static void BuildIdentifyRequest(uint8_t *cmdout) { - uint8_t cmd[CMD_ID_RESP] = {0, ISO15_CMD_INVENTORY, 0, 0, 0}; +// When READER: +static void BuildIdentifyRequest(uint8_t *cmd) { // flags cmd[0] = ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1; - // no mask - cmd[2] = 0x00; + // inventory command code + cmd[1] = ISO15_CMD_INVENTORY; + // no mask + cmd[2] = 0x00; // CRC AddCrc15(cmd, 3); - // coding as high speed (1 out of 4) - CodeIso15693AsReader(cmd, CMD_ID_RESP); - memcpy(cmdout, cmd, CMD_ID_RESP); } // uid is in transmission order (which is reverse of display order) -/* -static void BuildReadBlockRequest(uint8_t **out, uint8_t *uid, uint8_t blockNumber ) { - uint8_t cmd[CMD_READ_RESP] = {0,0,0,0,0,0,0,0,0,0,0,0,0}; - // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block - // followed by teh block data - // one sub-carrier, inventory, 1 slot, fast rate - cmd[0] = (1 << 6)| (1 << 5) | (1 << 1); // no SELECT bit, ADDR bit, OPTION bit - // READ BLOCK command code - cmd[1] = 0x20; - // UID may be optionally specified here - // 64-bit UID - cmd[2] = uid[0]; - cmd[3] = uid[1]; - cmd[4] = uid[2]; - cmd[5] = uid[3]; - cmd[6] = uid[4]; - cmd[7] = uid[5]; - cmd[8] = uid[6]; - cmd[9] = uid[7]; // 0xe0; // always e0 (not exactly unique) - // Block number to read - cmd[10] = blockNumber;//0x00; - // CRC - AddCrc15(cmd, 11); - CodeIso15693AsReader(cmd, CMD_READ_RESP); - memcpy(out, cmd, CMD_ID_RESP); -} -*/ -// Now the VICC>VCD responses when we are simulating a tag -// It expects "out" to be at least CMD_INV_RESP large -static void BuildInventoryResponse(uint8_t *cmdout, uint8_t *uid) { +// When SIM: now the VICC>VCD responses when we are simulating a tag +static void BuildInventoryResponse(uint8_t *uid) { - uint8_t cmd[CMD_INV_RESP] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t cmd[CMD_INV_RESP] = {0}; - // one sub-carrier, inventory, 1 slot, fast rate - // AFI is at bit 5 (1<<4) when doing an INVENTORY - //(1 << 2) | (1 << 5) | (1 << 1); - cmd[0] = 0; // + cmd[0] = 0; // No error, no protocol format extension cmd[1] = 0; // DSFID (data storage format identifier). 0x00 = not supported + // 64-bit UID - cmd[2] = uid[7]; //0x32; - cmd[3] = uid[6]; //0x4b; - cmd[4] = uid[5]; //0x03; - cmd[5] = uid[4]; //0x01; - cmd[6] = uid[3]; //0x00; - cmd[7] = uid[2]; //0x10; - cmd[8] = uid[1]; //0x05; - cmd[9] = uid[0]; //0xe0; + cmd[2] = uid[7]; + cmd[3] = uid[6]; + cmd[4] = uid[5]; + cmd[5] = uid[4]; + cmd[6] = uid[3]; + cmd[7] = uid[2]; + cmd[8] = uid[1]; + cmd[9] = uid[0]; + // CRC AddCrc15(cmd, 10); - CodeIso15693AsReader(cmd, CMD_INV_RESP); - memcpy(cmdout, cmd, CMD_INV_RESP); + CodeIso15693AsTag(cmd, CMD_INV_RESP); } // Universal Method for sending to and recv bytes from a tag @@ -704,36 +1373,52 @@ static void BuildInventoryResponse(uint8_t *cmdout, uint8_t *uid) { // If you do not need the answer use NULL for *recv[] // return: length of received data // logging enabled -static int SendDataTag(uint8_t *send, int sendlen, bool init, int speed, uint8_t *outdata) { +int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t *recv, + uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time) { - int t_samples = 0, wait = 0, elapsed = 0, answer_len = 0; + if (init) { + Iso15693InitReader(); + StartCountSspClk(); + } - LEDsoff(); + int answerLen = 0; - if (init) Iso15693InitReader(); + if (speed_fast) { + // high speed (1 out of 4) + CodeIso15693AsReader(send, sendlen); + } else { + // low speed (1 out of 256) + CodeIso15693AsReader256(send, sendlen); + } - LED_A_ON(); + TransmitTo15693Tag(ToSend, ToSendMax, &start_time); + uint32_t end_time = start_time + 32*(8*ToSendMax-4); // substract the 4 padding bits after EOF + LogTrace(send, sendlen, start_time*4, end_time*4, NULL, true); - if (!speed) - CodeIso15693AsReader256(send, sendlen); // low speed (1 out of 256) - else - CodeIso15693AsReader(send, sendlen); // high speed (1 out of 4) + // Now wait for a response + if (recv != NULL) { + answerLen = GetIso15693AnswerFromTag(recv, max_recv_len, timeout, eof_time); + } - LED_A_INV(); + return answerLen; +} - uint32_t time_start = GetCountSspClk(); +int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time) { - TransmitTo15693Tag(ToSend, ToSendMax, &t_samples, &wait); - LogTrace(send, sendlen, time_start << 4, GetCountSspClk() << 4, NULL, true); + int answerLen = 0; - // Now wait for a response - if (outdata != NULL) { - LED_B_INV(); - answer_len = GetIso15693AnswerFromTag(outdata, &elapsed); - } + CodeIso15693AsReaderEOF(); - LEDsoff(); - return answer_len; + TransmitTo15693Tag(ToSend, ToSendMax, &start_time); + uint32_t end_time = start_time + 32*(8*ToSendMax-4); // substract the 4 padding bits after EOF + LogTrace(NULL, 0, start_time*4, end_time*4, NULL, true); + + // Now wait for a response + if (recv != NULL) { + answerLen = GetIso15693AnswerFromTag(recv, max_recv_len, timeout, eof_time); + } + + return answerLen; } // -------------------------------------------------------------------- @@ -745,10 +1430,13 @@ static int SendDataTag(uint8_t *send, int sendlen, bool init, int speed, uint8_t static void DbdecodeIso15693Answer(int len, uint8_t *d) { if (len > 3) { + char status[DBD15STATLEN + 1] = {0}; - if (d[0] & (1 << 3)) + + if (d[0] & ISO15_RES_EXT) strncat(status, "ProtExt ", DBD15STATLEN - strlen(status)); - if (d[0] & 1) { + + if (d[0] & ISO15_RES_ERROR) { // error strncat(status, "Error ", DBD15STATLEN - strlen(status)); switch (d[1]) { @@ -788,9 +1476,9 @@ static void DbdecodeIso15693Answer(int len, uint8_t *d) { } if (CheckCrc15(d, len)) - strncat(status, "[+] crc OK", DBD15STATLEN - strlen(status)); + strncat(status, "[+] crc (" _GREEN_("OK") ")", DBD15STATLEN - strlen(status)); else - strncat(status, "[!] crc fail", DBD15STATLEN - strlen(status)); + strncat(status, "[!] crc (" _RED_("fail") ")", DBD15STATLEN - strlen(status)); if (DBGLEVEL >= DBG_ERROR) Dbprintf("%s", status); } @@ -807,41 +1495,35 @@ static void DbdecodeIso15693Answer(int len, uint8_t *d) { // ok // parameter is unused !?! void ReaderIso15693(uint32_t parameter) { - int answerLen1 = 0; - int tsamples = 0, wait = 0, elapsed = 0; - // set up device/fpga - Iso15693InitReader(); - uint8_t *answer1 = BigBuf_malloc(50); - uint8_t *answer2 = BigBuf_malloc(50); + LED_A_ON(); + set_tracing(true); - // Blank arrays - memset(answer1, 0x00, 50); - memset(answer2, 0x00, 50); + uint8_t *answer = BigBuf_malloc(ISO15693_MAX_RESPONSE_LENGTH); + memset(answer, 0x00, ISO15693_MAX_RESPONSE_LENGTH); - // Now send the IDENTIFY command - // FIRST WE RUN AN INVENTORY TO GET THE TAG UID - // THIS MEANS WE CAN PRE-BUILD REQUESTS TO SAVE CPU TIME - uint32_t time_start = GetCountSspClk(); - uint8_t cmd[CMD_ID_RESP] = {0}; - BuildIdentifyRequest(cmd); - TransmitTo15693Tag(ToSend, ToSendMax, &tsamples, &wait); - LogTrace(cmd, CMD_ID_RESP, time_start << 4, GetCountSspClk() << 4, NULL, true); + // FIRST WE RUN AN INVENTORY TO GET THE TAG UID + // THIS MEANS WE CAN PRE-BUILD REQUESTS TO SAVE CPU TIME - // Now wait for a response - answerLen1 = GetIso15693AnswerFromTag(answer1, &elapsed) ; + // Send the IDENTIFY command + uint8_t cmd[5] = {0}; + BuildIdentifyRequest(cmd); + uint32_t start_time = 0; + uint32_t eof_time; + int answerLen = SendDataTag(cmd, sizeof(cmd), true, true, answer, ISO15693_MAX_RESPONSE_LENGTH, start_time, ISO15693_READER_TIMEOUT, &eof_time); + start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; // we should do a better check than this - if (answerLen1 >= 12) { + if (answerLen >= 12) { uint8_t uid[8]; - uid[0] = answer1[9]; // always E0 - uid[1] = answer1[8]; // IC Manufacturer code - uid[2] = answer1[7]; - uid[3] = answer1[6]; - uid[4] = answer1[5]; - uid[5] = answer1[4]; - uid[6] = answer1[3]; - uid[7] = answer1[2]; + uid[0] = answer[9]; // always E0 + uid[1] = answer[8]; // IC Manufacturer code + uid[2] = answer[7]; + uid[3] = answer[6]; + uid[4] = answer[5]; + uid[5] = answer[4]; + uid[6] = answer[3]; + uid[7] = answer[2]; if (DBGLEVEL >= DBG_EXTENDED) { Dbprintf("[+] UID = %02X%02X%02X%02X%02X%02X%02X%02X", @@ -854,36 +1536,37 @@ void ReaderIso15693(uint32_t parameter) { // arg1 = len of response (12 bytes) // arg2 = rtf // asbytes = uid. - reply_old(CMD_ACK, 1, sizeof(uid), 0, uid, sizeof(uid)); + reply_mix(CMD_ACK, 1, sizeof(uid), 0, uid, sizeof(uid)); } if (DBGLEVEL >= DBG_EXTENDED) { - Dbprintf("[+] %d octets read from IDENTIFY request:", answerLen1); - DbdecodeIso15693Answer(answerLen1, answer1); - Dbhexdump(answerLen1, answer1, true); + Dbprintf("[+] %d octets read from IDENTIFY request:", answerLen); + DbdecodeIso15693Answer(answerLen, answer); + Dbhexdump(answerLen, answer, true); } switch_off(); + BigBuf_free(); +} + +// When SIM: initialize the Proxmark3 as ISO15693 tag +static void Iso15693InitTag(void) { + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); + StartCountSspClk(); } // Simulate an ISO15693 TAG, perform anti-collision and then print any reader commands // all demodulation performed in arm rather than host. - greg -void SimTagIso15693(uint32_t parameter, uint8_t *uid) { +void SimTagIso15693(uint8_t *uid) { LEDsoff(); - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); - // Start from off (no field generated) - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(200); + Iso15693InitTag(); LED_A_ON(); - uint32_t time_start; - int samples = 0, tsamples = 0; - int wait = 0, elapsed = 0; - Dbprintf("ISO-15963 Simulating uid: %02X%02X%02X%02X%02X%02X%02X%02X", uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6], uid[7]); uint8_t buf[ISO15_MAX_FRAME]; @@ -892,31 +1575,29 @@ void SimTagIso15693(uint32_t parameter, uint8_t *uid) { LED_C_ON(); // Build a suitable reponse to the reader INVENTORY cocmmand - // not so obsvious, but in the call to BuildInventoryResponse, the command is copied to the global ToSend buffer used below. - uint8_t cmd[CMD_INV_RESP] = {0}; - BuildInventoryResponse(cmd, uid); + // not so obvious, but in the call to BuildInventoryResponse, the command is copied to the global ToSend buffer used below. + BuildInventoryResponse(uid); - while (!BUTTON_PRESS() && !data_available()) { + while (!BUTTON_PRESS()) { WDT_HIT(); // Listen to reader - int ans = GetIso15693AnswerFromSniff(buf, &samples, &elapsed) ; + uint8_t cmd[ISO15693_MAX_COMMAND_LENGTH]; + uint32_t eof_time = 0, start_time = 0; + int cmd_len = GetIso15693CommandFromReader(cmd, sizeof(cmd), &eof_time); - // we should do a better check than this - if (ans >= 1) { + if ((cmd_len >= 5) && (cmd[0] & ISO15_REQ_INVENTORY) && (cmd[1] == ISO15_CMD_INVENTORY)) { // TODO: check more flags + bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH); + start_time = eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM; + TransmitTo15693Reader(ToSend, ToSendMax, &start_time, 0, slow); + } - time_start = GetCountSspClk(); - TransmitTo15693Reader(ToSend, ToSendMax, &tsamples, &wait); - LogTrace(cmd, CMD_INV_RESP, time_start << 4, GetCountSspClk() << 4, NULL, true); - - if (DBGLEVEL >= DBG_EXTENDED) { - Dbprintf("[+] %d octets read from reader command: %x %x %x %x %x %x %x %x", ans, - buf[0], buf[1], buf[2], buf[3], - buf[4], buf[5], buf[6], buf[7] - ); - } + if (DBGLEVEL >= DBG_EXTENDED) { + Dbprintf(" %d bytes read from reader:", cmd_len); + Dbhexdump(cmd_len, cmd, false); } - } + } + switch_off(); } @@ -924,12 +1605,8 @@ void SimTagIso15693(uint32_t parameter, uint8_t *uid) { // (some manufactures offer a way to read the AFI, though) void BruteforceIso15693Afi(uint32_t speed) { - uint8_t data[7] = {0, 0, 0, 0, 0, 0, 0}; - uint8_t buf[ISO15_MAX_FRAME]; - memset(buf, 0x00, sizeof(buf)); - int datalen = 0, recvlen = 0; - bool aborted = false; - + uint8_t data[7] = {0}; + uint8_t recv[ISO15693_MAX_RESPONSE_LENGTH]; Iso15693InitReader(); // first without AFI @@ -939,31 +1616,40 @@ void BruteforceIso15693Afi(uint32_t speed) { data[1] = ISO15_CMD_INVENTORY; data[2] = 0; // AFI AddCrc15(data, 3); - datalen = 5; - recvlen = SendDataTag(data, datalen, false, speed, buf); + + int datalen = 5; + uint32_t eof_time = 0; + uint32_t start_time = GetCountSspClk(); + int recvlen = SendDataTag(data, datalen, true, speed, recv, sizeof(recv), 0, ISO15693_READER_TIMEOUT, &eof_time); + start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; WDT_HIT(); if (recvlen >= 12) { - Dbprintf("NoAFI UID = %s", iso15693_sprintUID(NULL, buf + 2)); + Dbprintf("NoAFI UID = %s", iso15693_sprintUID(NULL, recv + 2)); } // now with AFI data[0] |= ISO15_REQINV_AFI; - //data[1] = ISO15_CMD_INVENTORY; data[2] = 0; // AFI data[3] = 0; // mask length // 4 + 2crc datalen = 6; + bool aborted = false; for (uint16_t i = 0; i < 256; i++) { + data[2] = i & 0xFF; AddCrc15(data, 4); - recvlen = SendDataTag(data, datalen, false, speed, buf); + + recvlen = SendDataTag(data, datalen, false, speed, recv, sizeof(recv), start_time, ISO15693_READER_TIMEOUT, &eof_time); + start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + WDT_HIT(); + if (recvlen >= 12) { - Dbprintf("AFI = %i UID = %s", i, iso15693_sprintUID(NULL, buf + 2)); + Dbprintf("AFI = %i UID = %s", i, iso15693_sprintUID(NULL, recv + 2)); } aborted = BUTTON_PRESS(); @@ -985,34 +1671,129 @@ void BruteforceIso15693Afi(uint32_t speed) { } // Allows to directly send commands to the tag via the client -// Has to increase dialog between device and client. +// OBS: doesn't turn off rf field afterwards. void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint8_t *data) { - bool init = true; - int buflen = 0; - uint8_t buf[ISO15_MAX_FRAME]; - memset(buf, 0x00, sizeof(buf)); + LED_A_ON(); - if (DBGLEVEL >= DBG_EXTENDED) { - DbpString("[+] SEND"); - Dbhexdump(datalen, data, true); - } + int recvlen = 0; + uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; + uint32_t eof_time; + uint16_t timeout; + bool request_answer = false; + + switch (data[1]) { + case ISO15_CMD_WRITE: + case ISO15_CMD_LOCK: + case ISO15_CMD_WRITEMULTI: + case ISO15_CMD_WRITEAFI: + case ISO15_CMD_LOCKAFI: + case ISO15_CMD_WRITEDSFID: + case ISO15_CMD_LOCKDSFID: + timeout = ISO15693_READER_TIMEOUT_WRITE; + request_answer = data[0] & ISO15_REQ_OPTION; + break; + default: + timeout = ISO15693_READER_TIMEOUT; + } - buflen = SendDataTag(data, datalen, init, speed, (recv ? buf : NULL)); + if (DBGLEVEL >= DBG_EXTENDED) { + Dbprintf("SEND:"); + Dbhexdump(datalen, data, false); + } + + recvlen = SendDataTag(data, datalen, true, speed, (recv ? recvbuf : NULL), sizeof(recvbuf), 0, timeout, &eof_time); + + // send a single EOF to get the tag response + if (request_answer) { + recvlen = SendDataTagEOF((recv ? recvbuf : NULL), sizeof(recvbuf), 0, ISO15693_READER_TIMEOUT, &eof_time); + } + + // for the time being, switch field off to protect rdv4.0 + // note: this prevents using hf 15 cmd with s option - which isn't implemented yet anyway + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); if (recv) { - buflen = (buflen > ISO15_MAX_FRAME) ? ISO15_MAX_FRAME : buflen; - LED_B_ON(); - reply_old(CMD_ACK, buflen, 0, 0, buf, buflen); - LED_B_OFF(); + if (recvlen > ISO15693_MAX_RESPONSE_LENGTH) { + recvlen = ISO15693_MAX_RESPONSE_LENGTH; + } + reply_mix(CMD_ACK, recvlen, 0, 0, recvbuf, ISO15693_MAX_RESPONSE_LENGTH); + + + if (DBGLEVEL >= DBG_EXTENDED) { + Dbprintf("RECV:"); + if (recvlen > 0) { + Dbhexdump(recvlen, recvbuf, false); + DbdecodeIso15693Answer(recvlen, recvbuf); + } + } + - if (DBGLEVEL >= DBG_EXTENDED) { - DbpString("[+] RECV"); - DbdecodeIso15693Answer(buflen, buf); - Dbhexdump(buflen, buf, true); - } } else { - reply_old(CMD_ACK, 1, 0, 0, 0, 0); + reply_mix(CMD_ACK, 1, 0, 0, 0, 0); } } + + +//----------------------------------------------------------------------------- +// Work with "magic Chinese" card. +// +//----------------------------------------------------------------------------- + +// Set the UID on Magic ISO15693 tag (based on Iceman's LUA-script). +void SetTag15693Uid(uint8_t *uid) { + + LED_A_ON(); + + uint8_t cmd[4][9] = { + {ISO15_REQ_DATARATE_HIGH, ISO15_CMD_WRITE, 0x3e, 0x00, 0x00, 0x00, 0x00}, + {ISO15_REQ_DATARATE_HIGH, ISO15_CMD_WRITE, 0x3f, 0x69, 0x96, 0x00, 0x00}, + {ISO15_REQ_DATARATE_HIGH, ISO15_CMD_WRITE, 0x38}, + {ISO15_REQ_DATARATE_HIGH, ISO15_CMD_WRITE, 0x39} + }; + + int recvlen = 0; + uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; + uint32_t eof_time; + + // Command 3 : 022138u8u7u6u5 (where uX = uid byte X) + cmd[2][3] = uid[7]; + cmd[2][4] = uid[6]; + cmd[2][5] = uid[5]; + cmd[2][6] = uid[4]; + + // Command 4 : 022139u4u3u2u1 (where uX = uid byte X) + cmd[3][3] = uid[3]; + cmd[3][4] = uid[2]; + cmd[3][5] = uid[1]; + cmd[3][6] = uid[0]; + + AddCrc15(cmd[0], 7); + AddCrc15(cmd[1], 7); + AddCrc15(cmd[2], 7); + AddCrc15(cmd[3], 7); + + uint32_t start_time = 0; + + for (int i = 0; i < 4; i++) { + + recvlen = SendDataTag(cmd[i], sizeof(cmd[i]), i == 0 ? true : false, true, recvbuf, sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT_WRITE, &eof_time); + start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + + if (DBGLEVEL >= DBG_EXTENDED) { + Dbprintf("SEND:"); + Dbhexdump(sizeof(cmd[i]), cmd[i], false); + Dbprintf("RECV:"); + if (recvlen > 0) { + Dbhexdump(recvlen, recvbuf, false); + DbdecodeIso15693Answer(recvlen, recvbuf); + } + } + } + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + reply_mix(CMD_ACK, recvlen, 0, 0, recvbuf, recvlen); +} diff --git a/armsrc/iso15693.h b/armsrc/iso15693.h index 2233ba9fd..bc9b89eee 100644 --- a/armsrc/iso15693.h +++ b/armsrc/iso15693.h @@ -12,15 +12,32 @@ #define __ISO15693_H #include "common.h" - #include "pm3_cmd.h" // struct + + +// Delays in SSP_CLK ticks. +// SSP_CLK runs at 13,56MHz / 32 = 423.75kHz when simulating a tag +#define DELAY_ISO15693_VCD_TO_VICC_SIM 132 // 132/423.75kHz = 311.5us from end of command EOF to start of tag response + +//SSP_CLK runs at 13.56MHz / 4 = 3,39MHz when acting as reader. All values should be multiples of 16 +#define DELAY_ISO15693_VCD_TO_VICC_READER 1056 // 1056/3,39MHz = 311.5us from end of command EOF to start of tag response +#define DELAY_ISO15693_VICC_TO_VCD_READER 1024 // 1024/3.39MHz = 302.1us between end of tag response and next reader command + void RecordRawAdcSamplesIso15693(void); void AcquireRawAdcSamplesIso15693(void); void ReaderIso15693(uint32_t parameter); // Simulate an ISO15693 reader - greg -void SimTagIso15693(uint32_t parameter, uint8_t *uid); // simulate an ISO15693 tag - greg +void SimTagIso15693(uint8_t *uid); // simulate an ISO15693 tag - greg void BruteforceIso15693Afi(uint32_t speed); // find an AFI of a tag - atrox void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint8_t *data); // send arbitrary commands from CLI - atrox void Iso15693InitReader(void); +void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string); + +int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t *recv, + uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time); + +int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time); + +void SetTag15693Uid(uint8_t *uid); #endif From 8189809f4832572e3a9c259207b1ff3b9bf93098 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 2 Jul 2020 12:38:38 +0200 Subject: [PATCH 018/139] chg: adapt to fpga changes --- armsrc/iso14443a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 3b7b096a4..225337485 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2527,7 +2527,7 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); From 2c0f595f865ad5407fcbc8f12f4b17d12e7247b3 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 3 Jul 2020 14:59:10 +0200 Subject: [PATCH 019/139] no more snooping around --- fpga/fpga_felica.v | 2 +- fpga/fpga_hf.v | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fpga/fpga_felica.v b/fpga/fpga_felica.v index 461d90b02..7c1a95b93 100644 --- a/fpga/fpga_felica.v +++ b/fpga/fpga_felica.v @@ -26,7 +26,7 @@ `define FPGA_MAJOR_MODE_HF_READER 0 `define FPGA_MAJOR_MODE_HF_SIMULATOR 1 `define FPGA_MAJOR_MODE_HF_ISO14443A 2 -`define FPGA_MAJOR_MODE_HF_SNOOP 3 +`define FPGA_MAJOR_MODE_HF_SNIFF 3 `define FPGA_MAJOR_MODE_HF_ISO18092 4 `define FPGA_MAJOR_MODE_HF_GET_TRACE 5 `define FPGA_MAJOR_MODE_OFF 7 diff --git a/fpga/fpga_hf.v b/fpga/fpga_hf.v index 5448219fc..35178a996 100644 --- a/fpga/fpga_hf.v +++ b/fpga/fpga_hf.v @@ -26,7 +26,7 @@ `define FPGA_MAJOR_MODE_HF_READER 0 `define FPGA_MAJOR_MODE_HF_SIMULATOR 1 `define FPGA_MAJOR_MODE_HF_ISO14443A 2 -`define FPGA_MAJOR_MODE_HF_SNOOP 3 +`define FPGA_MAJOR_MODE_HF_SNIFF 3 `define FPGA_MAJOR_MODE_HF_ISO18092 4 `define FPGA_MAJOR_MODE_HF_GET_TRACE 5 `define FPGA_MAJOR_MODE_OFF 7 From 7d1a27819720df273f59b03b6a8b98cbf7eba96b Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 3 Jul 2020 21:33:17 +0200 Subject: [PATCH 020/139] merge hell p.x --- armsrc/fpgaloader.h | 8 +- armsrc/iclass.c | 2077 ++++++++------------------------------ armsrc/iclass.h | 3 +- armsrc/iso14443b.c | 841 ++++++++------- armsrc/iso14443b.h | 2 +- armsrc/iso15693.c | 30 +- armsrc/iso15693.h | 9 + client/src/cmdhf14b.c | 24 +- client/src/cmdhficlass.c | 31 +- include/pm3_cmd.h | 12 + 10 files changed, 919 insertions(+), 2118 deletions(-) diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h index 474c08e1a..17bd8bbc2 100644 --- a/armsrc/fpgaloader.h +++ b/armsrc/fpgaloader.h @@ -65,7 +65,7 @@ thres| x x x x x x x x #define FPGA_MAJOR_MODE_HF_READER (0<<6) // D #define FPGA_MAJOR_MODE_HF_SIMULATOR (1<<6) // D #define FPGA_MAJOR_MODE_HF_ISO14443A (2<<6) // D -#define FPGA_MAJOR_MODE_HF_SNOOP (3<<6) // D +#define FPGA_MAJOR_MODE_HF_SNIFF (3<<6) // D #define FPGA_MAJOR_MODE_HF_ISO18092 (4<<6) // D #define FPGA_MAJOR_MODE_HF_GET_TRACE (5<<6) // D @@ -87,9 +87,9 @@ thres| x x x x x x x x #define FPGA_HF_READER_MODE_RECEIVE_PHASE (2<<0) #define FPGA_HF_READER_MODE_SEND_FULL_MOD (3<<0) #define FPGA_HF_READER_MODE_SEND_SHALLOW_MOD (4<<0) -#define FPGA_HF_READER_MODE_SNOOP_IQ (5<<0) -#define FPGA_HF_READER_MODE_SNOOP_AMPLITUDE (6<<0) -#define FPGA_HF_READER_MODE_SNOOP_PHASE (7<<0) +#define FPGA_HF_READER_MODE_SNIFF_IQ (5<<0) +#define FPGA_HF_READER_MODE_SNIFF_AMPLITUDE (6<<0) +#define FPGA_HF_READER_MODE_SNIFF_PHASE (7<<0) #define FPGA_HF_READER_MODE_SEND_JAM (8<<0) #define FPGA_HF_READER_SUBCARRIER_848_KHZ (0<<4) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 1e9bb671f..dbf759adf 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -54,6 +54,7 @@ #include "dbprint.h" #include "protocols.h" #include "ticks.h" +#include "iso15693.h" static int g_wait = 290; static int timeout = 5000; @@ -63,523 +64,33 @@ static uint32_t time_response = 0; static int SendIClassAnswer(uint8_t *resp, int respLen, uint16_t delay); int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf); -#define MODE_SIM_CSN 0 -#define MODE_EXIT_AFTER_MAC 1 -#define MODE_FULLSIM 2 - -#ifndef ICLASS_DMA_BUFFER_SIZE -# define ICLASS_DMA_BUFFER_SIZE 256 -#endif - // The length of a received command will in most cases be no more than 18 bytes. -// 32 should be enough! +// we expect max 34 bytes as tag answer (response to READ4) #ifndef ICLASS_BUFFER_SIZE -#define ICLASS_BUFFER_SIZE 32 +#define ICLASS_BUFFER_SIZE 34 #endif +// iCLASS has a slightly different timing compared to ISO15693. According to the picopass data sheet the tag response is expected 330us after +// the reader command. This is measured from end of reader EOF to first modulation of the tag's SOF which starts with a 56,64us unmodulated period. +// 330us = 140 ssp_clk cycles @ 423,75kHz when simulating. +// 56,64us = 24 ssp_clk_cycles +#define DELAY_ICLASS_VCD_TO_VICC_SIM (140 - 24) + +// times in ssp_clk_cycles @ 3,3625MHz when acting as reader +#define DELAY_ICLASS_VICC_TO_VCD_READER DELAY_ISO15693_VICC_TO_VCD_READER + +// times in samples @ 212kHz when acting as reader +#define ICLASS_READER_TIMEOUT_ACTALL 330 // 1558us, nominal 330us + 7slots*160us = 1450us +#define ICLASS_READER_TIMEOUT_UPDATE 3390 // 16000us, nominal 4-15ms +#define ICLASS_READER_TIMEOUT_OTHERS 80 // 380us, nominal 330us + #define AddCrc(data, len) compute_crc(CRC_ICLASS, (data), (len), (data)+(len), (data)+(len)+1) -//----------------------------------------------------------------------------- -// The software UART that receives commands from the reader, and its state -// variables. -//----------------------------------------------------------------------------- -/* -typedef struct { - enum { - STATE_UNSYNCD, - STATE_START_OF_COMMUNICATION, - STATE_RECEIVING - } state; - uint16_t shiftReg; - int bitCnt; - int byteCnt; -// int byteCntMax; - int posCnt; - int nOutOfCnt; - int OutOfCnt; - int syncBit; - int samples; - int highCnt; - int swapper; - int counter; - int bitBuffer; - int dropPosition; - uint8_t *output; -} tUartIc; -*/ -typedef struct { - enum { - DEMOD_IC_UNSYNCD, - DEMOD_IC_START_OF_COMMUNICATION, - DEMOD_IC_START_OF_COMMUNICATION2, - DEMOD_IC_START_OF_COMMUNICATION3, - DEMOD_IC_SOF_COMPLETE, - DEMOD_IC_MANCHESTER_D, - DEMOD_IC_MANCHESTER_E, - DEMOD_IC_END_OF_COMMUNICATION, - DEMOD_IC_END_OF_COMMUNICATION2, - DEMOD_IC_MANCHESTER_F, - DEMOD_IC_ERROR_WAIT - } state; - int bitCount; - int posCount; - int syncBit; - uint16_t shiftReg; - uint32_t buffer; - uint32_t buffer2; - uint32_t buffer3; - int buff; - int samples; - int len; - enum { - SUB_NONE, - SUB_FIRST_HALF, - SUB_SECOND_HALF, - SUB_BOTH - } sub; - uint8_t *output; -} tDemodIc; - -/* -* Abrasive's uart implementation -* https://github.com/abrasive/proxmark3/commit/2b8bff7daea8ae1193bf7ee29b1fa46e95218902 -*/ -// Static vars for UART -typedef struct { - bool synced; - bool frame; - bool frame_done; - uint8_t *buf; - int len; -} tUartIc; -static tUartIc Uart; - static void OnError(uint8_t reason) { reply_mix(CMD_ACK, 0, reason, 0, 0, 0); switch_off(); } -static void uart_reset(void) { - Uart.frame_done = false; - Uart.synced = false; - Uart.frame = false; -} - -static void uart_init(uint8_t *data) { - Uart.buf = data; - uart_reset(); -} - -static void uart_bit(uint8_t bit) { - static uint8_t buf = 0xff; - static uint8_t n_buf; - static int nmsg_byte; - buf <<= 1; - buf |= bit ? 1 : 0; - - if (!Uart.frame) { - if (buf == 0x7b) { // 0b0111 1011 - Uart.frame = true; - n_buf = 0; - Uart.len = 0; - nmsg_byte = 0; - } - } else { - static uint8_t msg_byte; - n_buf++; - if (n_buf == 8) { - msg_byte >>= 2; - switch (buf) { - case 0xbf: // 0 - 1011 1111 - break; - case 0xef: // 1 - 1110 1111 - msg_byte |= (1 << 6); - break; - case 0xfb: // 2 - 1111 1011 - msg_byte |= (2 << 6); - break; - case 0xfe: // 3 - 1111 1110 - msg_byte |= (3 << 6); - break; - case 0xdf: // eof - 1101 1111 - Uart.frame = false; - Uart.synced = false; - Uart.frame_done = true; - break; - default: - Uart.frame = false; - Uart.synced = false; - Dbprintf("[-] bad %02X at %d:%d", buf, Uart.len, nmsg_byte); - } - - if (Uart.frame) { // data bits - nmsg_byte += 2; - if (nmsg_byte >= 8) { - Uart.buf[Uart.len++] = msg_byte; - nmsg_byte = 0; - } - } - n_buf = 0; - buf = 0xff; - } - } -} - -static void uart_samples(uint8_t byte) { - static uint32_t buf; - static int window; - static int drop_next = 0; - - uint32_t falling; - int lz; - - if (!Uart.synced) { - if (byte == 0xFF) - return; - buf = 0xFFFFFFFF; - window = 0; - drop_next = 0; - Uart.synced = true; - } - - buf <<= 8; - buf |= byte; - - if (drop_next) { - drop_next = 0; - return; - } - -again: - falling = ~buf & ((buf >> 1) ^ buf) & (0xFF << window); - - uart_bit(!falling); - - if (!falling) - return; - - lz = __builtin_clz(falling) - 24 + window; - - // aim to get falling edge on fourth-leftmost bit of window - window += 3 - lz; - - if (window < 0) { - window += 8; - drop_next = 1; - } else if (window >= 8) { - window -= 8; - goto again; - } -} - - -/* -static void UartReset(){ - Uart.state = STATE_UNSYNCD; - Uart.shiftReg = 0; - Uart.bitCnt = 0; - Uart.byteCnt = 0; - Uart.posCnt = 0; - Uart.nOutOfCnt = 0; - Uart.OutOfCnt = 0; - Uart.syncBit = 0; - Uart.samples = 0; - Uart.highCnt = 0; - Uart.swapper = 0; - Uart.counter = 0; - Uart.bitBuffer = 0; - Uart.dropPosition = 0; -} -*/ - -/* -* READER TO CARD -* 1 out of 4 Decoding -* 1 out of 256 Decoding -*/ -/* -static RAMFUNC int OutOfNDecoding(int bit) { - //int error = 0; - int bitright; - - if (!Uart.bitBuffer) { - Uart.bitBuffer = bit ^ 0xFF0; - return false; - } else { - Uart.bitBuffer <<= 4; - Uart.bitBuffer ^= bit; - } - - // if (Uart.swapper) { - // Uart.output[Uart.byteCnt] = Uart.bitBuffer & 0xFF; - // Uart.byteCnt++; - // Uart.swapper = 0; - // if (Uart.byteCnt > 15) return true; - // } - // else { - // Uart.swapper = 1; - // } - - if (Uart.state != STATE_UNSYNCD) { - Uart.posCnt++; - - if ((Uart.bitBuffer & Uart.syncBit) ^ Uart.syncBit) - bit = 0; - else - bit = 1; - - if (((Uart.bitBuffer << 1) & Uart.syncBit) ^ Uart.syncBit) - bitright = 0; - else - bitright = 1; - - if(bit != bitright) - bit = bitright; - - - // So, now we only have to deal with *bit*, lets see... - if (Uart.posCnt == 1) { - // measurement first half bitperiod - if (!bit) { - // Drop in first half means that we are either seeing - // an SOF or an EOF. - - if (Uart.nOutOfCnt == 1) { - // End of Communication - Uart.state = STATE_UNSYNCD; - Uart.highCnt = 0; - if (Uart.byteCnt == 0) { - // Its not straightforward to show single EOFs - // So just leave it and do not return TRUE - Uart.output[0] = 0xf0; - Uart.byteCnt++; - } else { - return true; - } - } else if (Uart.state != STATE_START_OF_COMMUNICATION) { - // When not part of SOF or EOF, it is an error - Uart.state = STATE_UNSYNCD; - Uart.highCnt = 0; - //error = 4; - } - } - } else { - // measurement second half bitperiod - // Count the bitslot we are in... (ISO 15693) - Uart.nOutOfCnt++; - - if (!bit) { - if (Uart.dropPosition) { - if (Uart.state == STATE_START_OF_COMMUNICATION) { - //error = 1; - } else { - //error = 7; - } - // It is an error if we already have seen a drop in current frame - Uart.state = STATE_UNSYNCD; - Uart.highCnt = 0; - } else { - Uart.dropPosition = Uart.nOutOfCnt; - } - } - Uart.posCnt = 0; - - if (Uart.nOutOfCnt == Uart.OutOfCnt && Uart.OutOfCnt == 4) { - Uart.nOutOfCnt = 0; - - if (Uart.state == STATE_START_OF_COMMUNICATION) { - if (Uart.dropPosition == 4) { - Uart.state = STATE_RECEIVING; - Uart.OutOfCnt = 256; - } else if (Uart.dropPosition == 3) { - Uart.state = STATE_RECEIVING; - Uart.OutOfCnt = 4; - //Uart.output[Uart.byteCnt] = 0xdd; - //Uart.byteCnt++; - } else { - Uart.state = STATE_UNSYNCD; - Uart.highCnt = 0; - } - Uart.dropPosition = 0; - } else { - // RECEIVING DATA - // 1 out of 4 - if (!Uart.dropPosition) { - Uart.state = STATE_UNSYNCD; - Uart.highCnt = 0; - //error = 9; - } else { - Uart.shiftReg >>= 2; - - // Swap bit order - Uart.dropPosition--; - //if(Uart.dropPosition == 1) { Uart.dropPosition = 2; } - //else if(Uart.dropPosition == 2) { Uart.dropPosition = 1; } - - Uart.shiftReg ^= ((Uart.dropPosition & 0x03) << 6); - Uart.bitCnt += 2; - Uart.dropPosition = 0; - - if (Uart.bitCnt == 8) { - Uart.output[Uart.byteCnt] = (Uart.shiftReg & 0xff); - Uart.byteCnt++; - Uart.bitCnt = 0; - Uart.shiftReg = 0; - } - } - } - } else if (Uart.nOutOfCnt == Uart.OutOfCnt) { - // RECEIVING DATA - // 1 out of 256 - if (!Uart.dropPosition) { - Uart.state = STATE_UNSYNCD; - Uart.highCnt = 0; - //error = 3; - } else { - Uart.dropPosition--; - Uart.output[Uart.byteCnt] = (Uart.dropPosition & 0xff); - Uart.byteCnt++; - Uart.bitCnt = 0; - Uart.shiftReg = 0; - Uart.nOutOfCnt = 0; - Uart.dropPosition = 0; - } - } -*/ -/*if (error) { - Uart.output[Uart.byteCnt] = 0xAA; - Uart.byteCnt++; - Uart.output[Uart.byteCnt] = error & 0xFF; - Uart.byteCnt++; - Uart.output[Uart.byteCnt] = 0xAA; - Uart.byteCnt++; - Uart.output[Uart.byteCnt] = (Uart.bitBuffer >> 8) & 0xFF; - Uart.byteCnt++; - Uart.output[Uart.byteCnt] = Uart.bitBuffer & 0xFF; - Uart.byteCnt++; - Uart.output[Uart.byteCnt] = (Uart.syncBit >> 3) & 0xFF; - Uart.byteCnt++; - Uart.output[Uart.byteCnt] = 0xAA; - Uart.byteCnt++; - return true; -}*/ -/* - } - } else { - bit = Uart.bitBuffer & 0xf0; - bit >>= 4; - bit ^= 0x0F; // drops become 1s ;-) - if (bit) { - // should have been high or at least (4 * 128) / fc - // according to ISO this should be at least (9 * 128 + 20) / fc - if (Uart.highCnt == 8) { - // we went low, so this could be start of communication - // it turns out to be safer to choose a less significant - // syncbit... so we check whether the neighbour also represents the drop - Uart.posCnt = 1; // apparently we are busy with our first half bit period - Uart.syncBit = bit & 8; - Uart.samples = 3; - - if (!Uart.syncBit) { Uart.syncBit = bit & 4; Uart.samples = 2; } - else if (bit & 4) { Uart.syncBit = bit & 4; Uart.samples = 2; bit <<= 2; } - - if (!Uart.syncBit) { Uart.syncBit = bit & 2; Uart.samples = 1; } - else if (bit & 2) { Uart.syncBit = bit & 2; Uart.samples = 1; bit <<= 1; } - - if (!Uart.syncBit) { Uart.syncBit = bit & 1; Uart.samples = 0; - if (Uart.syncBit && (Uart.bitBuffer & 8)) { - Uart.syncBit = 8; - - // the first half bit period is expected in next sample - Uart.posCnt = 0; - Uart.samples = 3; - } - } else if (bit & 1) { Uart.syncBit = bit & 1; Uart.samples = 0; } - - Uart.syncBit <<= 4; - Uart.state = STATE_START_OF_COMMUNICATION; - Uart.bitCnt = 0; - Uart.byteCnt = 0; - Uart.nOutOfCnt = 0; - Uart.OutOfCnt = 4; // Start at 1/4, could switch to 1/256 - Uart.dropPosition = 0; - Uart.shiftReg = 0; - //error = 0; - } else { - Uart.highCnt = 0; - } - } else { - if (Uart.highCnt < 8) - Uart.highCnt++; - } - } - return false; -} -*/ -//============================================================================= -// Manchester -//============================================================================= -static tDemodIc Demod; -static void DemodIcReset(void) { - Demod.bitCount = 0; - Demod.posCount = 0; - Demod.syncBit = 0; - Demod.shiftReg = 0; - Demod.buffer = 0; - Demod.buffer2 = 0; - Demod.buffer3 = 0; - Demod.buff = 0; - Demod.samples = 0; - Demod.len = 0; - Demod.sub = SUB_NONE; - Demod.state = DEMOD_IC_UNSYNCD; -} -static void DemodIcInit(uint8_t *data) { - Demod.output = data; - DemodIcReset(); -} - -// UART debug -// it adds the debug values which will be put in the tracelog, -// visible on client when running 'hf list iclass' -/* -pm3 --> hf li iclass -Recorded Activity (TraceLen = 162 bytes) - Start | End | Src | Data (! denotes parity error) | CRC | Annotation | -------------|------------|-----|-----------------------------------------------------------------|-----|--------------------| - 0 | 0 | Rdr |0a | | ACTALL - 1280 | 1280 | Tag |bb! 33! bb! 01 02 04 08 bb! | ok | - 1280 | 1280 | Rdr |0c | | IDENTIFY - 1616 | 1616 | Tag |bb! 33! bb! 00! 02 00! 02 bb! | ok | - 1616 | 1616 | Rdr |0a | | ACTALL - 2336 | 2336 | Tag |bb! d4! bb! 02 08 00! 08 bb! | ok | - 2336 | 2336 | Rdr |0c | | IDENTIFY - 2448 | 2448 | Tag |bb! 33! bb! 00! 00! 00! 02 bb! | ok | - 2448 | 2448 | Rdr |0a | | ACTALL - 2720 | 2720 | Tag |bb! d4! bb! 08 0b 01 04 bb! | ok | - 2720 | 2720 | Rdr |0c | | IDENTIFY - 3232 | 3232 | Tag |bb! d4! bb! 02 02 08 04 bb! | ok | -*/ -static void uart_debug(int error, int bit) { - Demod.output[Demod.len] = 0xBB; - Demod.len++; - Demod.output[Demod.len] = error & 0xFF; - Demod.len++; - Demod.output[Demod.len] = 0xBB; - Demod.len++; - Demod.output[Demod.len] = bit & 0xFF; - Demod.len++; - Demod.output[Demod.len] = Demod.buffer & 0xFF; - Demod.len++; - // Look harder ;-) - Demod.output[Demod.len] = Demod.buffer2 & 0xFF; - Demod.len++; - Demod.output[Demod.len] = Demod.syncBit & 0xFF; - Demod.len++; - Demod.output[Demod.len] = 0xBB; - Demod.len++; -} - /* * CARD TO READER * in ISO15693-2 mode - Manchester @@ -596,568 +107,63 @@ static void uart_debug(int error, int bit) { * * So for current implementation in ISO15693, its 330 µs from end of reader, to start of card. */ -static RAMFUNC int ManchesterDecoding_iclass(uint32_t v) { - int bit; - int modulation; - int error = 0; - - bit = Demod.buffer; - Demod.buffer = Demod.buffer2; - Demod.buffer2 = Demod.buffer3; - Demod.buffer3 = v; - - // too few bits? - if (Demod.buff < 3) { - Demod.buff++; - return false; - } - - if (Demod.state == DEMOD_IC_UNSYNCD) { - Demod.output[Demod.len] = 0xfa; - Demod.syncBit = 0; - //Demod.samples = 0; - Demod.posCount = 1; // This is the first half bit period, so after syncing handle the second part - - if (bit & 0x08) - Demod.syncBit = 0x08; - - if (bit & 0x04) { - if (Demod.syncBit) - bit <<= 4; - - Demod.syncBit = 0x04; - } - - if (bit & 0x02) { - if (Demod.syncBit) - bit <<= 2; - - Demod.syncBit = 0x02; - } - - if (bit & 0x01 && Demod.syncBit) - Demod.syncBit = 0x01; - - if (Demod.syncBit) { - Demod.len = 0; - Demod.state = DEMOD_IC_START_OF_COMMUNICATION; - Demod.sub = SUB_FIRST_HALF; - Demod.bitCount = 0; - Demod.shiftReg = 0; - Demod.samples = 0; - - if (Demod.posCount) { - - switch (Demod.syncBit) { - case 0x08: - Demod.samples = 3; - break; - case 0x04: - Demod.samples = 2; - break; - case 0x02: - Demod.samples = 1; - break; - case 0x01: - Demod.samples = 0; - break; - } - // SOF must be long burst... otherwise stay unsynced!!! - if (!(Demod.buffer & Demod.syncBit) || !(Demod.buffer2 & Demod.syncBit)) - Demod.state = DEMOD_IC_UNSYNCD; - - } else { - // SOF must be long burst... otherwise stay unsynced!!! - if (!(Demod.buffer2 & Demod.syncBit) || !(Demod.buffer3 & Demod.syncBit)) { - Demod.state = DEMOD_IC_UNSYNCD; - error = 0x88; - uart_debug(error, bit); - return false; - } - } - } - return false; - } - - // state is DEMOD is in SYNC from here on. - - modulation = bit & Demod.syncBit; - modulation |= ((bit << 1) ^ ((Demod.buffer & 0x08) >> 3)) & Demod.syncBit; - Demod.samples += 4; - - if (Demod.posCount == 0) { - Demod.posCount = 1; - Demod.sub = (modulation) ? SUB_FIRST_HALF : SUB_NONE; - return false; - } - - Demod.posCount = 0; - - if (modulation) { - - if (Demod.sub == SUB_FIRST_HALF) - Demod.sub = SUB_BOTH; - else - Demod.sub = SUB_SECOND_HALF; - } - - if (Demod.sub == SUB_NONE) { - if (Demod.state == DEMOD_IC_SOF_COMPLETE) { - Demod.output[Demod.len] = 0x0f; - Demod.len++; - Demod.state = DEMOD_IC_UNSYNCD; - return true; - } else { - Demod.state = DEMOD_IC_ERROR_WAIT; - error = 0x33; - } - } - - switch (Demod.state) { - - case DEMOD_IC_START_OF_COMMUNICATION: - if (Demod.sub == SUB_BOTH) { - - Demod.state = DEMOD_IC_START_OF_COMMUNICATION2; - Demod.posCount = 1; - Demod.sub = SUB_NONE; - } else { - Demod.output[Demod.len] = 0xab; - Demod.state = DEMOD_IC_ERROR_WAIT; - error = 0xd2; - } - break; - - case DEMOD_IC_START_OF_COMMUNICATION2: - if (Demod.sub == SUB_SECOND_HALF) { - Demod.state = DEMOD_IC_START_OF_COMMUNICATION3; - } else { - Demod.output[Demod.len] = 0xab; - Demod.state = DEMOD_IC_ERROR_WAIT; - error = 0xd3; - } - break; - - case DEMOD_IC_START_OF_COMMUNICATION3: - if (Demod.sub == SUB_SECOND_HALF) { - Demod.state = DEMOD_IC_SOF_COMPLETE; - } else { - Demod.output[Demod.len] = 0xab; - Demod.state = DEMOD_IC_ERROR_WAIT; - error = 0xd4; - } - break; - - case DEMOD_IC_SOF_COMPLETE: - case DEMOD_IC_MANCHESTER_D: - case DEMOD_IC_MANCHESTER_E: - // OPPOSITE FROM ISO14443 - 11110000 = 0 (1 in 14443) - // 00001111 = 1 (0 in 14443) - if (Demod.sub == SUB_SECOND_HALF) { // SUB_FIRST_HALF - Demod.bitCount++; - Demod.shiftReg = (Demod.shiftReg >> 1) ^ 0x100; - Demod.state = DEMOD_IC_MANCHESTER_D; - } else if (Demod.sub == SUB_FIRST_HALF) { // SUB_SECOND_HALF - Demod.bitCount++; - Demod.shiftReg >>= 1; - Demod.state = DEMOD_IC_MANCHESTER_E; - } else if (Demod.sub == SUB_BOTH) { - Demod.state = DEMOD_IC_MANCHESTER_F; - } else { - Demod.state = DEMOD_IC_ERROR_WAIT; - error = 0x55; - } - break; - - case DEMOD_IC_MANCHESTER_F: - // Tag response does not need to be a complete byte! - if (Demod.len > 0 || Demod.bitCount > 0) { - if (Demod.bitCount > 1) { // was > 0, do not interpret last closing bit, is part of EOF - Demod.shiftReg >>= (9 - Demod.bitCount); // right align data - Demod.output[Demod.len] = Demod.shiftReg & 0xff; - Demod.len++; - } - - Demod.state = DEMOD_IC_UNSYNCD; - return true; - } else { - Demod.output[Demod.len] = 0xad; - Demod.state = DEMOD_IC_ERROR_WAIT; - error = 0x03; - } - break; - - case DEMOD_IC_ERROR_WAIT: - Demod.state = DEMOD_IC_UNSYNCD; - break; - - default: - Demod.output[Demod.len] = 0xdd; - Demod.state = DEMOD_IC_UNSYNCD; - break; - } - - if (Demod.bitCount >= 8) { - Demod.shiftReg >>= 1; - Demod.output[Demod.len] = (Demod.shiftReg & 0xff); - Demod.len++; - Demod.bitCount = 0; - Demod.shiftReg = 0; - } - - if (error) { - uart_debug(error, bit); - return true; - } - - return false; -} //============================================================================= -// Finally, a `sniffer' for iClass communication +// a `sniffer' for iClass communication // Both sides of communication! //============================================================================= -static void iclass_setup_sniff(void) { - if (DBGLEVEL > 3) Dbprintf("iclass_setup_sniff Enter"); - - LEDsoff(); - - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - - // connect Demodulated Signal to ADC: - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - - // Set up the synchronous serial port - FpgaSetupSsc(); - - BigBuf_free(); - BigBuf_Clear_ext(false); - clear_trace(); - set_tracing(true); - - // Initialize Demod and Uart structs - DemodIcInit(BigBuf_malloc(ICLASS_BUFFER_SIZE)); - - uart_init(BigBuf_malloc(ICLASS_BUFFER_SIZE)); - //UartIcInit(BigBuf_malloc(ICLASS_BUFFER_SIZE)); - - if (DBGLEVEL > 1) { - // Print debug information about the buffer sizes - Dbprintf("[+] Sniffing buffers initialized:"); - Dbprintf(" Trace: %i bytes", BigBuf_max_traceLen()); - Dbprintf(" Reader -> tag: %i bytes", ICLASS_BUFFER_SIZE); - Dbprintf(" tag -> Reader: %i bytes", ICLASS_BUFFER_SIZE); - Dbprintf(" DMA: %i bytes", ICLASS_DMA_BUFFER_SIZE); - } - - // Set FPGA in the appropriate mode - // put the FPGA in the appropriate mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_SNIFFER); - SpinDelay(200); - - // Start the SSP timer - StartCountSspClk(); - - LED_A_ON(); - if (DBGLEVEL > 3) Dbprintf("[+] iclass_setup_sniff Exit"); +void SniffIClass(uint8_t jam_search_len, uint8_t *jam_search_string) { + SniffIso15693(jam_search_len, jam_search_string); } -//----------------------------------------------------------------------------- -// Record the sequence of commands sent by the reader to the tag, with -// triggering so that we start recording at the point that the tag is moved -// near the reader. -//----------------------------------------------------------------------------- -// turn off afterwards -void RAMFUNC SniffIClass(void) { - - //int datalen = 0; - uint32_t previous_data = 0; - uint32_t time_0 = 0, time_start = 0, time_stop; - uint32_t sniffCounter = 0; - bool TagIsActive = false; - bool ReaderIsActive = false; - - iclass_setup_sniff(); - - // The DMA buffer, used to stream samples from the FPGA - // *dmaBuf is the start reference. - uint8_t *dmaBuf = BigBuf_malloc(ICLASS_DMA_BUFFER_SIZE); - // pointer to samples from fpga - uint8_t *data = dmaBuf; - - // Setup and start DMA. - if (!FpgaSetupSscDma(dmaBuf, ICLASS_DMA_BUFFER_SIZE)) { - if (DBGLEVEL > 1) DbpString("[-] FpgaSetupSscDma failed. Exiting"); - return; - } - - // time ZERO, the point from which it all is calculated. - time_0 = GetCountSspClk(); - - // loop and listen - // every sample (1byte in data), - // contains HIGH nibble = reader data - // contains LOW nibble = tag data - // so two bytes are needed in order to get 1byte of either reader or tag data. (ie 2 sample bytes) - // since reader data is manchester encoded, we need 2bytes of data in order to get one demoded byte. (ie: 4 sample bytes) - uint16_t checked = 0; - for (;;) { - WDT_HIT(); - - if (checked == 1000) { - if (BUTTON_PRESS() || data_available()) break; - checked = 0; - } - ++checked; - - previous_data <<= 8; - previous_data |= *data; - - sniffCounter++; - data++; - - if (data == dmaBuf + ICLASS_DMA_BUFFER_SIZE) { - data = dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNCR = ICLASS_DMA_BUFFER_SIZE; - } - - // every odd sample - if (sniffCounter & 0x01) { - // no need to try decoding reader data if the tag is sending - // READER TO CARD - if (!TagIsActive) { - LED_C_INV(); - // HIGH nibble is always reader data. - uint8_t reader_byte = (previous_data & 0xF0) | (*data >> 4); - uart_samples(reader_byte); - if (Uart.frame_done) { - time_stop = GetCountSspClk() - time_0; - LogTrace(Uart.buf, Uart.len, time_start, time_stop, NULL, true); - DemodIcReset(); - uart_reset(); - } else { - time_start = GetCountSspClk() - time_0; - } - ReaderIsActive = Uart.frame_done; - } - } - // every four sample - if ((sniffCounter % 4) == 0) { - // need two samples to feed Manchester - // no need to try decoding tag data if the reader is sending - and we cannot afford the time - // CARD TO READER - if (!ReaderIsActive) { - LED_C_INV(); - // LOW nibble is always tag data. - /* - uint32_t tag_byte = - ((previous_data & 0x0F000000) >> 8 ) | - ((previous_data & 0x000F0000) >> 4 ) | - ((previous_data & 0x00000F00) ) | - ((previous_data & 0x0000000F) << 4 ) | - (*data & 0xF); - */ - - - uint8_t tag_byte = ((previous_data & 0xF) << 4) | (*data & 0xF); - if (ManchesterDecoding_iclass(tag_byte)) { - time_stop = GetCountSspClk() - time_0; - LogTrace(Demod.output, Demod.len, time_start, time_stop, NULL, false); - DemodIcReset(); - uart_reset(); - } else { - time_start = GetCountSspClk() - time_0; - } - TagIsActive = (Demod.state != DEMOD_IC_UNSYNCD); - } - } - } // end main loop - - /* - if (DBGLEVEL >= 1) { - DbpString("[+] Sniff statistics:"); - Dbhexdump(ICLASS_DMA_BUFFER_SIZE, data, false); - } - */ - switch_off(); +static void rotateCSN(uint8_t *original_csn, uint8_t *rotated_csn) { + for (uint8_t i = 0; i < 8; i++) + rotated_csn[i] = (original_csn[i] >> 3) | (original_csn[(i + 1) % 8] << 5); } -static void rotateCSN(uint8_t *originalCSN, uint8_t *rotatedCSN) { - int i; - for (i = 0; i < 8; i++) - rotatedCSN[i] = (originalCSN[i] >> 3) | (originalCSN[(i + 1) % 8] << 5); +// Encode SOF only +static void CodeIClassTagSOF(void) { + ToSendReset(); + ToSend[++ToSendMax] = 0x1D; + ToSendMax++; } -//----------------------------------------------------------------------------- -// SIMULATION -// Wait for commands from reader -// Stop when button is pressed -// Or return TRUE when command is captured -//----------------------------------------------------------------------------- -static bool GetIClassCommandFromReader(uint8_t *received, int *len, int maxLen) { - // Set FPGA mode to "simulated ISO 14443 tag", no modulation (listen - // only, since we are receiving, not transmitting). - // Signal field is off with the appropriate LED - LED_D_OFF(); - uart_init(received); - - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); - // clear RXRDY: - uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - (void)b; - - uint16_t checked = 0; - for (;;) { - - WDT_HIT(); - - if (checked == 1000) { - if (BUTTON_PRESS() || data_available()) return false; - checked = 0; - } - ++checked; - - // keep tx buffer in a defined state anyway. - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) - AT91C_BASE_SSC->SSC_THR = 0x00; - - // wait for byte to become available in rx holding register - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - - uart_samples(b); - if (Uart.frame_done) { - *len = Uart.len; - return true; - } - } - } - return false; -} /* -static uint8_t encode4Bits(const uint8_t b) { - // OTA, the least significant bits first - // Manchester encoding added - // The columns are - // 1 - Bit value to send - // 2 - Reversed (big-endian) - // 3 - Machester Encoded - // 4 - Hex values + * SOF comprises 3 parts; + * * An unmodulated time of 56.64 us + * * 24 pulses of 423.75 kHz (fc/32) + * * A logic 1, which starts with an unmodulated time of 18.88us + * followed by 8 pulses of 423.75kHz (fc/32) + * + * + * EOF comprises 3 parts: + * - A logic 0 (which starts with 8 pulses of fc/32 followed by an unmodulated + * time of 18.88us. + * - 24 pulses of fc/32 + * - An unmodulated time of 56.64 us + * + * + * A logic 0 starts with 8 pulses of fc/32 + * followed by an unmodulated time of 256/fc (~18,88us). + * + * A logic 0 starts with unmodulated time of 256/fc (~18,88us) followed by + * 8 pulses of fc/32 (also 18.88us) + * + * The mode FPGA_HF_SIMULATOR_MODULATE_424K_8BIT which we use to simulate tag, + * works like this. + * - A 1-bit input to the FPGA becomes 8 pulses on 423.5kHz (fc/32) (18.88us). + * - A 0-bit input to the FPGA becomes an unmodulated time of 18.88us + * + * In this mode + * SOF can be written as 00011101 = 0x1D + * EOF can be written as 10111000 = 0xb8 + * logic 1 be written as 01 = 0x1 + * logic 0 be written as 10 = 0x2 + * + * + */ - uint8_t c = b & 0xF; - switch (c) { - // 1 2 3 4 - case 15: - return 0x55; // 1111 -> 1111 -> 01010101 -> 0x55 - case 14: - return 0x95; // 1110 -> 0111 -> 10010101 -> 0x95 - case 13: - return 0x65; // 1101 -> 1011 -> 01100101 -> 0x65 - case 12: - return 0xa5; // 1100 -> 0011 -> 10100101 -> 0xa5 - case 11: - return 0x59; // 1011 -> 1101 -> 01011001 -> 0x59 - case 10: - return 0x99; // 1010 -> 0101 -> 10011001 -> 0x99 - case 9: - return 0x69; // 1001 -> 1001 -> 01101001 -> 0x69 - case 8: - return 0xa9; // 1000 -> 0001 -> 10101001 -> 0xa9 - case 7: - return 0x56; // 0111 -> 1110 -> 01010110 -> 0x56 - case 6: - return 0x96; // 0110 -> 0110 -> 10010110 -> 0x96 - case 5: - return 0x66; // 0101 -> 1010 -> 01100110 -> 0x66 - case 4: - return 0xa6; // 0100 -> 0010 -> 10100110 -> 0xa6 - case 3: - return 0x5a; // 0011 -> 1100 -> 01011010 -> 0x5a - case 2: - return 0x9a; // 0010 -> 0100 -> 10011010 -> 0x9a - case 1: - return 0x6a; // 0001 -> 1000 -> 01101010 -> 0x6a - default: - return 0xaa; // 0000 -> 0000 -> 10101010 -> 0xaa - } -} -*/ - -static uint8_t lut_enc[] = { 0xAA, 0x6A, 0x9A, 0x5A, 0xA6, 0x66, 0x96, 0x56, 0xA9, 0x69, 0x99, 0x59, 0xA5, 0x65, 0x95, 0x55 }; - -//----------------------------------------------------------------------------- -// Prepare tag messages -//----------------------------------------------------------------------------- -static void CodeIClassTagAnswer(const uint8_t *cmd, int len) { - /* - * SOF comprises 3 parts; - * * An unmodulated time of 56.64 us - * * 24 pulses of 423.75 kHz (fc/32) - * * A logic 1, which starts with an unmodulated time of 18.88us - * followed by 8 pulses of 423.75kHz (fc/32) - * - * - * EOF comprises 3 parts: - * - A logic 0 (which starts with 8 pulses of fc/32 followed by an unmodulated - * time of 18.88us. - * - 24 pulses of fc/32 - * - An unmodulated time of 56.64 us - * - * - * A logic 0 starts with 8 pulses of fc/32 - * followed by an unmodulated time of 256/fc (~18,88us). - * - * A logic 0 starts with unmodulated time of 256/fc (~18,88us) followed by - * 8 pulses of fc/32 (also 18.88us) - * - * The mode FPGA_HF_SIMULATOR_MODULATE_424K_8BIT which we use to simulate tag, - * works like this. - * - A 1-bit input to the FPGA becomes 8 pulses on 423.5kHz (fc/32) (18.88us). - * - A 0-bit input to the FPGA becomes an unmodulated time of 18.88us - * - * In this mode - * SOF can be written as 00011101 = 0x1D - * EOF can be written as 10111000 = 0xb8 - * logic 1 be written as 01 = 0x1 - * logic 0 be written as 10 = 0x2 - * - * */ - ToSendReset(); - - // Send SOF - ToSend[++ToSendMax] = 0x1D; - - int i; - for (i = 0; i < len; i++) { - uint8_t b = cmd[i]; - ToSend[++ToSendMax] = lut_enc[b & 0xF]; // least significant half - ToSend[++ToSendMax] = lut_enc[(b >> 4) & 0xF]; // most significant half - } - - // Send EOF - ToSend[++ToSendMax] = 0xB8; - //lastProxToAirDuration = 8*ToSendMax - 3*8 - 3*8;//Not counting zeroes in the beginning or end - // Convert from last byte pos to length - ToSendMax++; -} - -// Only SOF -static void CodeIClassTagSOF(void) { - //So far a dummy implementation, not used - //int lastProxToAirDuration =0; - - ToSendReset(); - // Send SOF - ToSend[++ToSendMax] = 0x1D; - // lastProxToAirDuration = 8*ToSendMax - 3*8;//Not counting zeroes in the beginning - - // Convert from last byte pos to length - ToSendMax++; -} /** * @brief SimulateIClass simulates an iClass card. @@ -1178,13 +184,8 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain LEDsoff(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - // this will clear out bigbuf memory, the eload command must select this before! - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaSetupSsc(); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - - // Enable and clear the trace + Iso15693InitTag(); + clear_trace(); set_tracing(true); @@ -1195,30 +196,29 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain uint8_t *emulator = BigBuf_get_EM_addr(); uint8_t mac_responses[PM3_CMD_DATA_SIZE] = { 0 }; - if (simType == 0) { + if (simType == ICLASS_SIM_MODE_CSN) { // Use the CSN from commandline memcpy(emulator, datain, 8); - doIClassSimulation(MODE_SIM_CSN, NULL); - } else if (simType == 1) { + doIClassSimulation(ICLASS_SIM_MODE_CSN, NULL); + } else if (simType == ICLASS_SIM_MODE_CSN_DEFAULT) { //Default CSN - uint8_t csn_crc[] = { 0x03, 0x1f, 0xec, 0x8a, 0xf7, 0xff, 0x12, 0xe0, 0x00, 0x00 }; + uint8_t csn[] = { 0x03, 0x1f, 0xec, 0x8a, 0xf7, 0xff, 0x12, 0xe0 }; // Use the CSN from commandline - memcpy(emulator, csn_crc, 8); - doIClassSimulation(MODE_SIM_CSN, NULL); - } else if (simType == 2) { + memcpy(emulator, csn, 8); + doIClassSimulation(ICLASS_SIM_MODE_CSN, NULL); + } else if (simType == ICLASS_SIM_MODE_READER_ATTACK) { Dbprintf("[+] going into attack mode, %d CSNS sent", numberOfCSNS); // In this mode, a number of csns are within datain. We'll simulate each one, one at a time // in order to collect MAC's from the reader. This can later be used in an offlne-attack // in order to obtain the keys, as in the "dismantling iclass"-paper. -#define EPURSE_MAC_SIZE 16 + #define EPURSE_MAC_SIZE 16 int i = 0; for (; i < numberOfCSNS && i * EPURSE_MAC_SIZE + 8 < PM3_CMD_DATA_SIZE; i++) { - // The usb data is 512 bytes, fitting 65 8-byte CSNs in there. memcpy(emulator, datain + (i * 8), 8); - if (doIClassSimulation(MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE)) { + if (doIClassSimulation(ICLASS_SIM_MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE)) { // Button pressed reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i, 0, mac_responses, i * EPURSE_MAC_SIZE); goto out; @@ -1226,11 +226,11 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain } reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i, 0, mac_responses, i * EPURSE_MAC_SIZE); - } else if (simType == 3) { + } else if (simType == ICLASS_SIM_MODE_FULL) { //This is 'full sim' mode, where we use the emulator storage for data. //ie: BigBuf_get_EM_addr should be previously filled with data from the "eload" command - doIClassSimulation(MODE_FULLSIM, NULL); - } else if (simType == 4) { + doIClassSimulation(ICLASS_SIM_MODE_FULL, NULL); + } else if (simType == ICLASS_SIM_MODE_READER_ATTACK_KEYROLL) { // This is the KEYROLL version of sim 2. // the collected data (mac_response) is doubled out since we are trying to collect both keys in the keyroll process. @@ -1287,11 +287,16 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { // free eventually allocated BigBuf memory BigBuf_free_keep_EM(); - State cipher_state; + uint16_t page_size = 32 * 8; + uint8_t current_page = 0; - uint8_t *csn = BigBuf_get_EM_addr(); - uint8_t *emulator = csn; - uint8_t sof_data[] = { 0x0F} ; + // maintain cipher states for both credit and debit key for each page + State cipher_state_KD[8]; + State cipher_state_KC[8]; + State *cipher_state = &cipher_state_KD[0]; + + uint8_t *emulator = BigBuf_get_EM_addr(); + uint8_t *csn = emulator; // CSN followed by two CRC bytes uint8_t anticoll_data[10] = { 0 }; @@ -1305,28 +310,75 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { AddCrc(anticoll_data, 8); AddCrc(csn_data, 8); - uint8_t diversified_key[8] = { 0 }; + uint8_t diversified_kd[8] = { 0 }; + uint8_t diversified_kc[8] = { 0 }; + uint8_t *diversified_key = diversified_kd; + + // configuration block + uint8_t conf_block[10] = {0x12, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0xFF, 0x3C, 0x00, 0x00}; // e-Purse uint8_t card_challenge_data[8] = { 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - //uint8_t card_challenge_data[8] = { 0 }; - if (simulationMode == MODE_FULLSIM) { - //The diversified key should be stored on block 3 - //Get the diversified key from emulator memory - memcpy(diversified_key, emulator + (8 * 3), 8); - //Card challenge, a.k.a e-purse is on block 2 - memcpy(card_challenge_data, emulator + (8 * 2), 8); + // AIA + uint8_t aia_data[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; + + if (simulationMode == ICLASS_SIM_MODE_FULL) { - //Precalculate the cipher state, feeding it the CC - cipher_state = opt_doTagMAC_1(card_challenge_data, diversified_key); + memcpy(conf_block, emulator + (8 * 1), 8); // blk 1 + memcpy(card_challenge_data, emulator + (8 * 2), 8); // e-purse, blk 2 + memcpy(diversified_kd, emulator + (8 * 3), 8); // Kd, blk 3 + memcpy(diversified_kc, emulator + (8 * 4), 8); // Kc, blk 4 + + + // (iceman) this only works for 2KS / 16KS tags. + // Use application data from block 5 + memcpy(aia_data, emulator + (8 * 5), 8); + // older 2K / 16K tags has its application issuer data on block 2 } + + AddCrc(conf_block, 8); + AddCrc(aia_data, 8); + // set epurse of sim2,4 attack if (reader_mac_buf != NULL) { memcpy(reader_mac_buf, card_challenge_data, 8); } + if ((conf_block[5] & 0x80) == 0x80) { + page_size = 256 * 8; + } + + // From PicoPass DS: + // When the page is in personalization mode this bit is equal to 1. + // Once the application issuer has personalized and coded its dedicated areas, this bit must be set to 0: + // the page is then "in application mode". + bool personalization_mode = conf_block[7] & 0x80; + + // chip memory may be divided in 8 pages + uint8_t max_page = ((conf_block[4] & 0x10) == 0x10) ? 0 : 7; + + // Precalculate the cipher states, feeding it the CC + cipher_state_KD[0] = opt_doTagMAC_1(card_challenge_data, diversified_kd); + cipher_state_KC[0] = opt_doTagMAC_1(card_challenge_data, diversified_kc); + + if (simulationMode == ICLASS_SIM_MODE_FULL) { + + for (int i = 1; i < max_page; i++) { + + // does all pages has their own epurse??) + uint8_t *epurse = emulator + (i * page_size) + (8 * 2); + uint8_t *kd = emulator + (i * page_size) + (8 * 3); + uint8_t *kc = emulator + (i * page_size) + (8 * 4); + + cipher_state_KD[i] = opt_doTagMAC_1(epurse, kd); + cipher_state_KC[i] = opt_doTagMAC_1(epurse, kc); + } + } + int exitLoop = 0; + + // Anti-collision process: // Reader 0a // Tag 0f // Reader 0c @@ -1340,8 +392,8 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { int trace_data_size = 0; // Respond SOF -- takes 1 bytes - uint8_t *resp_sof = BigBuf_malloc(2); - int resp_sof_Len; + uint8_t *resp_sof = BigBuf_malloc(1); + int resp_sof_len; // Anticollision CSN (rotated CSN) // 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte) @@ -1353,34 +405,27 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { uint8_t *resp_csn = BigBuf_malloc(28); int resp_csn_len; - // configuration Picopass 2ks + // configuration (blk 1) PICOPASS 2ks uint8_t *resp_conf = BigBuf_malloc(28); int resp_conf_len; - uint8_t conf_data[10] = {0x12, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0xFF, 0x3C, 0x00, 0x00}; - AddCrc(conf_data, 8); // e-Purse // 18: Takes 2 bytes for SOF/EOF and 8 * 2 = 16 bytes (2 bytes/bit) uint8_t *resp_cc = BigBuf_malloc(28); int resp_cc_len; + // Kd, Kc (blocks 3 and 4). Cannot be read. Always respond with 0xff bytes only + uint8_t *resp_ff = BigBuf_malloc(22); + int resp_ff_len; + uint8_t ff_data[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; + AddCrc(ff_data, 8); + // Application Issuer Area uint8_t *resp_aia = BigBuf_malloc(28); int resp_aia_len; - uint8_t aia_data[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; - if (simulationMode == MODE_FULLSIM) { - - // (iceman) this only works for 2KS / 16KS tags. - // Use application data from block 5 - memcpy(aia_data, emulator + (8 * 5), 8); - - // older 2K / 16K tags has its application issuer data on block 2 - } - AddCrc(aia_data, 8); // receive command uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); - int len = 0; // Prepare card messages ToSendMax = 0; @@ -1388,142 +433,153 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { // First card answer: SOF CodeIClassTagSOF(); memcpy(resp_sof, ToSend, ToSendMax); - resp_sof_Len = ToSendMax; + resp_sof_len = ToSendMax; // Anticollision CSN - CodeIClassTagAnswer(anticoll_data, sizeof(anticoll_data)); + CodeIso15693AsTag(anticoll_data, sizeof(anticoll_data)); memcpy(resp_anticoll, ToSend, ToSendMax); resp_anticoll_len = ToSendMax; - // CSN - CodeIClassTagAnswer(csn_data, sizeof(csn_data)); + // CSN (block 0) + CodeIso15693AsTag(csn_data, sizeof(csn_data)); memcpy(resp_csn, ToSend, ToSendMax); resp_csn_len = ToSendMax; - // Configuration - CodeIClassTagAnswer(conf_data, sizeof(conf_data)); + // Configuration (block 1) + CodeIso15693AsTag(conf_block, sizeof(conf_block)); memcpy(resp_conf, ToSend, ToSendMax); resp_conf_len = ToSendMax; - // e-Purse - CodeIClassTagAnswer(card_challenge_data, sizeof(card_challenge_data)); + // e-Purse (block 2) + CodeIso15693AsTag(card_challenge_data, sizeof(card_challenge_data)); memcpy(resp_cc, ToSend, ToSendMax); resp_cc_len = ToSendMax; - // Application Issuer Area - CodeIClassTagAnswer(aia_data, sizeof(aia_data)); + // Kd, Kc (blocks 3 and 4) + CodeIso15693AsTag(ff_data, sizeof(ff_data)); + memcpy(resp_ff, ToSend, ToSendMax); + resp_ff_len = ToSendMax; + + // Application Issuer Area (block 5) + CodeIso15693AsTag(aia_data, sizeof(aia_data)); memcpy(resp_aia, ToSend, ToSendMax); resp_aia_len = ToSendMax; //This is used for responding to READ-block commands or other data which is dynamically generated //First the 'trace'-data, not encoded for FPGA - uint8_t *data_generic_trace = BigBuf_malloc((8 * 4) + 2);//8 bytes data + 2byte CRC is max tag answer + uint8_t *data_generic_trace = BigBuf_malloc(32 + 2);//8 bytes data + 2byte CRC is max tag answer //Then storage for the modulated data //Each bit is doubled when modulated for FPGA, and we also have SOF and EOF (2 bytes) - uint8_t *data_response = BigBuf_malloc(((8 * 4) + 2) * 2 + 2); + uint8_t *data_response = BigBuf_malloc((32 + 2) * 2 + 2); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); - SpinDelay(100); - StartCountSspClk(); + enum { IDLE, ACTIVATED, SELECTED, HALTED } chip_state = IDLE; - // To control where we are in the protocol - uint32_t time_0 = GetCountSspClk(); - uint32_t t2r_stime = 0, t2r_etime = 0; - uint32_t r2t_stime, r2t_etime = 0; - LED_A_ON(); - bool buttonPressed = false; + bool button_pressed = false; uint8_t cmd, options, block; + int len = 0; - while (!exitLoop) { + while (exitLoop == false) { WDT_HIT(); - - //Signal tracer, can be used to get a trigger for an oscilloscope.. - LED_B_OFF(); - LED_C_OFF(); - - r2t_stime = (GetCountSspClk() - time_0) << 4; - if (!GetIClassCommandFromReader(receivedCmd, &len, 0)) { - buttonPressed = true; + + uint32_t reader_eof_time = 0; + len = GetIso15693CommandFromReader(receivedCmd, MAX_FRAME_SIZE, &reader_eof_time); + if (len < 0) { + button_pressed = true; exitLoop = true; continue; } - r2t_etime = ((GetCountSspClk() - time_0) << 4) - r2t_stime; - // 330us normal wait, adjusted for our execution - - LED_C_ON(); //Signal tracer + // Now look at the reader command and provide appropriate responses + // default is no response: + modulated_response = NULL; + modulated_response_size = 0; + trace_data = NULL; + trace_data_size = 0; + // extra response data cmd = receivedCmd[0] & 0xF; options = (receivedCmd[0] >> 4) & 0xFF; block = receivedCmd[1]; - if (cmd == ICLASS_CMD_ACTALL) { // 0x0A + if (cmd == ICLASS_CMD_ACTALL && len == 1) { // 0x0A // Reader in anticollission phase - modulated_response = resp_sof; - modulated_response_size = resp_sof_Len; //order = 1; - trace_data = sof_data; - trace_data_size = sizeof(sof_data); - // adjusted for 330 + (160*num of slot) - goto send; - } else if (cmd == ICLASS_CMD_READ_OR_IDENTIFY) { // 0x0C - if (len == 1) { - // Reader asks for anticollission CSN - modulated_response = resp_anticoll; - modulated_response_size = resp_anticoll_len; //order = 2; - trace_data = anticoll_data; - trace_data_size = sizeof(anticoll_data); + if (chip_state != HALTED) { + modulated_response = resp_sof; + modulated_response_size = resp_sof_len; //order = 1; + chip_state = ACTIVATED; goto send; } + } else if (cmd == ICLASS_CMD_READ_OR_IDENTIFY) { // 0x0C + if (len == 1) { + // Reader asks for anticollission CSN + if (chip_state == SELECTED || chip_state == ACTIVATED) { + modulated_response = resp_anticoll; + modulated_response_size = resp_anticoll_len; //order = 2; + trace_data = anticoll_data; + trace_data_size = sizeof(anticoll_data); + goto send; + } + } + if (len == 4) { - // block0,1,2,5 is always readable. - switch (block) { - case 0: // csn (0c 00) - modulated_response = resp_csn; - modulated_response_size = resp_csn_len; - trace_data = csn_data; - trace_data_size = sizeof(csn_data); - goto send; - case 1: // configuration (0c 01) - modulated_response = resp_conf; - modulated_response_size = resp_conf_len; - trace_data = conf_data; - trace_data_size = sizeof(conf_data); - goto send; - case 2: // e-purse (0c 02) - modulated_response = resp_cc; - modulated_response_size = resp_cc_len; - trace_data = card_challenge_data; - trace_data_size = sizeof(card_challenge_data); - // set epurse of sim2,4 attack - if (reader_mac_buf != NULL) { - memcpy(reader_mac_buf, card_challenge_data, 8); - } - goto send; - case 5:// Application Issuer Area (0c 05) - modulated_response = resp_aia; - modulated_response_size = resp_aia_len; - trace_data = aia_data; - trace_data_size = sizeof(aia_data); - goto send; - default : { - if (simulationMode == MODE_FULLSIM) { // 0x0C - //Read block - //Take the data... - memcpy(data_generic_trace, emulator + (block << 3), 8); - AddCrc(data_generic_trace, 8); - trace_data = data_generic_trace; - trace_data_size = 10; - CodeIClassTagAnswer(trace_data, trace_data_size); - memcpy(modulated_response, ToSend, ToSendMax); - modulated_response_size = ToSendMax; + if (chip_state == SELECTED) { + // block0,1,2,5 is always readable. + switch (block) { + case 0: // csn (0c 00) + modulated_response = resp_csn; + modulated_response_size = resp_csn_len; + trace_data = csn_data; + trace_data_size = sizeof(csn_data); goto send; + case 1: // configuration (0c 01) + modulated_response = resp_conf; + modulated_response_size = resp_conf_len; + trace_data = conf_data; + trace_data_size = sizeof(conf_data); + goto send; + case 2: // e-purse (0c 02) + modulated_response = resp_cc; + modulated_response_size = resp_cc_len; + trace_data = card_challenge_data; + trace_data_size = sizeof(card_challenge_data); + // set epurse of sim2,4 attack + if (reader_mac_buf != NULL) { + memcpy(reader_mac_buf, card_challenge_data, 8); + } + goto send; + case 3: + case 4: // Kd, Kc, always respond with 0xff bytes + modulated_response = resp_ff; + modulated_response_size = resp_ff_len; + trace_data = ff_data; + trace_data_size = sizeof(ff_data); + goto send; + case 5:// Application Issuer Area (0c 05) + modulated_response = resp_aia; + modulated_response_size = resp_aia_len; + trace_data = aia_data; + trace_data_size = sizeof(aia_data); + goto send; + default : { + if (simulationMode == ICLASS_SIM_MODE_FULL) { // 0x0C + //Read block + //Take the data... + memcpy(data_generic_trace, emulator + (block << 3), 8); + AddCrc(data_generic_trace, 8); + trace_data = data_generic_trace; + trace_data_size = 10; + CodeIClassTagAnswer(trace_data, trace_data_size); + memcpy(modulated_response, ToSend, ToSendMax); + modulated_response_size = ToSendMax; + goto send; + } + break; } - break; - } - }//swith - }// if 4 + } // swith + } // selected + } // if 4 } else if (cmd == ICLASS_CMD_SELECT) { // 0x81 // Reader selects anticollission CSN. // Tag sends the corresponding real CSN @@ -1542,7 +598,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { goto send; } else if (cmd == ICLASS_CMD_CHECK) { // 0x05 // Reader random and reader MAC!!! - if (simulationMode == MODE_FULLSIM) { + if (simulationMode == ICLASS_SIM_MODE_FULL) { // NR, from reader, is in receivedCmd +1 opt_doTagMAC_2(cipher_state, receivedCmd + 1, data_generic_trace, diversified_key); @@ -1579,24 +635,28 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } goto send; } else if (cmd == ICLASS_CMD_HALT && options == 0 && len == 1) { - // Reader ends the session - modulated_response = resp_sof; - modulated_response_size = 0; //order = 0; - trace_data = NULL; - trace_data_size = 0; - goto send; - } else if (simulationMode == MODE_FULLSIM && cmd == ICLASS_CMD_READ4 && len == 4) { // 0x06 - //Read block - //Take the data... - memcpy(data_generic_trace, emulator + (block << 3), 8 * 4); - AddCrc(data_generic_trace, 8 * 4); - trace_data = data_generic_trace; - trace_data_size = 34; - CodeIClassTagAnswer(trace_data, trace_data_size); - memcpy(modulated_response, ToSend, ToSendMax); - modulated_response_size = ToSendMax; - goto send; - } else if (simulationMode == MODE_FULLSIM && cmd == ICLASS_CMD_UPDATE) { + + if (chip_state == SELECTED) { + // Reader ends the session + modulated_response = resp_sof; + modulated_response_size = resp_sof_Len; + chip_state = HALTED; + goto send; + } + } else if (simulationMode == ICLASS_SIM_MODE_FULL && cmd == ICLASS_CMD_READ4 && len == 4) { // 0x06 + + if (chip_state == SELECTED) { + //Read block + memcpy(data_generic_trace, emulator + (current_page * page_size) + (block * 8), 8 * 4); + AddCrc(data_generic_trace, 8 * 4); + trace_data = data_generic_trace; + trace_data_size = 34; + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(modulated_response, ToSend, ToSendMax); + modulated_response_size = ToSendMax; + goto send; + } + } else if (simulationMode == ICLASS_SIM_MODE_FULL && cmd == ICLASS_CMD_UPDATE) { //Probably the reader wants to update the nonce. Let's just ignore that for now. // OBS! If this is implemented, don't forget to regenerate the cipher_state @@ -1622,17 +682,12 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { // It appears we're fine ignoring this. //Otherwise, we should answer 8bytes (block) + 2bytes CRC // } else if(receivedCmd[0] == ICLASS_CMD_DETECT) { // 0x0F + } else if (receivedCmd[0] == 0x26 && len == 5) { + // standard ISO15693 INVENTORY command. Ignore. } else { - //#db# Unknown command received from reader (len=5): 26 1 0 f6 a 44 44 44 44 // Never seen this command before - if (DBGLEVEL == DBG_EXTENDED) + if (DBGLEVEL >= DBG_EXTENDED) print_result("[-] Unhandled command received ", receivedCmd, len); - - // Do not respond - modulated_response = resp_sof; - modulated_response_size = 0; //order = 0; - trace_data = NULL; - trace_data_size = 0; } send: @@ -1640,70 +695,20 @@ send: A legit tag has about 330us delay between reader EOT and tag SOF. **/ if (modulated_response_size > 0) { - t2r_stime = GetCountSspClkDelta(time_0) << 4; - SendIClassAnswer(modulated_response, modulated_response_size, 0); - t2r_etime = ((GetCountSspClk() - time_0) << 4) - t2r_stime; + uint32_t response_time = reader_eof_time + DELAY_ICLASS_VCD_TO_VICC_SIM; + TransmitTo15693Reader(modulated_response, modulated_response_size, &response_time, 0, false); + LogTrace(trace_data, trace_data_size, response_time*32, response_time*32 + modulated_response_size*32*64, NULL, false); } - - LogTrace(receivedCmd, len, r2t_stime, r2t_etime, NULL, true); - - if (trace_data != NULL) - LogTrace(trace_data, trace_data_size, t2r_stime, t2r_etime, NULL, false); } LEDsoff(); - if (buttonPressed) + if (button_pressed) DbpString("[+] button pressed"); - return buttonPressed; + return button_pressed; } -/** - * @brief sends our simulated tag answer - * @param resp - * @param respLen - * @param delay - */ -static int SendIClassAnswer(uint8_t *resp, int respLen, uint16_t delay) { - int i = 0; - volatile uint8_t b; - - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_424K_8BIT); - - AT91C_BASE_SSC->SSC_THR = 0x00; - - uint16_t checked = 0; - for (;;) { - - if (checked == 1000) { - if (BUTTON_PRESS() || data_available()) return 0; - checked = 0; - } - ++checked; - - // Prevent rx holding register from overflowing - if ((AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) { - b = AT91C_BASE_SSC->SSC_RHR; - (void) b; - } - - // Put byte into tx holding register as soon as it is ready - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - b = 0x00; - if (i < respLen) { - b = resp[i]; - //Hack - //b = 0xAC; - } - i++; - AT91C_BASE_SSC->SSC_THR = b; - } -// if (i > respLen + 4) break; - if (i > respLen + 1) break; - } - return 0; -} /// THE READER CODE @@ -1754,194 +759,42 @@ static void TransmitIClassCommand(const uint8_t *cmd, int len, int *wait) { time_rdr = GetCountSspClk(); } -//----------------------------------------------------------------------------- -// Prepare iClass reader command to send to FPGA -//----------------------------------------------------------------------------- -static void CodeIClassCommand(const uint8_t *cmd, int len) { - int i, j, k; +static void ReaderTransmitIClass(uint8_t *frame, int len, uint32_t *start_time) { - ToSendReset(); + CodeIso15693AsReader(frame, len); + TransmitTo15693Tag(ToSend, ToSendMax, start_time); + uint32_t end_time = *start_time + 32 * (8 * ToSendMax - 4); // substract the 4 padding bits after EOF - // (SOC) Start of Communication: 1 out of 4 - ToSend[++ToSendMax] = 0xf0; - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0x0f; - ToSend[++ToSendMax] = 0x00; - - // Modulate the bytes - for (i = 0; i < len; i++) { - uint8_t b = cmd[i]; - for (j = 0; j < 4; j++) { - for (k = 0; k < 4; k++) { - - if (k == (b & 3)) - ToSend[++ToSendMax] = 0x0f; - else - ToSend[++ToSendMax] = 0x00; - } - b >>= 2; - } - } - - // (EOC) End of Communication - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0xf0; - ToSend[++ToSendMax] = 0x00; - - // Convert from last character reference to length - ToSendMax++; + LogTrace(frame, len, *start_time * 4, end_time * 4, NULL, true); } -static void ReaderTransmitIClass_ext(uint8_t *frame, int len, int wait) { +static bool sendCmdGetResponseWithRetries(uint8_t* command, size_t cmdsize, uint8_t* resp, size_t max_resp_size, + uint8_t expected_size, uint8_t tries, uint32_t start_time, + uint32_t timeout, uint32_t *eof_time) { + while (tries-- > 0) { - // This is tied to other size changes - CodeIClassCommand(frame, len); - - // Select the card - TransmitIClassCommand(ToSend, ToSendMax, &wait); - LED_A_ON(); - - LogTrace(frame, len, g_rsamples, g_rsamples, NULL, true); -} -static void ReaderTransmitIClass(uint8_t *frame, int len) { - ReaderTransmitIClass_ext(frame, len, 330); -} - -//----------------------------------------------------------------------------- -// Wait a certain time for tag response -// If a response is captured return TRUE -// If it takes too long return FALSE -//----------------------------------------------------------------------------- -static int GetIClassAnswer(uint8_t *receivedResponse, int maxLen, int *wait) { - // buffer needs to be 512 bytes - // maxLen is not used... - bool skip = false; - - LED_D_ON(); - // Set FPGA mode to "reader listen mode", no modulation (listen - // only, since we are receiving, not transmitting). - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_LISTEN); - - // Setup UART/DEMOD to receive - DemodIcInit(receivedResponse); - - SpinDelayUs(g_wait); //310 Tout= 330us (iso15603-2) (330/21.3) take consideration for clock increments. - - // clear RXRDY: - uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - (void)b; - - uint16_t checked = 0; - - uint32_t card_start = GetCountSspClk(); - for (;;) { - WDT_HIT(); - - if (checked == 1000) { - if (BUTTON_PRESS() || data_available()) return false; - checked = 0; - } - ++checked; - - // Wait for byte be become available in rx holding register - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - - b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - skip = !skip; - if (skip) continue; - - if (ManchesterDecoding_iclass(b & 0x0f)) { - time_response = GetCountSspClk() - card_start; - return true; - } else if (GetCountSspClkDelta(card_start) > timeout && Demod.state == DEMOD_IC_UNSYNCD) { - return false; - } - } - } - return false; -} - -static int ReaderReceiveIClass(uint8_t *receivedAnswer) { - - if (GetIClassAnswer(receivedAnswer, 0, NULL) == false) - return 0; - - LogTrace(receivedAnswer, Demod.len, g_rsamples, g_rsamples, NULL, false); - return Demod.len; -} - -static void setupIclassReader(void) { - - LEDsoff(); - - // Start from off (no field generated) - // Signal field is off with the appropriate LED - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - - FpgaSetupSsc(); - - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - - clear_trace(); - set_tracing(true); - - // Now give it time to spin up. - // Signal field is on with the appropriate LED - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); - SpinDelay(500); - - StartCountSspClk(); - - LED_A_ON(); -} - -static bool sendCmdGetResponseWithRetries(uint8_t *command, size_t cmdsize, uint8_t *resp, uint8_t expected_size, int8_t retries) { - while (retries-- > 0) { - - ReaderTransmitIClass(command, cmdsize); - - //iceman - if received size is bigger than expected, we smash the stack here - // since its called with fixed sized arrays - - // update/write command takes 4ms to 15ms before responding - int old_wait = g_wait; - if ((command[0] & 0xF) == ICLASS_CMD_UPDATE) - g_wait = 3900; - - uint8_t got_n = ReaderReceiveIClass(resp); - - g_wait = old_wait; - - // 0xBB is the internal debug separator byte.. - if (expected_size != got_n || (resp[0] == 0xBB || resp[7] == 0xBB || resp[2] == 0xBB)) { - //try again -// SpinDelayUs(360); - continue; - } - - if (got_n == expected_size) + ReaderTransmitIClass(command, cmdsize, &start_time); + if (expected_size == GetIso15693AnswerFromTag(resp, max_resp_size, timeout, eof_time)) { return true; + } } return false; } /** * @brief Talks to an iclass tag, sends the commands to get CSN and CC. - * @param card_data where the CSN and CC are stored for return - * @return 0 = fail - * 1 = Got CSN - * 2 = Got CSN and CC + * @param card_data where the CSN, CONFIG, CC are stored for return + * 8 bytes csn + 8 bytes config + 8 bytes CC + * @return false = fail + * true = Got all. */ -static uint8_t handshakeIclassTag_ext(uint8_t *card_data, bool use_credit_key) { +static bool selectIclassTag(uint8_t *card_data, bool use_credit_key, uint32_t *eof_time) { - // act_all... - static uint8_t act_all[] = { ICLASS_CMD_ACTALL }; - static uint8_t identify[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x00, 0x73, 0x33 }; - static uint8_t select[] = { 0x80 | ICLASS_CMD_SELECT, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - uint8_t readcheck_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; + static uint8_t act_all[] = { ICLASS_CMD_ACTALL }; + static uint8_t identify[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x00, 0x73, 0x33 }; + static uint8_t select[] = { 0x80 | ICLASS_CMD_SELECT, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static uint8_t read_conf[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x01, 0xfa, 0x22 }; + static uint8_t read_check_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; // Bit 4: K.If this bit equals to one, the READCHECK will use the Credit Key (Kc); if equals to zero, Debit Key (Kd) willbe used // bit 7: parity. @@ -1950,58 +803,63 @@ static uint8_t handshakeIclassTag_ext(uint8_t *card_data, bool use_credit_key) { readcheck_cc[0] = 0x10 | ICLASS_CMD_READCHECK; uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; + + uint32_t start_time = GetCountSspClk(); - // Send act_all ( 330 timeout + 160 timeslot); - ReaderTransmitIClass_ext(act_all, 1, 330 + 180); + ReaderTransmitIClass(act_all, 1, &start_time); - // Card present? - if (ReaderReceiveIClass(resp) == 0) - return 0; + // card present? + if (GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_ACTALL, eof_time) < 0) + return false; - //Send Identify - ReaderTransmitIClass(identify, 1); + // send Identify + start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + ReaderTransmitIClass(identify, 1, &start_time); - //We expect a 10-byte response here, 8 byte anticollision-CSN and 2 byte CRC - if (ReaderReceiveIClass(resp) != 10) - return 0; + // expect a 10-byte response here, 8 byte anticollision-CSN and 2 byte CRC + uint8_t len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + if (len != 10) + return false; - //Copy the Anti-collision CSN to our select-packet + // copy the Anti-collision CSN to our select-packet memcpy(&select[1], resp, 8); - //Select the card - ReaderTransmitIClass(select, sizeof(select)); + // select the card + start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + ReaderTransmitIClass(select, sizeof(select), &start_time); - //We expect a 10-byte response here, 8 byte CSN and 2 byte CRC - if (ReaderReceiveIClass(resp) != 10) - return 0; + // expect a 10-byte response here, 8 byte CSN and 2 byte CRC + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + if (len != 10) + return false; - // Card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) - // ReaderTransmitIClass(readcheck_cc, sizeof(readcheck_cc)); - // if (ReaderReceiveIClass(resp) == 8) { - // //Save CC (e-purse) in response data - // memcpy(card_data+8, resp, 8); - // read_status++; - // } - - //Success - level 1, we got CSN //Save CSN in response data memcpy(card_data, resp, 8); - bool isBlk_2 = sendCmdGetResponseWithRetries(readcheck_cc, sizeof(readcheck_cc), resp, 8, 3); - - //Flag that we got to at least stage 1, read CSN - if (isBlk_2 == false) { - return 1; - } + // card selected, now read config (block1) (only 8 bytes no CRC) + start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + ReaderTransmitIClass(read_conf, sizeof(read_conf), &start_time); + + // expect a 8-byte response here + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + if (len != 8) + return false; //Save CC (e-purse) in response data memcpy(card_data + 8, resp, 8); - // we got all data; - return 2; -} -static uint8_t handshakeIclassTag(uint8_t *card_data) { - return handshakeIclassTag_ext(card_data, false); + // card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) + start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + ReaderTransmitIClass(readcheck_cc, sizeof(readcheck_cc), &start_time); + + // expect a 8-byte response here + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + if (len != 8) + return false; + + //Save CC (e-purse) in response data + memcpy(card_data + 16, resp, 8); + return true; } // Reader iClass Anticollission @@ -2015,129 +873,70 @@ void ReaderIClass(uint8_t arg0) { memset(card_data, 0xFF, sizeof(card_data)); memset(resp, 0xFF, sizeof(resp)); - //Read conf block CRC(0x01) => 0xfa 0x22 - uint8_t readConf[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x01, 0xfa, 0x22}; - - //Read App Issuer Area block CRC(0x05) => 0xde 0x64 - uint8_t readAA[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x05, 0xde, 0x64}; - - uint16_t tryCnt = 0; - bool abort_after_read = arg0 & FLAG_ICLASS_READER_ONLY_ONCE; // flag to read until one tag is found successfully bool try_once = arg0 & FLAG_ICLASS_READER_ONE_TRY; // flag to not to loop continuously, looking for tag bool use_credit_key = arg0 & FLAG_ICLASS_READER_CEDITKEY; // flag to use credit key - bool flagReadConfig = arg0 & FLAG_ICLASS_READER_CONF; // flag to read block1, configuration - bool flagReadCC = arg0 & FLAG_ICLASS_READER_CC; // flag to read block2, e-purse bool flagReadAIA = arg0 & FLAG_ICLASS_READER_AIA; // flag to read block5, application issuer area - setupIclassReader(); + if (flags & FLAG_ICLASS_READER_INIT) { + Iso15693InitReader(); + } - uint16_t checked = 0; - bool userCancelled = BUTTON_PRESS() || data_available(); - while (!userCancelled) { + if (flags & FLAG_ICLASS_READER_CLEARTRACE) { + set_tracing(true); + clear_trace(); + StartCountSspClk(); + } - WDT_HIT(); + uint32_t start_time = 0; + uint32_t eof_time = 0; - // if only looking for one card try 2 times if we missed it the first time - if (try_once && tryCnt > 10) { - if (DBGLEVEL > 1) DbpString("Failed to find a tag"); - break; + int read_status = selectIclassTag(card_data, use_credit_key, &eof_time); + if (read_status == 0) { + reply_mix(CMD_ACK, 0xFF, 0, 0, card_data, 0); + switch_off(); + return; + } + + uint8_t result_status = FLAG_ICLASS_READER_CSN | FLAG_ICLASS_READER_CONF | FLAG_ICLASS_READER_CC; + + //Read block 5, AIA + if (flagReadAIA) { + //Read App Issuer Area block CRC(0x05) => 0xde 0x64 + uint8_t readAA[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x05, 0xde, 0x64}; + + if (sendCmdGetResponseWithRetries(readAA, sizeof(readAA), resp, sizeof(resp), 10, 10, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time)) { + result_status |= FLAG_ICLASS_READER_AIA; + memcpy(card_data + (8 * 5), resp, 8); + } else { + if (DBGLEVEL >= DBG_EXTENDED) DbpString("Failed to dump AA block"); } + } - tryCnt++; - uint8_t result_status = 0; + // 0 : CSN + // 1 : Configuration + // 2 : e-purse + // 3 : kd / debit / aa2 (write-only) + // 4 : kc / credit / aa1 (write-only) + // 5 : AIA, Application issuer area + // + //Then we can 'ship' back the 6 * 8 bytes of data, + // with 0xFF:s in block 3 and 4. - int read_status = handshakeIclassTag_ext(card_data, use_credit_key); + LED_B_ON(); - if (read_status == 0) continue; - if (read_status == 1) result_status = FLAG_ICLASS_READER_CSN; - if (read_status == 2) result_status = FLAG_ICLASS_READER_CSN | FLAG_ICLASS_READER_CC; - - // handshakeIclass returns CSN|CC, but the actual block - // layout is CSN|CONFIG|CC, so here we reorder the data, - // moving CC forward 8 bytes - memcpy(card_data + 16, card_data + 8, 8); - - //Read block 1, config - if (flagReadConfig) { - if (sendCmdGetResponseWithRetries(readConf, sizeof(readConf), resp, 10, 5)) { - result_status |= FLAG_ICLASS_READER_CONF; - memcpy(card_data + 8, resp, 8); - } else { - if (DBGLEVEL > 1) DbpString("Failed to dump config block"); - } - } - - //Read block 5, AIA - if (flagReadAIA) { - if (sendCmdGetResponseWithRetries(readAA, sizeof(readAA), resp, 10, 5)) { - result_status |= FLAG_ICLASS_READER_AIA; - memcpy(card_data + (8 * 5), resp, 8); - } else { - if (DBGLEVEL > 1) DbpString("Failed to dump AA block"); - } - } - - // 0 : CSN - // 1 : Configuration - // 2 : e-purse - // 3 : kd / debit / aa2 (write-only) - // 4 : kc / credit / aa1 (write-only) - // 5 : AIA, Application issuer area - // - //Then we can 'ship' back the 6 * 8 bytes of data, - // with 0xFF:s in block 3 and 4. - - LED_B_ON(); - //Send back to client, but don't bother if we already sent this - - // only useful if looping in arm (not try_once && not abort_after_read) - if (memcmp(last_csn, card_data, 8) != 0) { - // If caller requires that we get Conf, CC, AA, continue until we got it - if (DBGLEVEL >= DBG_EXTENDED) { - Dbprintf("STATUS %02X | CSN %c | CONF %c | CC %c | AIA %c | ONCE %c | 1TRY %c", - result_status, - (result_status & FLAG_ICLASS_READER_CSN) ? 'Y' : 'N', - (result_status & FLAG_ICLASS_READER_CONF) ? 'Y' : 'N', - (result_status & FLAG_ICLASS_READER_CC) ? 'Y' : 'N', - (result_status & FLAG_ICLASS_READER_AIA) ? 'Y' : 'N' - ); - Dbprintf(" aar %c | to %c, | uc %c | frc %c | fra %c | cc %c", - abort_after_read ? 'Y' : 'N', - try_once ? 'Y' : 'N', - use_credit_key ? 'Y' : 'N', - flagReadConfig ? 'Y' : 'N', - flagReadAIA ? 'Y' : 'N', - flagReadCC ? 'Y' : 'N' - ); - } - - bool send = (result_status & FLAG_ICLASS_READER_CSN); - if (flagReadCC) - send |= (result_status & FLAG_ICLASS_READER_CC); - if (flagReadAIA) - send |= (result_status & FLAG_ICLASS_READER_AIA); - if (flagReadConfig) - send |= (result_status & FLAG_ICLASS_READER_CONF); - - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("SEND %c", send ? 'y' : 'n'); - - if (send) { - reply_mix(CMD_ACK, result_status, 0, 0, card_data, sizeof(card_data)); - if (abort_after_read) { - LED_B_OFF(); - return; - } - //Save that we already sent this.... - memcpy(last_csn, card_data, 8); + //Send back to client, but don't bother if we already sent this - + // only useful if looping in arm (not try_once && not abort_after_read) + if (memcmp(last_csn, card_data, 8) != 0) { + + if (send) { + reply_mix(CMD_ACK, result_status, 0, 0, card_data, sizeof(card_data)); + if (abort_after_read) { + LED_B_OFF(); + return; } } LED_B_OFF(); - - if (checked == 1000) { - userCancelled = BUTTON_PRESS() || data_available(); - checked = 0; - } - ++checked; } if (userCancelled) { @@ -2272,8 +1071,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { void iClass_ReadCheck(uint8_t blockno, uint8_t keytype) { uint8_t readcheck[] = { keytype, blockno }; uint8_t resp[] = {0, 0, 0, 0, 0, 0, 0, 0}; - size_t isOK = 0; - isOK = sendCmdGetResponseWithRetries(readcheck, sizeof(readcheck), resp, sizeof(resp), 6); + bool isOK = sendCmdGetResponseWithRetries(readcheck, sizeof(readcheck), resp, sizeof(resp), 8, 3, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); reply_mix(CMD_ACK, isOK, 0, 0, 0, 0); switch_off(); } @@ -2399,13 +1197,13 @@ out: // Tries to read block. // retries 10times. -bool iClass_ReadBlock(uint8_t blockno, uint8_t *data, uint8_t len) { +static bool iClass_ReadBlock(uint8_t blockno, uint8_t *data) { uint8_t resp[10]; uint8_t cmd[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00}; AddCrc(cmd + 1, 1); - // expect size 10, retry 5times - bool isOK = sendCmdGetResponseWithRetries(cmd, sizeof(cmd), resp, 10, 5); - memcpy(data, resp, len); + uint32_t eof_time; + bool isOK = sendCmdGetResponseWithRetries(readcmd, sizeof(readcmd), resp, sizeof(resp), 10, 10, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + memcpy(data, resp, 8); return isOK; } @@ -2417,33 +1215,32 @@ void iClass_ReadBlk(uint8_t blockno) { uint8_t blockdata[8]; } PACKED result; - result.isOK = iClass_ReadBlock(blockno, result.blockdata, sizeof(result.blockdata)); + LED_A_ON(); + result.isOK = iClass_ReadBlock(blockno, result.blockdata); switch_off(); reply_ng(CMD_HF_ICLASS_READBL, PM3_SUCCESS, (uint8_t *)&result, sizeof(result)); } // turn off afterwards void iClass_Dump(uint8_t blockno, uint8_t numblks) { - uint8_t blockdata[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - bool isOK = false; - uint8_t blkCnt = 0; - BigBuf_free(); - uint8_t *dataout = BigBuf_malloc(255 * 8); + uint8_t *dataout = BigBuf_malloc(0xFF * 8); if (dataout == NULL) { DbpString("[!] fail to allocate memory"); OnError(1); return; } - // fill mem with 0xFF - memset(dataout, 0xFF, 255 * 8); + memset(dataout, 0xFF, 0xFF * 8); + uint8_t blockdata[8] = {0}; + uint8_t blkCnt = 0; + bool isOK; for (; blkCnt < numblks; blkCnt++) { - isOK = iClass_ReadBlock(blockno + blkCnt, blockdata, sizeof(blockdata)); + isOK = iClass_ReadBlock(blockno + blkCnt, blockdata); // 0xBB is the internal debug separator byte.. if (!isOK || (blockdata[0] == 0xBB || blockdata[7] == 0xBB || blockdata[2] == 0xBB)) { //try again - isOK = iClass_ReadBlock(blockno + blkCnt, blockdata, sizeof(blockdata)); + isOK = iClass_ReadBlock(blockno + blkCnt, blockdata); if (!isOK) { Dbprintf("[!] block %02X failed to read", blkCnt + blockno); break; @@ -2459,15 +1256,42 @@ void iClass_Dump(uint8_t blockno, uint8_t numblks) { } static bool iClass_WriteBlock_ext(uint8_t blockno, uint8_t *data) { - uint8_t resp[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t write[] = { 0x80 | ICLASS_CMD_UPDATE, blockno, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; memcpy(write + 2, data, 12); // data + mac AddCrc(write + 1, 13); - return sendCmdGetResponseWithRetries(write, sizeof(write), resp, sizeof(resp), 5); + + uint8_t resp[10] = {0}; + uint32_t eof_time = 0; + bool isOK = sendCmdGetResponseWithRetries(write, sizeof(write), resp, sizeof(resp), 10, 3, 0, ICLASS_READER_TIMEOUT_UPDATE, &eof_time); + if (isOK == false) { + return false; + } + + uint8_t all_ff[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + if (blockNo == 2) { + // check response. e-purse update swaps first and second half + if (memcmp(data+4, resp, 4) || memcmp(data, resp+4, 4)) { + return false; + } + } else if (blockNo == 3 || blockNo == 4) { + // check response. Key updates always return 0xffffffffffffffff + if (memcmp(all_ff, resp, 8)) { + return false; + } + } else { + // check response. All other updates return unchanged data + if (memcmp(data, resp, 8)) { + return false; + } + } + + return true; } // turn off afterwards void iClass_WriteBlock(uint8_t blockno, uint8_t *data) { + LED_A_ON(); uint8_t isOK = iClass_WriteBlock_ext(blockno, data); switch_off(); reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); @@ -2475,23 +1299,20 @@ void iClass_WriteBlock(uint8_t blockno, uint8_t *data) { // turn off afterwards void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data) { - int i, written = 0; - int total_block = (endblock - startblock) + 1; - for (i = 0; i < total_block; i++) { - // block number - if (iClass_WriteBlock_ext(startblock + i, data + (i * 12))) { - Dbprintf("Write block [%02x] successful", startblock + i); + LED_A_ON(); + uint16_t written = 0; + uint16_t total_blocks = (endblock - startblock) + 1; + for (uint8_t b = startblock; b < total_blocks; b++) { + + if (iClass_WriteBlock_ext(b, data + ((b - startblock) * 12))) { + Dbprintf("Write block [%02x] successful", b); written++; } else { - Dbprintf("Write block [%02x] failed", startblock + i); + Dbprintf("Write block [%02x] failed", b); } } switch_off(); - - uint8_t isOK = 0; - if (written == total_block) - isOK = 1; - + uint8_t isOK = (written == total_blocks) ? 1 : 0; reply_ng(CMD_HF_ICLASS_CLONE, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); } diff --git a/armsrc/iclass.h b/armsrc/iclass.h index a9edbdfed..6ce23ceb7 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -13,7 +13,7 @@ #include "common.h" -void RAMFUNC SniffIClass(void); +void SniffIClass(uint8_t jam_search_len, uint8_t *jam_search_string); void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void ReaderIClass(uint8_t arg0); void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac); @@ -21,7 +21,6 @@ void iClass_Authentication(uint8_t *mac); void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain); void iClass_WriteBlock(uint8_t blockno, uint8_t *data); void iClass_ReadBlk(uint8_t blockno); -bool iClass_ReadBlock(uint8_t blockno, uint8_t *data, uint8_t len); void iClass_Dump(uint8_t blockno, uint8_t numblks); void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data); void iClass_ReadCheck(uint8_t blockno, uint8_t keytype); diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 4999c4d85..4448d3570 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- // Jonathan Westhues, split Nov 2006 +// piwi 2018 // // 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 @@ -24,20 +25,19 @@ #include "dbprint.h" #include "ticks.h" -#ifndef FWT_TIMEOUT_14B -// defaults to 2000ms -# define FWT_TIMEOUT_14B 35312 -#endif + #ifndef ISO14443B_DMA_BUFFER_SIZE -# define ISO14443B_DMA_BUFFER_SIZE 512 //changed this from 256 +# define ISO14443B_DMA_BUFFER_SIZE 128 #endif #ifndef RECEIVE_MASK # define RECEIVE_MASK (ISO14443B_DMA_BUFFER_SIZE-1) #endif +#define RECEIVE_SAMPLES_TIMEOUT 64 + // Guard Time (per 14443-2) #ifndef TR0 -# define TR0 32 //this value equals 8 ETU = 32 ssp clk (w/ 424 khz) +# define TR0 64 // TR0 max is 256/fs = 256/(848kHz) = 302us or 64 samples from FPGA #endif // Synchronization time (per 14443-2) @@ -51,30 +51,161 @@ // 4sample #define SEND4STUFFBIT(x) ToSendStuffBit(x);ToSendStuffBit(x);ToSendStuffBit(x);ToSendStuffBit(x); -//#define SEND4STUFFBIT(x) ToSendStuffBit(x); -// iceman, this threshold value, what makes 8 a good amplitude for this IQ values? -#ifndef SUBCARRIER_DETECT_THRESHOLD -# define SUBCARRIER_DETECT_THRESHOLD 8 -#endif static void iso14b_set_timeout(uint32_t timeout); static void iso14b_set_maxframesize(uint16_t size); // the block number for the ISO14443-4 PCB (used with APDUs) static uint8_t pcb_blocknum = 0; -static uint32_t iso14b_timeout = FWT_TIMEOUT_14B; +static uint32_t iso14b_timeout = TR0; + + +/* ISO 14443 B +* +* Reader to card | ASK - Amplitude Shift Keying Modulation (PCD to PICC for Type B) (NRZ-L encodig) +* Card to reader | BPSK - Binary Phase Shift Keying Modulation, (PICC to PCD for Type B) +* +* fc - carrier frequency 13.56 MHz +* TR0 - Guard Time per 14443-2 +* TR1 - Synchronization Time per 14443-2 +* TR2 - PICC to PCD Frame Delay Time (per 14443-3 Amendment 1) +* +* Elementary Time Unit (ETU) is +* - 128 Carrier Cycles (9.4395 µS) = 8 Subcarrier Units +* - 1 ETU = 1 bit +* - 10 ETU = 1 startbit, 8 databits, 1 stopbit (10bits length) +* - startbit is a 0 +* - stopbit is a 1 +* +* Start of frame (SOF) is +* - [10-11] ETU of ZEROS, unmodulated time +* - [2-3] ETU of ONES, +* +* End of frame (EOF) is +* - [10-11] ETU of ZEROS, unmodulated time +* +* -TO VERIFY THIS BELOW- +* The mode FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_BPSK which we use to simulate tag +* works like this: +* - A 1-bit input to the FPGA becomes 8 pulses at 847.5kHz (1.18µS / pulse) == 9.44us +* - A 0-bit input to the FPGA becomes an unmodulated time of 1.18µS or does it become 8 nonpulses for 9.44us +* +* FPGA doesn't seem to work with ETU. It seems to work with pulse / duration instead. +* +* Card sends data ub 847.e kHz subcarrier +* subcar |duration| FC division +* -------+--------+------------ +* 106kHz | 9.44µS | FC/128 +* 212kHz | 4.72µS | FC/64 +* 424kHz | 2.36µS | FC/32 +* 848kHz | 1.18µS | FC/16 +* -------+--------+------------ +* +* Reader data transmission: +* - no modulation ONES +* - SOF +* - Command, data and CRC_B +* - EOF +* - no modulation ONES +* +* Card data transmission +* - TR1 +* - SOF +* - data (each bytes is: 1startbit, 8bits, 1stopbit) +* - CRC_B +* - EOF +* +* FPGA implementation : +* At this point only Type A is implemented. This means that we are using a +* bit rate of 106 kbit/s, or fc/128. Oversample by 4, which ought to make +* things practical for the ARM (fc/32, 423.8 kbits/s, ~50 kbytes/s) +* +*/ + + //============================================================================= // An ISO 14443 Type B tag. We listen for commands from the reader, using -// a kind of thing that's implemented in software. When we get a +// a UART kind of thing that's implemented in software. When we get a // frame (i.e., a group of bytes between SOF and EOF), we check the CRC. // If it's good, then we can do something appropriate with it, and send // a response. //============================================================================= +//----------------------------------------------------------------------------- +// Code up a string of octets at layer 2 (including CRC, we don't generate +// that here) so that they can be transmitted to the reader. Doesn't transmit +// them yet, just leaves them ready to send in ToSend[]. +//----------------------------------------------------------------------------- +static void CodeIso14443bAsTag(const uint8_t *cmd, int len) { + int i; + + ToSendReset(); + + // Transmit a burst of ones, as the initial thing that lets the + // reader get phase sync. + // This loop is TR1, per specification + // TR1 minimum must be > 80/fs + // TR1 maximum 200/fs + // 80/fs < TR1 < 200/fs + // 10 ETU < TR1 < 24 ETU + + // Send TR1. + // 10-11 ETU * 4times samples ONES + for (i = 0; i < 20; i++) { + SEND4STUFFBIT(1); + } + + // Send SOF. + // 10-11 ETU * 4times samples ZEROS + for (i = 0; i < 10; i++) { + SEND4STUFFBIT(0); + } + + // 2-3 ETU * 4times samples ONES + for (i = 0; i < 2; i++) { + SEND4STUFFBIT(1); + } + + // data + for (i = 0; i < len; i++) { + + // Start bit + SEND4STUFFBIT(0); + + // Data bits + uint8_t b = cmd[i]; + for (int j = 0; j < 8; j++) { + SEND4STUFFBIT(b & 1); + b >>= 1; + } + + // Stop bit + SEND4STUFFBIT(1); + + // Extra Guard bit + // For PICC it ranges 0-18us (1etu = 9us) + //SEND4STUFFBIT(1); + } + + // Send EOF. + // 10-11 ETU * 4 sample rate = ZEROS + for(i = 0; i < 10; i++) { + SEND4STUFFBIT(0); + } + + // why this? + for(i = 0; i < 2; i++) { + SEND4STUFFBIT(1); + } + + // Convert from last byte pos to length + ToSendMax++; +} //----------------------------------------------------------------------------- -// The software that receives commands from the reader, and its state variables. +// The software UART that receives commands from the reader, and its state +// variables. //----------------------------------------------------------------------------- static struct { enum { @@ -103,7 +234,6 @@ static void Uart14bReset(void) { static void Uart14bInit(uint8_t *data) { Uart.output = data; Uart14bReset(); -// memset(Uart.output, 0x00, MAX_FRAME_SIZE); } //----------------------------------------------------------------------------- @@ -121,10 +251,6 @@ static struct { uint16_t bitCount; int posCount; int thisBit; - /* this had been used to add RSSI (Received Signal Strength Indication) to traces. Currently not implemented. - int metric; - int metricN; - */ uint16_t shiftReg; uint8_t *output; uint16_t len; @@ -150,7 +276,6 @@ static void Demod14bReset(void) { static void Demod14bInit(uint8_t *data) { Demod.output = data; Demod14bReset(); - // memset(Demod.output, 0x00, MAX_FRAME_SIZE); } @@ -175,6 +300,7 @@ static void iso14b_set_timeout(uint32_t timeout) { iso14b_timeout = timeout; if (DBGLEVEL >= 3) Dbprintf("ISO14443B Timeout set to %ld fwt", iso14b_timeout); } + static void iso14b_set_maxframesize(uint16_t size) { if (size > 256) size = MAX_FRAME_SIZE; @@ -183,142 +309,6 @@ static void iso14b_set_maxframesize(uint16_t size) { if (DBGLEVEL >= 3) Dbprintf("ISO14443B Max frame size set to %d bytes", Uart.byteCntMax); } -//----------------------------------------------------------------------------- -// Code up a string of octets at layer 2 (including CRC, we don't generate -// that here) so that they can be transmitted to the reader. Doesn't transmit -// them yet, just leaves them ready to send in ToSend[]. -//----------------------------------------------------------------------------- -static void CodeIso14443bAsTag(const uint8_t *cmd, int len) { - /* ISO 14443 B - * - * Reader to card | ASK - Amplitude Shift Keying Modulation (PCD to PICC for Type B) (NRZ-L encodig) - * Card to reader | BPSK - Binary Phase Shift Keying Modulation, (PICC to PCD for Type B) - * - * fc - carrier frequency 13.56 MHz - * TR0 - Guard Time per 14443-2 - * TR1 - Synchronization Time per 14443-2 - * TR2 - PICC to PCD Frame Delay Time (per 14443-3 Amendment 1) - * - * Elementary Time Unit (ETU) is - * - 128 Carrier Cycles (9.4395 µS) = 8 Subcarrier Units - * - 1 ETU = 1 bit - * - 10 ETU = 1 startbit, 8 databits, 1 stopbit (10bits length) - * - startbit is a 0 - * - stopbit is a 1 - * - * Start of frame (SOF) is - * - [10-11] ETU of ZEROS, unmodulated time - * - [2-3] ETU of ONES, - * - * End of frame (EOF) is - * - [10-11] ETU of ZEROS, unmodulated time - * - * -TO VERIFY THIS BELOW- - * The mode FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_BPSK which we use to simulate tag - * works like this: - * - A 1-bit input to the FPGA becomes 8 pulses at 847.5kHz (1.18µS / pulse) == 9.44us - * - A 0-bit input to the FPGA becomes an unmodulated time of 1.18µS or does it become 8 nonpulses for 9.44us - * - * FPGA doesn't seem to work with ETU. It seems to work with pulse / duration instead. - * - * Card sends data ub 847.e kHz subcarrier - * subcar |duration| FC division - * -------+--------+------------ - * 106kHz | 9.44µS | FC/128 - * 212kHz | 4.72µS | FC/64 - * 424kHz | 2.36µS | FC/32 - * 848kHz | 1.18µS | FC/16 - * -------+--------+------------ - * - * Reader data transmission: - * - no modulation ONES - * - SOF - * - Command, data and CRC_B - * - EOF - * - no modulation ONES - * - * Card data transmission - * - TR1 - * - SOF - * - data (each bytes is: 1startbit, 8bits, 1stopbit) - * - CRC_B - * - EOF - * - * FPGA implementation : - * At this point only Type A is implemented. This means that we are using a - * bit rate of 106 kbit/s, or fc/128. Oversample by 4, which ought to make - * things practical for the ARM (fc/32, 423.8 kbits/s, ~50 kbytes/s) - * - */ - - ToSendReset(); - - // Transmit a burst of ones, as the initial thing that lets the - // reader get phase sync. - // This loop is TR1, per specification - // TR1 minimum must be > 80/fs - // TR1 maximum 200/fs - // 80/fs < TR1 < 200/fs - // 10 ETU < TR1 < 24 ETU - - // Send TR1. - // 10-11 ETU * 4times samples ONES - for (int i = 0; i < 10; i++) { SEND4STUFFBIT(1); } - - // Send SOF. - // 10-11 ETU * 4times samples ZEROS - for (int i = 0; i < 10; i++) { SEND4STUFFBIT(0); } - //for(i = 0; i < 10; i++) { ToSendStuffBit(0); } - - // 2-3 ETU * 4times samples ONES - for (int i = 0; i < 3; i++) { SEND4STUFFBIT(1); } - //for(i = 0; i < 3; i++) { ToSendStuffBit(1); } - - // data - for (int i = 0; i < len; ++i) { - - // Start bit - SEND4STUFFBIT(0); - //ToSendStuffBit(0); - - // Data bits - uint8_t b = cmd[i]; - for (int j = 0; j < 8; ++j) { - // if(b & 1) { - // SEND4STUFFBIT(1); - // //ToSendStuffBit(1); - // } else { - // SEND4STUFFBIT(0); - // //ToSendStuffBit(0); - // } - SEND4STUFFBIT(b & 1); - b >>= 1; - } - - // Stop bit - SEND4STUFFBIT(1); - //ToSendStuffBit(1); - - // Extra Guard bit - // For PICC it ranges 0-18us (1etu = 9us) - SEND4STUFFBIT(1); - //ToSendStuffBit(1); - } - - // Send EOF. - // 10-11 ETU * 4 sample rate = ZEROS - for (int i = 0; i < 10; i++) { SEND4STUFFBIT(0); } - //for(i = 0; i < 10; i++) { ToSendStuffBit(0); } - - // why this? - for (int i = 0; i < 2; i++) { SEND4STUFFBIT(1); } - //for(i = 0; i < 40; i++) { ToSendStuffBit(1); } - - // Convert from last byte pos to length - ++ToSendMax; -} - - /* Receive & handle a bit coming from the reader. * * This function is called 4 times per bit (every 2 subcarrier cycles). @@ -454,42 +444,16 @@ static int GetIso14443bCommandFromReader(uint8_t *received, uint16_t *len) { LED_D_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); - StartCountSspClk(); - - volatile uint8_t b; - - // clear receiving shift register and holding register - // What does this loop do? Is it TR1? - // loop is a wait/delay ? - /* - for(uint8_t c = 0; c < 10;) { - - // keep tx buffer in a defined state anyway. - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0xFF; - ++c; - } - } - */ // Now run a `software UART' on the stream of incoming samples. Uart14bInit(received); - uint8_t mask; - while (!BUTTON_PRESS()) { + while (BUTTON_PRESS() == false) { WDT_HIT(); - // keep tx buffer in a defined state anyway. - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x00; - } - - // Wait for byte be become available in rx holding register - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - - b = (uint8_t) AT91C_BASE_SSC->SSC_RHR; - - for (mask = 0x80; mask != 0; mask >>= 1) { - if (Handle14443bReaderUartBit(b & mask)) { + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + for (uint8_t mask = 0x80; mask != 0x00; mask >>= 1) { + if (Handle14443bReaderUartBit(b & mask)) { *len = Uart.byteCnt; return true; } @@ -499,57 +463,16 @@ static int GetIso14443bCommandFromReader(uint8_t *received, uint16_t *len) { return false; } -void ClearFpgaShiftingRegisters(void) { - - volatile uint8_t b; - - // clear receiving shift register and holding register - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) {}; - - b = AT91C_BASE_SSC->SSC_RHR; - (void) b; - - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) {}; - - b = AT91C_BASE_SSC->SSC_RHR; - (void) b; - - // wait for the FPGA to signal fdt_indicator == 1 (the FPGA is ready to queue new data in its delay line) - for (uint8_t j = 0; j < 5; j++) { // allow timeout - better late than never - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) {}; - if (AT91C_BASE_SSC->SSC_RHR) break; - } - - // Clear TXRDY: - //AT91C_BASE_SSC->SSC_THR = 0xFF; -} - -void WaitForFpgaDelayQueueIsEmpty(uint16_t delay) { - // Ensure that the FPGA Delay Queue is empty before we switch to TAGSIM_LISTEN again: - uint8_t fpga_queued_bits = delay >> 3; // twich /8 ?? >>3, - for (uint8_t i = 0; i <= fpga_queued_bits / 8 + 1;) { - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0xFF; - i++; - } - } -} static void TransmitFor14443b_AsTag(uint8_t *response, uint16_t len) { - volatile uint32_t b; - // Signal field is off with the appropriate LED LED_D_OFF(); - //uint16_t fpgasendQueueDelay = 0; // Modulate BPSK FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_BPSK); - SpinDelay(40); - - ClearFpgaShiftingRegisters(); - - FpgaSetupSsc(); + AT91C_BASE_SSC->SSC_THR = 0xFF; + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); // Transmit the response. for (uint16_t i = 0; i < len;) { @@ -561,13 +484,10 @@ static void TransmitFor14443b_AsTag(uint8_t *response, uint16_t len) { // Prevent rx holding register from overflowing if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - b = AT91C_BASE_SSC->SSC_RHR; + volatile uint32_t b = AT91C_BASE_SSC->SSC_RHR; (void)b; } } - - //WaitForFpgaDelayQueueIsEmpty(fpgasendQueueDelay); - AT91C_BASE_SSC->SSC_THR = 0xFF; } //----------------------------------------------------------------------------- // Main loop of simulated tag: receive commands from reader, decide what @@ -575,41 +495,26 @@ static void TransmitFor14443b_AsTag(uint8_t *response, uint16_t len) { //----------------------------------------------------------------------------- void SimulateIso14443bTag(uint32_t pupi) { - // setup device. - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - // connect Demodulated Signal to ADC: - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - // Set up the synchronous serial port - FpgaSetupSsc(); - - // allocate command receive buffer - BigBuf_free(); - BigBuf_Clear_ext(false); - - clear_trace(); //sim - set_tracing(true); - - uint16_t len, cmdsReceived = 0; - int cardSTATE = SIM_NOFIELD; - int vHf = 0; // in mV - // uint32_t time_0 = 0; - // uint32_t t2r_time = 0; - // uint32_t r2t_time = 0; - uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); - - // the only commands we understand is WUPB, AFI=0, Select All, N=1: -// static const uint8_t cmdWUPB[] = { ISO14443B_REQB, 0x00, 0x08, 0x39, 0x73 }; // WUPB - // ... and REQB, AFI=0, Normal Request, N=1: -// static const uint8_t cmdREQB[] = { ISO14443B_REQB, 0x00, 0x00, 0x71, 0xFF }; // REQB - // ... and ATTRIB -// static const uint8_t cmdATTRIB[] = { ISO14443B_ATTRIB, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; // ATTRIB + LED_A_ON(); + // the only commands we understand is WUPB, AFI=0, Select All, N=1: +// static const uint8_t cmdWUPB[] = { ISO14443B_REQB, 0x00, 0x08, 0x39, 0x73 }; // WUPB + // ... and REQB, AFI=0, Normal Request, N=1: +// static const uint8_t cmdREQB[] = { ISO14443B_REQB, 0x00, 0x00, 0x71, 0xFF }; // REQB + // ... and HLTB +// static const uint8_t cmdHLTB[] = { 0x50, 0xff, 0xff, 0xff, 0xff }; // HLTB + // ... and ATTRIB +// static const uint8_t cmdATTRIB[] = { ISO14443B_ATTRIB, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; // ATTRIB // ... if not PUPI/UID is supplied we always respond with ATQB, PUPI = 820de174, Application Data = 0x20381922, // supports only 106kBit/s in both directions, max frame size = 32Bytes, // supports ISO14443-4, FWI=8 (77ms), NAD supported, CID not supported: - uint8_t respATQB[] = { 0x50, 0x82, 0x0d, 0xe1, 0x74, 0x20, 0x38, 0x19, - 0x22, 0x00, 0x21, 0x85, 0x5e, 0xd7 - }; + uint8_t respATQB[] = { + 0x50, + 0x82, 0x0d, 0xe1, 0x74, + 0x20, 0x38, 0x19, + 0x22, 0x00, 0x21, 0x85, + 0x5e, 0xd7 + }; // response to HLTB and ATTRIB static const uint8_t respOK[] = {0x00, 0x78, 0xF0}; @@ -619,6 +524,26 @@ void SimulateIso14443bTag(uint32_t pupi) { num_to_bytes(pupi, 4, respATQB + 1); AddCrc14B(respATQB, 12); } + + // setup device. + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + // connect Demodulated Signal to ADC: + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + // Set up the synchronous serial port + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); + + // allocate command receive buffer + BigBuf_free(); + BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(true); + + uint16_t len, cmdsReceived = 0; + int cardSTATE = SIM_NOFIELD; + int vHf = 0; // in mV + uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); // prepare "ATQB" tag answer (encoded): CodeIso14443bAsTag(respATQB, sizeof(respATQB)); @@ -663,7 +588,7 @@ void SimulateIso14443bTag(uint32_t pupi) { // WUP in HALTED state if (len == 5) { if ((receivedCmd[0] == ISO14443B_REQB && (receivedCmd[2] & 0x8) == 0x8 && cardSTATE == SIM_HALTED) || - receivedCmd[0] == ISO14443B_REQB) { + receivedCmd[0] == ISO14443B_REQB) { LogTrace(receivedCmd, len, 0, 0, NULL, true); cardSTATE = SIM_SELECTING; } @@ -714,15 +639,21 @@ void SimulateIso14443bTag(uint32_t pupi) { // - SLOT MARKER // - ISO7816 // - emulate with a memory dump - Dbprintf("new cmd from reader: len=%d, cmdsRecvd=%d", len, cmdsReceived); + if (DBGLEVEL >= DBG_DEBUG) + Dbprintf("new cmd from reader: len=%d, cmdsRecvd=%d", len, cmdsReceived); // CRC Check if (len >= 3) { // if crc exists - if (!check_crc(CRC_14443_B, receivedCmd, len)) - DbpString("+++CRC fail"); - else - DbpString("CRC passes"); + if (!check_crc(CRC_14443_B, receivedCmd, len)) { + if (DBGLEVEL >= DBG_DEBUG) { + DbpString("CRC fail"); + } + } + } else { + if (DBGLEVEL >= DBG_DEBUG) { + DbpString("CRC passed"); + } } cardSTATE = SIM_IDLE; } @@ -734,9 +665,11 @@ void SimulateIso14443bTag(uint32_t pupi) { ++cmdsReceived; } - if (DBGLEVEL >= 2) + + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Emulator stopped. Trace length: %d ", BigBuf_get_traceLen()); - switch_off(); //simulate + + switch_off(); //simulate } //============================================================================= @@ -780,37 +713,8 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { // Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by abs(ci) + abs(cq) // Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by max(abs(ci),abs(cq)) + 1/2*min(abs(ci),abs(cq))) -#define CHECK_FOR_SUBCARRIER_old(void) { \ - if (ci < 0) { \ - if (cq < 0) { /* ci < 0, cq < 0 */ \ - if (cq < ci) { \ - v = -cq - (ci >> 1); \ - } else { \ - v = -ci - (cq >> 1); \ - } \ - } else { /* ci < 0, cq >= 0 */ \ - if (cq < -ci) { \ - v = -ci + (cq >> 1); \ - } else { \ - v = cq - (ci >> 1); \ - } \ - } \ - } else { \ - if (cq < 0) { /* ci >= 0, cq < 0 */ \ - if (-cq < ci) { \ - v = ci - (cq >> 1); \ - } else { \ - v = -cq + (ci >> 1); \ - } \ - } else { /* ci >= 0, cq >= 0 */ \ - if (cq < ci) { \ - v = ci + (cq >> 1); \ - } else { \ - v = cq + (ci >> 1); \ - } \ - } \ - } \ - } + +#define SUBCARRIER_DETECT_THRESHOLD 8 //note: couldn't we just use MAX(ABS(ci),ABS(cq)) + (MIN(ABS(ci),ABS(cq))/2) from common.h - marshmellow #define CHECK_FOR_SUBCARRIER(void) { v = MAX(myI, myQ) + (MIN(myI, myQ) >> 1); } @@ -859,7 +763,9 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { Demod.posCount = 0; // start of SOF sequence } else { // maximum length of TR1 = 200 1/fs - if (Demod.posCount > 200 / 4) Demod.state = DEMOD_UNSYNCD; + if (Demod.posCount > 200 / 4){ + Demod.state = DEMOD_UNSYNCD; + } } Demod.posCount++; break; @@ -877,6 +783,7 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { LED_C_ON(); // Got SOF Demod.state = DEMOD_AWAITING_START_BIT; Demod.posCount = 0; + Demod.bitCount = 0; Demod.len = 0; } } else { @@ -895,8 +802,12 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { if (v > 0) { if (Demod.posCount > 6 * 2) { // max 19us between characters = 16 1/fs, max 3 etu after low phase of SOF = 24 1/fs - Demod.state = DEMOD_UNSYNCD; - LED_C_OFF(); + LED_C_OFF(); + if (Demod.bitCount == 0 && Demod.len == 0) { // received SOF only, this is valid for iClass/Picopass + return true; + } else { + Demod.state = DEMOD_UNSYNCD; + } } } else { // start bit detected Demod.bitCount = 0; @@ -932,11 +843,12 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { uint16_t s = Demod.shiftReg; // stop bit == '1', start bit == '0' - if ((s & 0x200) && (s & 0x001) == 0) { + if ((s & 0x200) && ((s & 0x001) == 0)) { // left shift to drop the startbit uint8_t b = (s >> 1); Demod.output[Demod.len] = b; - ++Demod.len; + Demod.len++; + Demod.bitCount = 0; Demod.state = DEMOD_AWAITING_START_BIT; } else { // this one is a bit hard, either its a correc byte or its unsynced. @@ -964,99 +876,114 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { * Demodulate the samples we received from the tag, also log to tracebuffer * quiet: set to 'TRUE' to disable debug output */ -static void GetTagSamplesFor14443bDemod(void) { - bool finished = false; -// int lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; +static int GetTagSamplesFor14443bDemod(int timeout) { + int ret = 0; + int maxBehindBy = 0; + int lastRxCounter, samples = 0; + int8_t ci, cq; uint32_t time_0 = 0, time_stop = 0; BigBuf_free(); + // The response (tag -> reader) that we're receiving. + uint8_t *receivedResponse = BigBuf_malloc(MAX_FRAME_SIZE); + + // The DMA buffer, used to stream samples from the FPGA + uint16_t *dmaBuf = (uint16_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE * sizeof(uint16_t)); // Set up the demodulator for tag -> reader responses. - Demod14bInit(BigBuf_malloc(MAX_FRAME_SIZE)); + Demod14bInit(receivedResponse); - // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t *) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); - int8_t *upTo = dmaBuf; + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) // Setup and start DMA. - if (!FpgaSetupSscDma((uint8_t *) dmaBuf, ISO14443B_DMA_BUFFER_SIZE)) { + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + if (FpgaSetupSscDma((uint8_t *) dmaBuf, ISO14443B_DMA_BUFFER_SIZE) == false) { if (DBGLEVEL > 1) Dbprintf("FpgaSetupSscDma failed. Exiting"); - return; + return -1; } + uint16_t *upTo = dmaBuf; + lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; + + // Signal field is ON with the appropriate LED: + LED_D_ON(); // And put the FPGA in the appropriate mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_848_KHZ | FPGA_HF_READER_MODE_RECEIVE_IQ); - // get current clock - time_0 = GetCountSspClk(); + for(;;) { + int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1); + if (behindBy > maxBehindBy) { + maxBehindBy = behindBy; + } - // rx counter - dma counter? (how much?) & (mod) mask > 2. (since 2bytes at the time is read) - while (!finished) { + if (behindBy < 1) continue; - LED_A_INV(); - WDT_HIT(); + ci = *upTo >> 8; + cq = *upTo; + upTo++; + lastRxCounter--; + if (upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning + lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; + } - // LSB is a fpga signal bit. - int ci = upTo[0]; - int cq = upTo[1]; - upTo += 2; -// lastRxCounter -= 2; + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; // DMA Next Counter registers + } + samples++; - // restart DMA buffer to receive again. - if (upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { - upTo = dmaBuf; -// lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; - } + if (Handle14443bTagSamplesDemod(ci, cq)) { + ret = Demod.len; + break; + } - // https://github.com/Proxmark/proxmark3/issues/103 - bool gotFrame = Handle14443bTagSamplesDemod(ci, cq); - time_stop = GetCountSspClk() - time_0; - - finished = (time_stop > iso14b_timeout || gotFrame); + if(samples > timeout && Demod.state < DEMOD_PHASE_REF_TRAINING) { + ret = -1; + LED_C_OFF(); + break; + } } FpgaDisableSscDma(); - if (upTo) - upTo = NULL; + if (ret < 0) { + return ret; + } - if (Demod.len > 0) - LogTrace(Demod.output, Demod.len, time_0, time_stop, NULL, false); + if (Demod.len > 0) { + LogTrace(Demod.output, Demod.len, time_0, time_stop, NULL, false); + } + + return ret; } //----------------------------------------------------------------------------- // Transmit the command (to the tag) that was placed in ToSend[]. //----------------------------------------------------------------------------- static void TransmitFor14443b_AsReader(void) { - int c; - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); - SpinDelay(60); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); - // What does this loop do? Is it TR1? - // 0xFF = 8 bits of 1. 1 bit == 1Etu,.. - // loop 10 * 8 = 80 ETU of delay, with a non modulated signal. why? - // 80*9 = 720us. + LED_B_ON(); - for (c = 0; c < 50;) { + for (int c = 0; c < ToSendMax; c++) { + uint8_t data = ToSend[c]; + for (int i = 0; i < 8; i++) { + uint16_t send_word = (data & 0x80) ? 0x0000 : 0xffff; + + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + + AT91C_BASE_SSC->SSC_THR = send_word; + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + AT91C_BASE_SSC->SSC_THR = send_word; - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0xFF; - c++; - } + data <<= 1; + } + WDT_HIT(); } - - // Send frame loop - for (c = 0; c < ToSendMax;) { - - // Put byte into tx holding register as soon as it is ready - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = ToSend[c++]; - } - } - WDT_HIT(); + LED_B_OFF(); } //----------------------------------------------------------------------------- @@ -1083,28 +1010,21 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) { // Send SOF // 10-11 ETUs of ZERO - for (int i = 0; i < 10; ++i) ToSendStuffBit(0); + for (int i = 0; i < 10; i++) + ToSendStuffBit(0); + // 2-3 ETUs of ONE ToSendStuffBit(1); ToSendStuffBit(1); -// ToSendStuffBit(1); // Sending cmd, LSB // from here we add BITS - for (int i = 0; i < len; ++i) { + for (int i = 0; i < len; i++) { // Start bit ToSendStuffBit(0); // Data bits uint8_t b = cmd[i]; - // if ( b & 1 ) ToSendStuffBit(1); else ToSendStuffBit(0); - // if ( (b>>1) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); - // if ( (b>>2) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); - // if ( (b>>3) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); - // if ( (b>>4) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); - // if ( (b>>5) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); - // if ( (b>>6) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); - // if ( (b>>7) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); ToSendStuffBit(b & 1); ToSendStuffBit((b >> 1) & 1); @@ -1119,20 +1039,24 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) { ToSendStuffBit(1); // EGT extra guard time // For PCD it ranges 0-57us (1etu = 9us) - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); +// ToSendStuffBit(1); +// ToSendStuffBit(1); +// ToSendStuffBit(1); } // Send EOF // 10-11 ETUs of ZERO - for (int i = 0; i < 10; ++i) ToSendStuffBit(0); + for (int i = 0; i < 10; i++) + ToSendStuffBit(0); // Transition time. TR0 - guard time // 8ETUS minum? // Per specification, Subcarrier must be stopped no later than 2 ETUs after EOF. // I'm guessing this is for the FPGA to be able to send all bits before we switch to listening mode - for (int i = 0; i < 24 ; ++i) ToSendStuffBit(1); + + // ensure that last byte is filled up + for (int i = 0; i < 8 ; ++i) + ToSendStuffBit(1); // TR1 - Synchronization time // Convert from last character reference to length @@ -1147,11 +1071,7 @@ static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len) { uint32_t time_start = GetCountSspClk(); CodeIso14443bAsReader(cmd, len); - TransmitFor14443b_AsReader(); - - if (g_trigger) LED_A_ON(); - LogTrace(cmd, len, time_start, GetCountSspClk(), NULL, true); } @@ -1159,7 +1079,7 @@ static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len) { * TODO: check CRC and preamble */ uint8_t iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *response) { - + LED_A_ON(); uint8_t message_frame[message_length + 4]; // PCB message_frame[0] = 0x0A | pcb_blocknum; @@ -1171,18 +1091,20 @@ uint8_t iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *r // EDC (CRC) AddCrc14B(message_frame, message_length + 2); // send - CodeAndTransmit14443bAsReader(message_frame, message_length + 4); //no + CodeAndTransmit14443bAsReader(message_frame, message_length + 4); // get response - GetTagSamplesFor14443bDemod(); //no + int ret = GetTagSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT); FpgaDisableTracing(); - if (Demod.len < 3) - return 0; + if (ret < 3) { + LED_A_OFF(); + return 0; + } // VALIDATE CRC if (!check_crc(CRC_14443_B, Demod.output, Demod.len)) { - if (DBGLEVEL > 3) Dbprintf("crc fail ICE"); + if (DBGLEVEL > 3) DbpString("CRC fail"); return 0; } // copy response contents @@ -1190,6 +1112,7 @@ uint8_t iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *r memcpy(response, Demod.output, Demod.len); return Demod.len; + LED_A_OFF(); } /** @@ -1202,12 +1125,14 @@ static uint8_t iso14443b_select_srx_card(iso14b_card_select_t *card) { uint8_t select_srx[] = { ISO14443B_SELECT, 0x00, 0x00, 0x00}; CodeAndTransmit14443bAsReader(init_srx, sizeof(init_srx)); - GetTagSamplesFor14443bDemod(); //no + int ret = GetTagSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT); FpgaDisableTracing(); - if (Demod.len == 0) + if (Demod.len == 0 || ret < 0) return 2; + Dbprintf("Randomly generated Chip ID (+ 2 byte CRC): %02x [%02x %02x]", Demod.output[0], Demod.output[1], Demod.output[2]); + // Randomly generated Chip ID if (card) card->chipid = Demod.output[0]; @@ -1216,10 +1141,10 @@ static uint8_t iso14443b_select_srx_card(iso14b_card_select_t *card) { AddCrc14B(select_srx, 2); CodeAndTransmit14443bAsReader(select_srx, sizeof(select_srx)); - GetTagSamplesFor14443bDemod(); //no + ret = GetTagSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT); FpgaDisableTracing(); - if (Demod.len != 3) + if (Demod.len != 3 || ret < 0) return 2; // Check the CRC of the answer: @@ -1235,10 +1160,10 @@ static uint8_t iso14443b_select_srx_card(iso14b_card_select_t *card) { AddCrc14B(select_srx, 1); CodeAndTransmit14443bAsReader(select_srx, 3); // Only first three bytes for this one - GetTagSamplesFor14443bDemod(); //no + ret = GetTagSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT); FpgaDisableTracing(); - if (Demod.len != 10) + if (Demod.len != 10 || ret < 0) return 2; // The check the CRC of the answer @@ -1258,7 +1183,7 @@ static uint8_t iso14443b_select_srx_card(iso14b_card_select_t *card) { * TODO: Support multiple cards (perform anticollision) * TODO: Verify CRC checksums */ -uint8_t iso14443b_select_card(iso14b_card_select_t *card) { +int iso14443b_select_card(iso14b_card_select_t *card) { // WUPB command (including CRC) // Note: WUPB wakes up all tags, REQB doesn't wake up tags in HALT state static const uint8_t wupb[] = { ISO14443B_REQB, 0x00, 0x08, 0x39, 0x73 }; @@ -1267,16 +1192,16 @@ uint8_t iso14443b_select_card(iso14b_card_select_t *card) { // first, wake up the tag CodeAndTransmit14443bAsReader(wupb, sizeof(wupb)); - GetTagSamplesFor14443bDemod(); //select_card + int ret = GetTagSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT); FpgaDisableTracing(); // ATQB too short? - if (Demod.len < 14) - return 2; + if (ret < 14) + return -1; // VALIDATE CRC if (!check_crc(CRC_14443_B, Demod.output, Demod.len)) - return 3; + return -2; if (card) { card->uidlen = 4; @@ -1292,16 +1217,16 @@ uint8_t iso14443b_select_card(iso14b_card_select_t *card) { AddCrc14B(attrib, 9); CodeAndTransmit14443bAsReader(attrib, sizeof(attrib)); - GetTagSamplesFor14443bDemod();//select_card + ret = GetTagSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT); FpgaDisableTracing(); // Answer to ATTRIB too short? - if (Demod.len < 3) - return 2; + if (ret < 3) + return -1; // VALIDATE CRC if (!check_crc(CRC_14443_B, Demod.output, Demod.len)) - return 3; + return -2; if (card) { @@ -1344,10 +1269,10 @@ void iso14443b_setup(void) { SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // Signal field is on with the appropriate LED - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); SpinDelay(100); // Start the timer @@ -1369,21 +1294,22 @@ static bool ReadSTBlock(uint8_t block) { uint8_t cmd[] = {ISO14443B_READ_BLK, block, 0x00, 0x00}; AddCrc14B(cmd, 2); CodeAndTransmit14443bAsReader(cmd, sizeof(cmd)); - GetTagSamplesFor14443bDemod(); + int res = GetTagSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT); FpgaDisableTracing(); // Check if we got an answer from the tag - if (Demod.len != 6) { + if (Demod.len != 6 || res < 0) { DbpString("[!] expected 6 bytes from tag, got less..."); return false; } // The check the CRC of the answer if (!check_crc(CRC_14443_B, Demod.output, Demod.len)) { - DbpString("[!] CRC Error block!"); + DbpString("CRC fail"); return false; } return true; } + void ReadSTMemoryIso14443b(uint8_t numofblocks) { // Make sure that we start from off, since the tags are stateful; // confusing things will happen if we don't reset them between reads. @@ -1427,6 +1353,9 @@ void ReadSTMemoryIso14443b(uint8_t numofblocks) { if (i == 0xff) break; ++i; } + + // Todo: iceman: send back read data to client. + // reply_ng(..., ); out: switch_off(); // disconnect raw @@ -1459,10 +1388,10 @@ static void iso1444b_setup_sniff(void) { SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Setup for the DMA. - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // Set FPGA in the appropriate mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | FPGA_HF_READER_RX_XCORR_SNOOP); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_848_KHZ | FPGA_HF_READER_MODE_SNIFF_IQ); SpinDelay(20); // Start the SSP timer @@ -1497,10 +1426,17 @@ void RAMFUNC SniffIso14443b(void) { bool ReaderIsActive = false; iso1444b_setup_sniff(); - + // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t *) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); - int8_t *data = dmaBuf; + uint16_t *dmaBuf = (uint16_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE * sizeof(uint16_t)); + uint16_t *upTo = dmaBuf; + int lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; + int8_t ci, cq; + int maxBehindBy = 0; + + // Count of samples received so far, so that we can include timing + // information in the trace buffer. + int samples = 0; // Setup and start DMA. if (!FpgaSetupSscDma((uint8_t *) dmaBuf, ISO14443B_DMA_BUFFER_SIZE)) { @@ -1513,25 +1449,48 @@ void RAMFUNC SniffIso14443b(void) { time_0 = GetCountSspClk(); // loop and listen - while (!BUTTON_PRESS()) { - WDT_HIT(); + for(;;) { - int ci = data[0]; - int cq = data[1]; - data += 2; + int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE - 1); + if (behindBy > maxBehindBy) { + maxBehindBy = behindBy; + } - if (data >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { - data = dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; - } + if (behindBy < 1) continue; + + ci = *upTo >> 8; + cq = *upTo; + upTo++; + lastRxCounter--; + if (upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning again + lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; + if (behindBy > (9 * ISO14443B_DMA_BUFFER_SIZE / 10)) { + Dbprintf("About to blow circular buffer - aborted! behindBy=%d", behindBy); + break; + } + } + + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; // DMA Next Counter registers + + WDT_HIT(); + if (BUTTON_PRESS()) { + DbpString("Button pressed, cancelled"); + break; + } + } + + samples++; // no need to try decoding reader data if the tag is sending - if (!TagIsActive) { + if (TagIsActive == false) { LED_A_INV(); if (Handle14443bReaderUartBit(ci & 0x01)) { + time_stop = GetCountSspClk() - time_0; LogTrace(Uart.output, Uart.byteCnt, time_start, time_stop, NULL, true); Uart14bReset(); @@ -1556,7 +1515,7 @@ void RAMFUNC SniffIso14443b(void) { // is this | 0x01 the error? & 0xfe in https://github.com/Proxmark/proxmark3/issues/103 // LSB is a fpga signal bit. - if (Handle14443bTagSamplesDemod(ci, cq)) { + if (Handle14443bTagSamplesDemod(ci/2, cq/2) >= 0) { time_stop = GetCountSspClk() - time_0; LogTrace(Demod.output, Demod.len, time_start, time_stop, NULL, false); Uart14bReset(); @@ -1568,6 +1527,7 @@ void RAMFUNC SniffIso14443b(void) { } } + FpgaDisableSscDma(); if (DBGLEVEL >= 2) { DbpString("[+] Sniff statistics:"); Dbprintf("[+] uart State: %x ByteCount: %i ByteCountMax: %i", Uart.state, Uart.byteCnt, Uart.byteCntMax); @@ -1593,12 +1553,14 @@ static void iso14b_set_trigger(bool enable) { * */ void SendRawCommand14443B_Ex(PacketCommandNG *c) { + iso14b_command_t param = c->oldarg[0]; size_t len = c->oldarg[1] & 0xffff; uint32_t timeout = c->oldarg[2]; uint8_t *cmd = c->data.asBytes; - uint8_t status; + int status; uint32_t sendlen = sizeof(iso14b_card_select_t); + uint8_t buf[PM3_CMD_DATA_SIZE] = {0x00}; if (DBGLEVEL > 3) Dbprintf("14b raw: param, %04x", param); @@ -1620,22 +1582,22 @@ void SendRawCommand14443B_Ex(PacketCommandNG *c) { if ((param & ISO14B_SELECT_STD) == ISO14B_SELECT_STD) { iso14b_card_select_t *card = (iso14b_card_select_t *)buf; status = iso14443b_select_card(card); - reply_mix(CMD_ACK, status, sendlen, 0, buf, sendlen); - // 0: OK 2: attrib fail, 3:crc fail, - if (status > 0) goto out; + reply_mix(CMD_HF_ISO14443B_COMMAND, status, sendlen, 0, buf, sendlen); + // 0: OK -1: attrib fail, -2:crc fail, + if (status != 0) goto out; } if ((param & ISO14B_SELECT_SR) == ISO14B_SELECT_SR) { iso14b_card_select_t *card = (iso14b_card_select_t *)buf; status = iso14443b_select_srx_card(card); - reply_mix(CMD_ACK, status, sendlen, 0, buf, sendlen); + reply_mix(CMD_HF_ISO14443B_COMMAND, status, sendlen, 0, buf, sendlen); // 0: OK 2: demod fail, 3:crc fail, if (status > 0) goto out; } if ((param & ISO14B_APDU) == ISO14B_APDU) { status = iso14443b_apdu(cmd, len, buf); - reply_mix(CMD_ACK, status, status, 0, buf, status); + reply_mix(CMD_HF_ISO14443B_COMMAND, status, status, 0, buf, status); } if ((param & ISO14B_RAW) == ISO14B_RAW) { @@ -1645,12 +1607,11 @@ void SendRawCommand14443B_Ex(PacketCommandNG *c) { } CodeAndTransmit14443bAsReader(cmd, len); // raw - GetTagSamplesFor14443bDemod(); // raw + status = GetTagSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT); // raw FpgaDisableTracing(); sendlen = MIN(Demod.len, PM3_CMD_DATA_SIZE); - status = (Demod.len > 0) ? 0 : 1; - reply_old(CMD_ACK, status, sendlen, 0, Demod.output, sendlen); + reply_old(CMD_HF_ISO14443B_COMMAND, status, sendlen, 0, Demod.output, sendlen); } out: diff --git a/armsrc/iso14443b.h b/armsrc/iso14443b.h index 62dbd6590..0013ad313 100644 --- a/armsrc/iso14443b.h +++ b/armsrc/iso14443b.h @@ -28,7 +28,7 @@ void iso14443b_setup(void); uint8_t iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *response); -uint8_t iso14443b_select_card(iso14b_card_select_t *card); +int iso14443b_select_card(iso14b_card_select_t *card); uint8_t iso14443b_select_card_srx(iso14b_card_select_t *card); void SimulateIso14443bTag(uint32_t pupi); diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index a82bb9a07..51dc7fac8 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -76,7 +76,7 @@ #define DELAY_ARM_TO_TAG 16 #define DELAY_TAG_TO_ARM 32 -//SSP_CLK runs at 13.56MHz / 4 = 3,39MHz when snooping. All values should be multiples of 16 +//SSP_CLK runs at 13.56MHz / 4 = 3,39MHz when sniffing. All values should be multiples of 16 #define DELAY_TAG_TO_ARM_SNIFF 32 #define DELAY_READER_TO_ARM_SNIFF 32 @@ -122,7 +122,7 @@ static void BuildInventoryResponse(uint8_t *uid); // resulting data rate is 26.48 kbit/s (fc/512) // cmd ... data // n ... length of data -static void CodeIso15693AsReader(uint8_t *cmd, int n) { +void CodeIso15693AsReader(uint8_t *cmd, int n) { ToSendReset(); @@ -195,7 +195,7 @@ static void CodeIso15693AsReader256(uint8_t *cmd, int n) { static const uint8_t encode_4bits[16] = { 0xaa, 0x6a, 0x9a, 0x5a, 0xa6, 0x66, 0x96, 0x56, 0xa9, 0x69, 0x99, 0x59, 0xa5, 0x65, 0x95, 0x55 }; -static void CodeIso15693AsTag(uint8_t *cmd, size_t len) { +void CodeIso15693AsTag(uint8_t *cmd, size_t len) { /* * SOF comprises 3 parts; * * An unmodulated time of 56.64 us @@ -241,7 +241,7 @@ static void CodeIso15693AsTag(uint8_t *cmd, size_t len) { } // Transmit the command (to the tag) that was placed in cmd[]. -static void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time) { +void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time) { FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_FULL_MOD); @@ -279,7 +279,7 @@ static void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time //----------------------------------------------------------------------------- // Transmit the command (to the reader) that was placed in cmd[]. //----------------------------------------------------------------------------- -static void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, uint32_t slot_time, bool slow) { +void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, uint32_t slot_time, bool slow) { // don't use the FPGA_HF_SIMULATOR_MODULATE_424K_8BIT minor mode. It would spoil GetCountSspClk() FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_424K); @@ -617,7 +617,7 @@ static void DecodeTagReset(DecodeTag_t *DecodeTag) { /* * Receive and decode the tag response, also log to tracebuffer */ -static int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeout, uint32_t *eof_time) { +int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeout, uint32_t *eof_time) { int samples = 0; int ret = 0; @@ -966,7 +966,7 @@ static RAMFUNC int Handle15693SampleFromReader(bool bit, DecodeReader_t *DecodeR DecodeReader->posCount++; if (DecodeReader->Coding == CODING_1_OUT_OF_4) { if (DecodeReader->posCount == 7*16) { // 7 bits jammed - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNOOP_AMPLITUDE); // stop jamming + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); // stop jamming // FpgaDisableTracing(); LED_D_OFF(); } else if (DecodeReader->posCount == 8*16) { @@ -976,7 +976,7 @@ static RAMFUNC int Handle15693SampleFromReader(bool bit, DecodeReader_t *DecodeR } } else { if (DecodeReader->posCount == 7*256) { // 7 bits jammend - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNOOP_AMPLITUDE); // stop jamming + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); // stop jamming LED_D_OFF(); } else if (DecodeReader->posCount == 8*256) { DecodeReader->posCount = 0; @@ -1005,7 +1005,7 @@ static RAMFUNC int Handle15693SampleFromReader(bool bit, DecodeReader_t *DecodeR // correctly. //----------------------------------------------------------------------------- -static int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time) { +int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time) { int samples = 0; bool gotFrame = false; uint8_t b; @@ -1167,7 +1167,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { Dbprintf("Sniff started. Press PM3 Button to stop."); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNOOP_AMPLITUDE); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); LED_D_OFF(); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); @@ -1197,7 +1197,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { dma_start_time = GetCountSspClk() & 0xfffffff0; } - uint16_t snoopdata = *upTo++; + uint16_t sniffdata = *upTo++; if (upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. upTo = dmaBuf; // start reading the circular buffer from the beginning @@ -1218,7 +1218,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { } if (!TagIsActive) { // no need to try decoding reader data if the tag is sending - if (Handle15693SampleFromReader(snoopdata & 0x02, &DecodeReader)) { + if (Handle15693SampleFromReader(sniffdata & 0x02, &DecodeReader)) { uint32_t eof_time = dma_start_time + samples*16 + 8 - DELAY_READER_TO_ARM_SNIFF; // end of EOF if (DecodeReader.byteCount > 0) { @@ -1236,7 +1236,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { ReaderIsActive = false; ExpectTagAnswer = true; - } else if (Handle15693SampleFromReader(snoopdata & 0x01, &DecodeReader)) { + } else if (Handle15693SampleFromReader(sniffdata & 0x01, &DecodeReader)) { uint32_t eof_time = dma_start_time + samples*16 + 16 - DELAY_READER_TO_ARM_SNIFF; // end of EOF if (DecodeReader.byteCount > 0) { @@ -1261,7 +1261,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { } if (!ReaderIsActive && ExpectTagAnswer) { // no need to try decoding tag data if the reader is currently sending or no answer expected yet - if (Handle15693SamplesFromTag(snoopdata >> 2, &DecodeTag)) { + if (Handle15693SamplesFromTag(sniffdata >> 2, &DecodeTag)) { uint32_t eof_time = dma_start_time + samples*16 - DELAY_TAG_TO_ARM_SNIFF; // end of EOF if (DecodeTag.lastBit == SOF_PART2) { @@ -1550,7 +1550,7 @@ void ReaderIso15693(uint32_t parameter) { } // When SIM: initialize the Proxmark3 as ISO15693 tag -static void Iso15693InitTag(void) { +void Iso15693InitTag(void) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); diff --git a/armsrc/iso15693.h b/armsrc/iso15693.h index bc9b89eee..1b5405b13 100644 --- a/armsrc/iso15693.h +++ b/armsrc/iso15693.h @@ -23,6 +23,15 @@ #define DELAY_ISO15693_VCD_TO_VICC_READER 1056 // 1056/3,39MHz = 311.5us from end of command EOF to start of tag response #define DELAY_ISO15693_VICC_TO_VCD_READER 1024 // 1024/3.39MHz = 302.1us between end of tag response and next reader command +void Iso15693InitReader(void); +void Iso15693InitTag(void); +void CodeIso15693AsReader(uint8_t *cmd, int n); +void CodeIso15693AsTag(uint8_t *cmd, size_t len); + +void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, uint32_t slot_time, bool slow); +int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time); +void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time); +int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeout, uint32_t *eof_time); void RecordRawAdcSamplesIso15693(void); void AcquireRawAdcSamplesIso15693(void); diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index bfb19323b..141fefda5 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -134,7 +134,7 @@ static bool waitCmd14b(bool verbose) { PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { if ((resp.oldarg[0] & 0xFF) > 0) return false; @@ -314,7 +314,7 @@ static bool get_14b_UID(iso14b_card_select_t *card) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); - if (WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { uint8_t status = resp.oldarg[0]; if (status == 0) { @@ -330,7 +330,7 @@ static bool get_14b_UID(iso14b_card_select_t *card) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); - if (WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { uint8_t status = resp.oldarg[0]; if (status == 0) { @@ -515,7 +515,7 @@ static bool HF14B_Std_Info(bool verbose) { SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); switch_off_field_14b(); return false; @@ -556,7 +556,7 @@ static bool HF14B_ST_Info(bool verbose) { SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); return false; } @@ -611,7 +611,7 @@ static bool HF14B_ST_Reader(bool verbose) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); return false; } @@ -651,7 +651,7 @@ static bool HF14B_Std_Reader(bool verbose) { SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); return false; } @@ -695,7 +695,7 @@ static bool HF14B_Other_Reader(void) { // clearCommandBuffer(); // SendCommandMIX(CMD_HF_ISO14443B_COMMAND, flags, datalen, 0, data, datalen); // PacketResponseNG resp; - // WaitForResponse(CMD_ACK,&resp); + // WaitForResponse(CMD_HF_ISO14443B_COMMAND,&resp); // if (datalen > 2 ) { // PrintAndLogEx(NORMAL, "\n14443-3b tag found:"); @@ -709,7 +709,7 @@ static bool HF14B_Other_Reader(void) { // clearCommandBuffer(); // SendCommandMIX(CMD_HF_ISO14443B_COMMAND, flags, 1, 0, data, 1); // PacketResponseNG resp; - // WaitForResponse(CMD_ACK, &resp); + // WaitForResponse(CMD_HF_ISO14443B_COMMAND, &resp); // if (datalen > 0) { // PrintAndLogEx(NORMAL, "\n14443-3b tag found:"); @@ -723,7 +723,7 @@ static bool HF14B_Other_Reader(void) { // clearCommandBuffer(); // SendCommandMIX(CMD_HF_ISO14443B_COMMAND, flags, 1, 0, data, 1); // PacketResponseNG resp; - // WaitForResponse(CMD_ACK, &resp); + // WaitForResponse(CMD_HF_ISO14443B_COMMAND, &resp); // if (datalen > 0) { // PrintAndLogEx(NORMAL, "\n14443-3b tag found:"); @@ -899,7 +899,7 @@ static int CmdHF14BDump(const char *Cmd) { SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR, 0, 0, NULL, 0); //select - if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2000)) { if (resp.oldarg[0]) { PrintAndLogEx(INFO, "failed to select %" PRId64 " | %" PRId64, resp.oldarg[0], resp.oldarg[1]); goto out; @@ -915,7 +915,7 @@ static int CmdHF14BDump(const char *Cmd) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_APPEND_CRC | ISO14B_RAW, 2, 0, req, sizeof(req)); - if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2000)) { uint8_t status = resp.oldarg[0] & 0xFF; if (status > 0) { diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 501190170..639519c30 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -529,21 +529,19 @@ static int CmdHFiClassSim(const char *Cmd) { char cmdp = tolower(param_getchar(Cmd, 0)); if (strlen(Cmd) < 1 || cmdp == 'h') return usage_hf_iclass_sim(); - uint8_t simType = 0; uint8_t CSN[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t sim_type = param_get8ex(Cmd, 0, 0, 10); - simType = param_get8ex(Cmd, 0, 0, 10); - - if (simType == 0) { + if (sim_type == 0) { if (param_gethex(Cmd, 1, CSN, 16)) { PrintAndLogEx(ERR, "A CSN should consist of 16 HEX symbols"); return usage_hf_iclass_sim(); } - PrintAndLogEx(INFO, " simtype: %02x CSN: %s", simType, sprint_hex(CSN, 8)); + PrintAndLogEx(INFO, " simtype: %02x CSN: %s", sim_type, sprint_hex(CSN, 8)); } - if (simType > 4) { - PrintAndLogEx(ERR, "Undefined simptype %d", simType); + if (sim_type > 4) { + PrintAndLogEx(ERR, "Undefined simtype %d", sim_type); return usage_hf_iclass_sim(); } @@ -575,14 +573,14 @@ static int CmdHFiClassSim(const char *Cmd) { **/ uint8_t tries = 0; - switch (simType) { + switch (sim_type) { - case 2: { + case ICLASS_SIM_MODE_READER_ATTACK: { PrintAndLogEx(INFO, "Starting iCLASS sim 2 attack (elite mode)"); - PrintAndLogEx(INFO, "press Enter to cancel"); + PrintAndLogEx(INFO, "press " _YELLOW_("`enter`") " to cancel"); PacketResponseNG resp; clearCommandBuffer(); - SendCommandMIX(CMD_HF_ICLASS_SIMULATE, simType, NUM_CSNS, 0, csns, 8 * NUM_CSNS); + SendCommandMIX(CMD_HF_ICLASS_SIMULATE, sim_type, NUM_CSNS, 0, csns, 8 * NUM_CSNS); while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { tries++; @@ -625,13 +623,13 @@ static int CmdHFiClassSim(const char *Cmd) { free(dump); break; } - case 4: { + case ICLASS_SIM_MODE_READER_ATTACK_KEYROLL: { // reader in key roll mode, when it has two keys it alternates when trying to verify. PrintAndLogEx(INFO, "Starting iCLASS sim 4 attack (elite mode, reader in key roll mode)"); PrintAndLogEx(INFO, "press Enter to cancel"); PacketResponseNG resp; clearCommandBuffer(); - SendCommandMIX(CMD_HF_ICLASS_SIMULATE, simType, NUM_CSNS, 0, csns, 8 * NUM_CSNS); + SendCommandMIX(CMD_HF_ICLASS_SIMULATE, sim_type, NUM_CSNS, 0, csns, 8 * NUM_CSNS); while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { tries++; @@ -689,12 +687,13 @@ static int CmdHFiClassSim(const char *Cmd) { free(dump); break; } - case 1: - case 3: + case ICLASS_SIM_MODE_CSN: + case ICLASS_SIM_MODE_CSN_DEFAULT: + case ICLASS_SIM_MODE_FULL: default: { uint8_t numberOfCSNs = 0; clearCommandBuffer(); - SendCommandMIX(CMD_HF_ICLASS_SIMULATE, simType, numberOfCSNs, 0, CSN, 8); + SendCommandMIX(CMD_HF_ICLASS_SIMULATE, sim_type, numberOfCSNs, 0, CSN, 8); break; } } diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 117eea672..0d24d5bae 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -588,6 +588,18 @@ typedef struct { #define FLAG_ICLASS_READER_ONE_TRY 0x20 #define FLAG_ICLASS_READER_CEDITKEY 0x40 +// iCLASS simulation modes +#define ICLASS_SIM_MODE_CSN 0 +#define ICLASS_SIM_MODE_CSN_DEFAULT 1 +#define ICLASS_SIM_MODE_READER_ATTACK 2 +#define ICLASS_SIM_MODE_FULL 3 +#define ICLASS_SIM_MODE_READER_ATTACK_KEYROLL 4 +#define ICLASS_SIM_MODE_EXIT_AFTER_MAC 5 // note: device internal only + +#define MODE_SIM_CSN 0 +#define MODE_EXIT_AFTER_MAC 1 +#define MODE_FULLSIM 2 + // Dbprintf flags #define FLAG_RAWPRINT 0x00 #define FLAG_LOG 0x01 From d83a45f0cb1860ac70d1588b016e587d97f3c7b3 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 6 Jul 2020 15:16:00 +0200 Subject: [PATCH 021/139] fpga merge changes --- armsrc/felica.c | 2 +- armsrc/hfsnoop.c | 4 +- armsrc/iclass.c | 274 +++++++++++++++++++++------------------ client/src/cmdhficlass.c | 34 +++-- include/pm3_cmd.h | 18 ++- include/protocols.h | 2 +- 6 files changed, 180 insertions(+), 154 deletions(-) diff --git a/armsrc/felica.c b/armsrc/felica.c index f8375f0cc..d61719c33 100644 --- a/armsrc/felica.c +++ b/armsrc/felica.c @@ -336,7 +336,7 @@ static void BuildFliteRdblk(uint8_t *idm, int blocknum, uint16_t *blocks) { } static void TransmitFor18092_AsReader(uint8_t *frame, int len, uint32_t *timing, uint8_t power, uint8_t highspeed) { - uint8_t flags = FPGA_MAJOR_MODE_HF_ISO18092; + uint16_t flags = FPGA_MAJOR_MODE_HF_ISO18092; if (power) flags |= FPGA_HF_ISO18092_FLAG_READER; if (highspeed) diff --git a/armsrc/hfsnoop.c b/armsrc/hfsnoop.c index 177a1f99e..e14be0858 100644 --- a/armsrc/hfsnoop.c +++ b/armsrc/hfsnoop.c @@ -41,12 +41,12 @@ int HfSniff(uint32_t samplesToSkip, uint32_t triggersToSkip, uint16_t *len) { SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SNIFF); // Setting Frame Mode For better performance on high speed data transfer. AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(16); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SNOOP); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SNIFF); SpinDelay(100); *len = (BigBuf_max_traceLen() & 0xFFFE); diff --git a/armsrc/iclass.c b/armsrc/iclass.c index dbf759adf..a42cdf8f1 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -3,6 +3,7 @@ // Hagen Fritsch - June 2010 // Gerhard de Koning Gans - May 2011 // Gerhard de Koning Gans - June 2012 - Added iClass card and reader emulation +// piwi - 2019 // // 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 @@ -10,7 +11,6 @@ //----------------------------------------------------------------------------- // Routines to support iClass. //----------------------------------------------------------------------------- -// Based on ISO14443a implementation. Still in experimental phase. // Contribution made during a security research at Radboud University Nijmegen // // Please feel free to contribute and extend iClass support!! @@ -56,11 +56,6 @@ #include "ticks.h" #include "iso15693.h" -static int g_wait = 290; -static int timeout = 5000; -static uint32_t time_rdr = 0; -static uint32_t time_response = 0; - static int SendIClassAnswer(uint8_t *resp, int respLen, uint16_t delay); int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf); @@ -117,8 +112,9 @@ void SniffIClass(uint8_t jam_search_len, uint8_t *jam_search_string) { } static void rotateCSN(uint8_t *original_csn, uint8_t *rotated_csn) { - for (uint8_t i = 0; i < 8; i++) + for (uint8_t i = 0; i < 8; i++) { rotated_csn[i] = (original_csn[i] >> 3) | (original_csn[(i + 1) % 8] << 5); + } } // Encode SOF only @@ -376,8 +372,6 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } } - int exitLoop = 0; - // Anti-collision process: // Reader 0a // Tag 0f @@ -409,7 +403,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { uint8_t *resp_conf = BigBuf_malloc(28); int resp_conf_len; - // e-Purse + // e-Purse (blk 2) // 18: Takes 2 bytes for SOF/EOF and 8 * 2 = 16 bytes (2 bytes/bit) uint8_t *resp_cc = BigBuf_malloc(28); int resp_cc_len; @@ -420,7 +414,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { uint8_t ff_data[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; AddCrc(ff_data, 8); - // Application Issuer Area + // Application Issuer Area (blk 5) uint8_t *resp_aia = BigBuf_malloc(28); int resp_aia_len; @@ -467,7 +461,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { //This is used for responding to READ-block commands or other data which is dynamically generated //First the 'trace'-data, not encoded for FPGA - uint8_t *data_generic_trace = BigBuf_malloc(32 + 2);//8 bytes data + 2byte CRC is max tag answer + uint8_t *data_generic_trace = BigBuf_malloc(32 + 2); // 32 bytes data + 2byte CRC is max tag answer //Then storage for the modulated data //Each bit is doubled when modulated for FPGA, and we also have SOF and EOF (2 bytes) @@ -479,14 +473,15 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { uint8_t cmd, options, block; int len = 0; - while (exitLoop == false) { + bool exit_loop = 0; + while (exit_loop == false) { WDT_HIT(); uint32_t reader_eof_time = 0; len = GetIso15693CommandFromReader(receivedCmd, MAX_FRAME_SIZE, &reader_eof_time); if (len < 0) { button_pressed = true; - exitLoop = true; + exit_loop = true; continue; } @@ -506,7 +501,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { // Reader in anticollission phase if (chip_state != HALTED) { modulated_response = resp_sof; - modulated_response_size = resp_sof_len; //order = 1; + modulated_response_size = resp_sof_len; chip_state = ACTIVATED; goto send; } @@ -527,19 +522,21 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { if (chip_state == SELECTED) { // block0,1,2,5 is always readable. switch (block) { - case 0: // csn (0c 00) + case 0: { // csn (0c 00) modulated_response = resp_csn; modulated_response_size = resp_csn_len; trace_data = csn_data; trace_data_size = sizeof(csn_data); goto send; - case 1: // configuration (0c 01) + } + case 1: { // configuration (0c 01) modulated_response = resp_conf; modulated_response_size = resp_conf_len; - trace_data = conf_data; - trace_data_size = sizeof(conf_data); + trace_data = conf_block; + trace_data_size = sizeof(conf_block); goto send; - case 2: // e-purse (0c 02) + } + case 2: {// e-purse (0c 02) modulated_response = resp_cc; modulated_response_size = resp_cc_len; trace_data = card_challenge_data; @@ -549,19 +546,22 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { memcpy(reader_mac_buf, card_challenge_data, 8); } goto send; + } case 3: - case 4: // Kd, Kc, always respond with 0xff bytes + case 4: { // Kd, Kc, always respond with 0xff bytes modulated_response = resp_ff; modulated_response_size = resp_ff_len; trace_data = ff_data; trace_data_size = sizeof(ff_data); goto send; - case 5:// Application Issuer Area (0c 05) + } + case 5: { // Application Issuer Area (0c 05) modulated_response = resp_aia; modulated_response_size = resp_aia_len; trace_data = aia_data; trace_data_size = sizeof(aia_data); goto send; + } default : { if (simulationMode == ICLASS_SIM_MODE_FULL) { // 0x0C //Read block @@ -570,7 +570,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { AddCrc(data_generic_trace, 8); trace_data = data_generic_trace; trace_data_size = 10; - CodeIClassTagAnswer(trace_data, trace_data_size); + CodeIso15693AsTag(trace_data, trace_data_size); memcpy(modulated_response, ToSend, ToSendMax); modulated_response_size = ToSendMax; goto send; @@ -588,23 +588,33 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { trace_data = csn_data; trace_data_size = sizeof(csn_data); goto send; + } else if (cmd == ICLASS_CMD_READCHECK) { // 0x88 // Read e-purse KD (88 02) KC (18 02) - modulated_response = resp_cc; - modulated_response_size = resp_cc_len; //order = 4; - trace_data = card_challenge_data; - trace_data_size = sizeof(card_challenge_data); - LED_B_ON(); - goto send; + if (chip_state == SELECTED) { + if ( ICLASS_DEBIT(cmd) ){ + cipher_state = &cipher_state_KD[current_page]; + diversified_key = diversified_kd; + } else { + cipher_state = &cipher_state_KC[current_page]; + diversified_key = diversified_kc; + } + modulated_response = resp_cc; + modulated_response_size = resp_cc_len; + trace_data = card_challenge_data; + trace_data_size = sizeof(card_challenge_data); + goto send; + } + } else if (cmd == ICLASS_CMD_CHECK) { // 0x05 // Reader random and reader MAC!!! if (simulationMode == ICLASS_SIM_MODE_FULL) { // NR, from reader, is in receivedCmd +1 - opt_doTagMAC_2(cipher_state, receivedCmd + 1, data_generic_trace, diversified_key); + opt_doTagMAC_2(*cipher_state, receivedCmd + 1, data_generic_trace, diversified_key); trace_data = data_generic_trace; trace_data_size = 4; - CodeIClassTagAnswer(trace_data, trace_data_size); + CodeIso15693AsTag(trace_data, trace_data_size); memcpy(data_response, ToSend, ToSendMax); modulated_response = data_response; modulated_response_size = ToSendMax; @@ -628,21 +638,24 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { Dbprintf("[+] CSN: %02x .... %02x OK", csn[0], csn[7]); } if (reader_mac_buf != NULL) { + // save NR and MAC for sim 2,4 memcpy(reader_mac_buf + 8, receivedCmd + 1, 8); } - exitLoop = true; + exit_loop = true; } } goto send; + } else if (cmd == ICLASS_CMD_HALT && options == 0 && len == 1) { if (chip_state == SELECTED) { // Reader ends the session modulated_response = resp_sof; - modulated_response_size = resp_sof_Len; + modulated_response_size = resp_sof_len; chip_state = HALTED; goto send; } + } else if (simulationMode == ICLASS_SIM_MODE_FULL && cmd == ICLASS_CMD_READ4 && len == 4) { // 0x06 if (chip_state == SELECTED) { @@ -656,31 +669,86 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { modulated_response_size = ToSendMax; goto send; } - } else if (simulationMode == ICLASS_SIM_MODE_FULL && cmd == ICLASS_CMD_UPDATE) { - //Probably the reader wants to update the nonce. Let's just ignore that for now. - // OBS! If this is implemented, don't forget to regenerate the cipher_state - //We're expected to respond with the data+crc, exactly what's already in the receivedcmd - //receivedcmd is now UPDATE 1b | ADDRESS 1b| DATA 8b| Signature 4b or CRC 2b| + } else if (simulationMode == ICLASS_SIM_MODE_FULL && cmd == ICLASS_CMD_UPDATE && (len == 12 || len == 14)) { - //Take the data... - memcpy(data_generic_trace, receivedCmd + 2, 8); - AddCrc(data_generic_trace, 8); - trace_data = data_generic_trace; - trace_data_size = 10; - CodeIClassTagAnswer(trace_data, trace_data_size); + // We're expected to respond with the data+crc, exactly what's already in the receivedCmd + // receivedCmd is now UPDATE 1b | ADDRESS 1b | DATA 8b | Signature 4b or CRC 2b + if (chip_state == SELECTED) { - memcpy(data_response, ToSend, ToSendMax); - modulated_response = data_response; - modulated_response_size = ToSendMax; -// response_delay = 4600 * 1.5; // tPROG 4-15ms + if (block == 2) { // update e-purse + memcpy(card_challenge_data, receivedCmd + 2, 8); + CodeIso15693AsTag(card_challenge_data, sizeof(card_challenge_data)); + memcpy(resp_cc, ToSend, ToSendMax); + resp_cc_len = ToSendMax; + cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd); + cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kc); + + } else if (block == 3) { // update Kd + for (int i = 0; i < 8; i++) { + if (personalization_mode) { + diversified_kd[i] = receivedCmd[2 + i]; + } else { + diversified_kd[i] ^= receivedCmd[2 + i]; + } + } + cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd); + + } else if (block == 4) { // update Kc + for (int i = 0; i < 8; i++) { + if (personalization_mode) { + diversified_kc[i] = receivedCmd[2 + i]; + } else { + diversified_kc[i] ^= receivedCmd[2 + i]; + } + } + cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kc); + } + + // update emulator + memcpy(emulator + (current_page * page_size) + (8 * block), receivedCmd + 2, 8); + + memcpy(data_generic_trace, receivedCmd + 2, 8); + AddCrc(data_generic_trace, 8); + trace_data = data_generic_trace; + trace_data_size = 10; + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ToSend, ToSendMax); + modulated_response = data_response; + modulated_response_size = ToSendMax; + } goto send; -// } else if(receivedCmd[0] == ICLASS_CMD_PAGESEL) { // 0x84 - //Pagesel - //Pagesel enables to select a page in the selected chip memory and return its configuration block - //Chips with a single page will not answer to this command - // It appears we're fine ignoring this. - //Otherwise, we should answer 8bytes (block) + 2bytes CRC + + } else if (receivedCmd[0] == ICLASS_CMD_PAGESEL && len == 4) { // 0x84 + // Pagesel, + // - enables to select a page in the selected chip memory and return its configuration block + // Chips with a single page will not answer to this command + // Otherwise, we should answer 8bytes (conf block 1) + 2bytes CRC + if (chip_state == SELECTED) { + + if (simulationMode == ICLASS_SIM_MODE_FULL && max_page > 0) { + + current_page = receivedCmd[1]; + + memcpy(data_generic_trace, emulator + (current_page * page_size) + (8 * 1), 8); + memcpy(diversified_kd, emulator + (current_page * page_size) + (8 * 3), 8); + memcpy(diversified_kc, emulator + (current_page * page_size) + (8 * 4), 8); + + cipher_state = &cipher_state_KD[current_page]; + + personalization_mode = data_generic_trace[7] & 0x80; + AddCrc(data_generic_trace, 8); + + trace_data = data_generic_trace; + trace_data_size = 10; + + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ToSend, ToSendMax); + modulated_response = data_response; + modulated_response_size = ToSendMax; + } + } + // } else if(receivedCmd[0] == ICLASS_CMD_DETECT) { // 0x0F } else if (receivedCmd[0] == 0x26 && len == 5) { // standard ISO15693 INVENTORY command. Ignore. @@ -697,7 +765,7 @@ send: if (modulated_response_size > 0) { uint32_t response_time = reader_eof_time + DELAY_ICLASS_VCD_TO_VICC_SIM; TransmitTo15693Reader(modulated_response, modulated_response_size, &response_time, 0, false); - LogTrace(trace_data, trace_data_size, response_time*32, response_time*32 + modulated_response_size*32*64, NULL, false); + LogTrace(trace_data, trace_data_size, response_time * 32, (response_time * 32) + (modulated_response_size * 32 * 64), NULL, false); } } @@ -711,54 +779,6 @@ send: /// THE READER CODE - -//----------------------------------------------------------------------------- -// Transmit the command (to the tag) that was placed in ToSend[]. -//----------------------------------------------------------------------------- -static void TransmitIClassCommand(const uint8_t *cmd, int len, int *wait) { - - int c = 0; - bool firstpart = true; - uint8_t sendbyte; - - time_rdr = 0; - - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); - - AT91C_BASE_SSC->SSC_THR = 0x00; - - // make sure we timeout previous comms. - if (*wait) - SpinDelayUs(*wait); - - for (;;) { - - WDT_HIT(); - - // Put byte into tx holding register as soon as it is ready - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - - // DOUBLE THE SAMPLES! - if (firstpart) { - sendbyte = (cmd[c] & 0xf0) | (cmd[c] >> 4); - } else { - sendbyte = (cmd[c] & 0x0f) | (cmd[c] << 4); - c++; - } - - if (sendbyte == 0xff) - sendbyte = 0xfe; - - AT91C_BASE_SSC->SSC_THR = sendbyte; - firstpart = !firstpart; - - if (c >= len) break; - } - } - - time_rdr = GetCountSspClk(); -} - static void ReaderTransmitIClass(uint8_t *frame, int len, uint32_t *start_time) { CodeIso15693AsReader(frame, len); @@ -800,7 +820,7 @@ static bool selectIclassTag(uint8_t *card_data, bool use_credit_key, uint32_t *e // bit 7: parity. if (use_credit_key) - readcheck_cc[0] = 0x10 | ICLASS_CMD_READCHECK; + read_check_cc[0] = 0x10 | ICLASS_CMD_READCHECK; uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; @@ -850,7 +870,7 @@ static bool selectIclassTag(uint8_t *card_data, bool use_credit_key, uint32_t *e // card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - ReaderTransmitIClass(readcheck_cc, sizeof(readcheck_cc), &start_time); + ReaderTransmitIClass(read_check_cc, sizeof(read_check_cc), &start_time); // expect a 8-byte response here len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); @@ -864,25 +884,24 @@ static bool selectIclassTag(uint8_t *card_data, bool use_credit_key, uint32_t *e // Reader iClass Anticollission // turn off afterwards -void ReaderIClass(uint8_t arg0) { +void ReaderIClass(uint8_t flags) { - uint8_t card_data[6 * 8] = {0}; + uint8_t card_data[6 * 8] = {0xFF}; uint8_t last_csn[8] = {0, 0, 0, 0, 0, 0, 0, 0}; uint8_t resp[ICLASS_BUFFER_SIZE]; - memset(card_data, 0xFF, sizeof(card_data)); +// memset(card_data, 0xFF, sizeof(card_data)); memset(resp, 0xFF, sizeof(resp)); - bool abort_after_read = arg0 & FLAG_ICLASS_READER_ONLY_ONCE; // flag to read until one tag is found successfully - bool try_once = arg0 & FLAG_ICLASS_READER_ONE_TRY; // flag to not to loop continuously, looking for tag - bool use_credit_key = arg0 & FLAG_ICLASS_READER_CEDITKEY; // flag to use credit key - bool flagReadAIA = arg0 & FLAG_ICLASS_READER_AIA; // flag to read block5, application issuer area + bool flag_readonce = flags & FLAG_ICLASS_READER_ONLY_ONCE; // flag to read until one tag is found successfully + bool use_credit_key = flags & FLAG_ICLASS_READER_CEDITKEY; // flag to use credit key + bool flag_read_aia = flags & FLAG_ICLASS_READER_AIA; // flag to read block5, application issuer area - if (flags & FLAG_ICLASS_READER_INIT) { + if ((flags & FLAG_ICLASS_READER_INIT) == FLAG_ICLASS_READER_INIT) { Iso15693InitReader(); } - if (flags & FLAG_ICLASS_READER_CLEARTRACE) { + if ((flags & FLAG_ICLASS_READER_CLEARTRACE) == FLAG_ICLASS_READER_CLEARTRACE) { set_tracing(true); clear_trace(); StartCountSspClk(); @@ -890,7 +909,6 @@ void ReaderIClass(uint8_t arg0) { uint32_t start_time = 0; uint32_t eof_time = 0; - int read_status = selectIclassTag(card_data, use_credit_key, &eof_time); if (read_status == 0) { reply_mix(CMD_ACK, 0xFF, 0, 0, card_data, 0); @@ -898,18 +916,18 @@ void ReaderIClass(uint8_t arg0) { return; } - uint8_t result_status = FLAG_ICLASS_READER_CSN | FLAG_ICLASS_READER_CONF | FLAG_ICLASS_READER_CC; + uint8_t result_status = FLAG_ICLASS_CSN | FLAG_ICLASS_CONF | FLAG_ICLASS_CC; //Read block 5, AIA - if (flagReadAIA) { + if (flag_read_aia) { //Read App Issuer Area block CRC(0x05) => 0xde 0x64 - uint8_t readAA[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x05, 0xde, 0x64}; + uint8_t read_aa[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x05, 0xde, 0x64}; - if (sendCmdGetResponseWithRetries(readAA, sizeof(readAA), resp, sizeof(resp), 10, 10, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time)) { - result_status |= FLAG_ICLASS_READER_AIA; + if (sendCmdGetResponseWithRetries(read_aa, sizeof(read_aa), resp, sizeof(resp), 10, 10, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time)) { + result_status |= FLAG_ICLASS_AIA; memcpy(card_data + (8 * 5), resp, 8); } else { - if (DBGLEVEL >= DBG_EXTENDED) DbpString("Failed to dump AA block"); + if (DBGLEVEL >= DBG_EXTENDED) DbpString("Failed to dump AIA block"); } } @@ -929,12 +947,10 @@ void ReaderIClass(uint8_t arg0) { // only useful if looping in arm (not try_once && not abort_after_read) if (memcmp(last_csn, card_data, 8) != 0) { - if (send) { - reply_mix(CMD_ACK, result_status, 0, 0, card_data, sizeof(card_data)); - if (abort_after_read) { - LED_B_OFF(); - return; - } + reply_mix(CMD_ACK, result_status, 0, 0, card_data, sizeof(card_data)); + if (flag_readonce) { + LED_B_OFF(); + return; } LED_B_OFF(); } @@ -1269,12 +1285,12 @@ static bool iClass_WriteBlock_ext(uint8_t blockno, uint8_t *data) { } uint8_t all_ff[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - if (blockNo == 2) { + if (blockno == 2) { // check response. e-purse update swaps first and second half if (memcmp(data+4, resp, 4) || memcmp(data, resp+4, 4)) { return false; } - } else if (blockNo == 3 || blockNo == 4) { + } else if (blockno == 3 || blockno == 4) { // check response. Key updates always return 0xffffffffffffffff if (memcmp(all_ff, resp, 8)) { return false; diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 639519c30..21ad93997 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -1101,7 +1101,7 @@ static void Calc_wb_mac(uint8_t blockno, uint8_t *data, uint8_t *div_key, uint8_ static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool use_credit_key, bool verbose) { PacketResponseNG resp; - uint8_t flags = FLAG_ICLASS_READER_ONLY_ONCE | FLAG_ICLASS_READER_CC | FLAG_ICLASS_READER_ONE_TRY; + uint8_t flags = FLAG_ICLASS_READER_ONLY_ONCE; if (use_credit_key) flags |= FLAG_ICLASS_READER_CEDITKEY; @@ -1279,9 +1279,12 @@ static int CmdHFiClassReader_Dump(const char *Cmd) { // if no debit key given try credit key on AA1 (not for iclass but for some picopass this will work) if (!have_debit_key && have_credit_key) use_credit_key = true; - uint32_t flags = FLAG_ICLASS_READER_CSN | FLAG_ICLASS_READER_CC | - FLAG_ICLASS_READER_CONF | FLAG_ICLASS_READER_ONLY_ONCE | - FLAG_ICLASS_READER_ONE_TRY; + uint32_t flags = ( + FLAG_ICLASS_READER_INIT | + FLAG_ICLASS_READER_CLEARTRACE | + FLAG_ICLASS_READER_ONLY_ONCE + ); + //get config and first 3 blocks PacketResponseNG resp; @@ -1305,7 +1308,7 @@ static int CmdHFiClassReader_Dump(const char *Cmd) { return PM3_ESOFT; } - if (readStatus & (FLAG_ICLASS_READER_CSN | FLAG_ICLASS_READER_CONF | FLAG_ICLASS_READER_CC)) { + if (readStatus & (FLAG_ICLASS_CSN | FLAG_ICLASS_CONF | FLAG_ICLASS_CC)) { memcpy(tag_data, data, 8 * 3); blockno += 2; // 2 to force re-read of block 2 later. (seems to respond differently..) numblks = data[8]; @@ -2927,9 +2930,12 @@ int CmdHFiClass(const char *Cmd) { int readIclass(bool loop, bool verbose) { bool tagFound = false; - uint32_t flags = FLAG_ICLASS_READER_CSN | FLAG_ICLASS_READER_CC | FLAG_ICLASS_READER_AIA | - FLAG_ICLASS_READER_CONF | FLAG_ICLASS_READER_ONLY_ONCE | - FLAG_ICLASS_READER_ONE_TRY; + uint32_t flags = ( + FLAG_ICLASS_READER_INIT | + FLAG_ICLASS_READER_CLEARTRACE | + FLAG_ICLASS_READER_ONLY_ONCE | + FLAG_ICLASS_READER_AIA + ); uint32_t res = PM3_ETIMEOUT; // loop in client not device - else on windows have a communication error @@ -2960,35 +2966,35 @@ int readIclass(bool loop, bool verbose) { PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " --------------------------"); PrintAndLogEx(INFO, "-------------------------------------------------------------"); - if (readStatus & FLAG_ICLASS_READER_CSN) { + if (readStatus & FLAG_ICLASS_CSN) { PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s") " (uid)", sprint_hex(hdr->csn, sizeof(hdr->csn))); tagFound = true; } - if (readStatus & FLAG_ICLASS_READER_CONF) { + if (readStatus & FLAG_ICLASS_CONF) { PrintAndLogEx(SUCCESS, " Config: %s (Card configuration)", sprint_hex((uint8_t *)&hdr->conf, sizeof(hdr->conf))); } - if (readStatus & FLAG_ICLASS_READER_CC) { + if (readStatus & FLAG_ICLASS_CC) { PrintAndLogEx(SUCCESS, "E-purse: %s (Card challenge, CC)", sprint_hex(hdr->epurse, sizeof(hdr->epurse))); } PrintAndLogEx(SUCCESS, " Kd: %s (Debit key, hidden)", sprint_hex(hdr->key_d, sizeof(hdr->key_d))); PrintAndLogEx(SUCCESS, " Kc: %s (Credit key, hidden)", sprint_hex(hdr->key_c, sizeof(hdr->key_c))); - if (readStatus & FLAG_ICLASS_READER_AIA) { + if (readStatus & FLAG_ICLASS_AIA) { // PrintAndLogEx(INFO, "--------- " _CYAN_("AIA") " ---------"); PrintAndLogEx(SUCCESS, " AIA: %s (Application Issuer area)", sprint_hex(hdr->app_issuer_area, sizeof(hdr->app_issuer_area))); } - if (readStatus & FLAG_ICLASS_READER_CONF) { + if (readStatus & FLAG_ICLASS_CONF) { printIclassDumpInfo(data); } // if CSN ends with FF12E0, it's inside HID CSN range. bool isHidRange = (memcmp((uint8_t *)(data + 5), "\xFF\x12\xE0", 3) == 0); - if (readStatus & FLAG_ICLASS_READER_AIA) { + if (readStatus & FLAG_ICLASS_AIA) { bool legacy = (memcmp((uint8_t *)(data + 8 * 5), "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); bool se_enabled = (memcmp((uint8_t *)(data + 8 * 5), "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 0d24d5bae..bf16aaca6 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -579,14 +579,18 @@ typedef struct { #define FLAG_FORCED_ATQA 0x800 #define FLAG_FORCED_SAK 0x1000 -//Iclass reader flags -#define FLAG_ICLASS_READER_ONLY_ONCE 0x01 -#define FLAG_ICLASS_READER_CC 0x02 -#define FLAG_ICLASS_READER_CSN 0x04 -#define FLAG_ICLASS_READER_CONF 0x08 +// iCLASS reader flags +#define FLAG_ICLASS_READER_INIT 0x01 +#define FLAG_ICLASS_READER_CLEARTRACE 0x02 +#define FLAG_ICLASS_READER_ONLY_ONCE 0x04 +#define FLAG_ICLASS_READER_CEDITKEY 0x08 #define FLAG_ICLASS_READER_AIA 0x10 -#define FLAG_ICLASS_READER_ONE_TRY 0x20 -#define FLAG_ICLASS_READER_CEDITKEY 0x40 + +// iCLASS reader status flags +#define FLAG_ICLASS_CSN 0x01 +#define FLAG_ICLASS_CC 0x02 +#define FLAG_ICLASS_CONF 0x04 +#define FLAG_ICLASS_AIA 0x08 // iCLASS simulation modes #define ICLASS_SIM_MODE_CSN 0 diff --git a/include/protocols.h b/include/protocols.h index 682f57eff..18e580b54 100644 --- a/include/protocols.h +++ b/include/protocols.h @@ -135,7 +135,7 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define ICLASS_CMD_ACT 0xE #define ICLASS_CREDIT(x) (((x) & 0x10) == 0x10) -#define ICLASS_DEBIT(x) !(ICLASS_CREDIT(x)) +#define ICLASS_DEBIT(x) (((x) & 0x80) == 0x80) #define ISO14443A_CMD_REQA 0x26 From 6948ccff3af42a47a02da7935a6d9f36fcb37d0a Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 6 Jul 2020 22:33:38 +0200 Subject: [PATCH 022/139] fpga iclass... --- armsrc/iclass.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index a42cdf8f1..e02975041 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -922,7 +922,6 @@ void ReaderIClass(uint8_t flags) { if (flag_read_aia) { //Read App Issuer Area block CRC(0x05) => 0xde 0x64 uint8_t read_aa[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x05, 0xde, 0x64}; - if (sendCmdGetResponseWithRetries(read_aa, sizeof(read_aa), resp, sizeof(resp), 10, 10, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time)) { result_status |= FLAG_ICLASS_AIA; memcpy(card_data + (8 * 5), resp, 8); @@ -993,7 +992,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { //for now replay captured auth (as cc not updated) memcpy(check + 5, mac, 4); - if (!sendCmdGetResponseWithRetries(check, sizeof(check), resp, 4, 5)) { + if (sendCmdGetResponseWithRetries(check, sizeof(check), resp, sizeof(resp), 4, 3, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time) == false) { DbpString("Error: Authentication Fail!"); continue; } @@ -1002,7 +1001,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { read[1] = 1; AddCrc(read + 1, 1); - if (!sendCmdGetResponseWithRetries(read, sizeof(read), resp, 10, 5)) { + if (sendCmdGetResponseWithRetries(read, sizeof(read), resp, sizeof(resp), 10, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time) == false) { DbpString("Dump config (block 1) failed"); continue; } @@ -1028,7 +1027,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { read[1] = block; AddCrc(read + 1, 1); - if (sendCmdGetResponseWithRetries(read, sizeof(read), resp, 10, 5)) { + if (sendCmdGetResponseWithRetries(read, sizeof(read), resp, sizeof(resp), 10, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time)) { Dbprintf(" %02x: %02x %02x %02x %02x %02x %02x %02x %02x", block, resp[0], resp[1], resp[2], resp[3], resp[4], resp[5], @@ -1038,6 +1037,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { //Fill up the buffer memcpy(card_data + stored_data_length, resp, 8); stored_data_length += 8; + if (stored_data_length + 8 > PM3_CMD_DATA_SIZE) { //Time to send this off and start afresh reply_old(CMD_ACK, @@ -1086,7 +1086,8 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { // turn off afterwards void iClass_ReadCheck(uint8_t blockno, uint8_t keytype) { uint8_t readcheck[] = { keytype, blockno }; - uint8_t resp[] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t resp[8] = {0}; + uint32_t eof_time; bool isOK = sendCmdGetResponseWithRetries(readcheck, sizeof(readcheck), resp, sizeof(resp), 8, 3, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); reply_mix(CMD_ACK, isOK, 0, 0, 0, 0); switch_off(); @@ -1104,9 +1105,8 @@ void iClass_Authentication(uint8_t *mac) { check[7] = mac[2]; check[8] = mac[3]; //memcpy(check+5, mac, 4); - - // 6 retries - uint8_t isOK = sendCmdGetResponseWithRetries(check, sizeof(check), resp, 4, 6); + uint32_t eof_time; + bool isOK = sendCmdGetResponseWithRetries(check, sizeof(check), resp, sizeof(resp), 4, 3, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); reply_ng(CMD_HF_ICLASS_AUTH, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); } @@ -1187,7 +1187,7 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { check[8] = keys[i].mac[3]; // expect 4bytes, 3 retries times.. - isOK = sendCmdGetResponseWithRetries(check, sizeof(check), resp, 4, 3); + isOK = sendCmdGetResponseWithRetries(check, sizeof(check), resp, sizeof(resp), 4, 3, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); if (isOK) goto out; From 7c15e4b7ff73dce83292858bdf0ee995de2927d9 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 6 Jul 2020 22:43:11 +0200 Subject: [PATCH 023/139] fpga more iclass --- armsrc/iclass.c | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index e02975041..02b73a2ba 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -971,6 +971,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { uint8_t read[] = { 0x0c, 0x00, 0x00, 0x00 }; uint8_t card_data[PM3_CMD_DATA_SIZE] = {0}; uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; + uint32_t eof_time = 0; static struct memory_t { int k16; @@ -980,8 +981,6 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { int keyaccess; } memory; - setupIclassReader(); - while (!BUTTON_PRESS()) { WDT_HIT(); @@ -993,7 +992,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { memcpy(check + 5, mac, 4); if (sendCmdGetResponseWithRetries(check, sizeof(check), resp, sizeof(resp), 4, 3, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time) == false) { - DbpString("Error: Authentication Fail!"); + if (DBGLEVEL >= DBG_EXTENDED) DbpString("Error: Authentication Fail!"); continue; } @@ -1002,22 +1001,24 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { AddCrc(read + 1, 1); if (sendCmdGetResponseWithRetries(read, sizeof(read), resp, sizeof(resp), 10, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time) == false) { - DbpString("Dump config (block 1) failed"); + if (DBGLEVEL >= DBG_EXTENDED) DbpString("Dump config (block 1) failed"); continue; } mem = resp[5]; - memory.k16 = (mem & 0x80); - memory.book = (mem & 0x20); - memory.k2 = (mem & 0x8); - memory.lockauth = (mem & 0x2); - memory.keyaccess = (mem & 0x1); + memory.k16 = ((mem & 0x80) == 0x80); + memory.book = ((mem & 0x20) == 0x20); + memory.k2 = ((mem & 0x08) == 0x08); + memory.lockauth = ((mem & 0x02) == 0x02); + memory.keyaccess = ((mem & 0x01) == 0x01); cardsize = memory.k16 ? 255 : 32; WDT_HIT(); - //Set card_data to all zeroes, we'll fill it with data - memset(card_data, 0x0, PM3_CMD_DATA_SIZE); + + // set card_data to 0xFF... + memset(card_data, 0xFF, PM3_CMD_DATA_SIZE); + uint8_t failedRead = 0; uint32_t stored_data_length = 0; @@ -1028,11 +1029,13 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { AddCrc(read + 1, 1); if (sendCmdGetResponseWithRetries(read, sizeof(read), resp, sizeof(resp), 10, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time)) { - Dbprintf(" %02x: %02x %02x %02x %02x %02x %02x %02x %02x", - block, resp[0], resp[1], resp[2], - resp[3], resp[4], resp[5], - resp[6], resp[7] - ); + if (DBGLEVEL >= DBG_EXTENDED) { + Dbprintf(" %02x: %02x %02x %02x %02x %02x %02x %02x %02x", + block, + resp[0], resp[1], resp[2], resp[3], + resp[4], resp[5], resp[6], resp[7] + ); + } //Fill up the buffer memcpy(card_data + stored_data_length, resp, 8); @@ -1054,7 +1057,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { } else { failedRead = 1; stored_data_length += 8;//Otherwise, data becomes misaligned - Dbprintf("Failed to dump block %d", block); + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("Failed to dump block %d", block); } } @@ -1087,7 +1090,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { void iClass_ReadCheck(uint8_t blockno, uint8_t keytype) { uint8_t readcheck[] = { keytype, blockno }; uint8_t resp[8] = {0}; - uint32_t eof_time; + uint32_t eof_time = 0; bool isOK = sendCmdGetResponseWithRetries(readcheck, sizeof(readcheck), resp, sizeof(resp), 8, 3, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); reply_mix(CMD_ACK, isOK, 0, 0, 0, 0); switch_off(); @@ -1122,9 +1125,12 @@ typedef struct iclass_premac { * to cover debit and credit key. (AA1/AA2) */ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { + uint8_t i = 0, isOK = 0; + uint8_t lastChunk = ((arg0 >> 8) & 0xFF); bool use_credit_key = ((arg0 >> 16) & 0xFF); + uint8_t keyCount = arg1 & 0xFF; uint8_t check[] = { ICLASS_CMD_CHECK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t resp[ICLASS_BUFFER_SIZE]; @@ -1143,11 +1149,10 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { switch_off(); SpinDelay(20); - setupIclassReader(); - uint16_t checked = 0; int read_status = 0; uint8_t startup_limit = 10; + uint32_t eof_time = 0; while (read_status != 2) { if (checked == 1000) { From 2502d4581b20ff79ebf2054c49ab010527d8e6b7 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 7 Jul 2020 12:32:56 +0200 Subject: [PATCH 024/139] iclass merges --- armsrc/iclass.c | 64 +++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 02b73a2ba..0fb0a4f53 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -954,12 +954,13 @@ void ReaderIClass(uint8_t flags) { LED_B_OFF(); } - if (userCancelled) { - reply_mix(CMD_ACK, 0xFF, 0, 0, card_data, 0); - switch_off(); - } else { +// if (userCancelled) { +// reply_mix(CMD_ACK, 0xFF, 0, 0, card_data, 0); +// switch_off(); +// } else { reply_mix(CMD_ACK, 0, 0, 0, card_data, 0); - } +// } + switch_off(); } // turn off afterwards @@ -971,6 +972,8 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { uint8_t read[] = { 0x0c, 0x00, 0x00, 0x00 }; uint8_t card_data[PM3_CMD_DATA_SIZE] = {0}; uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; + + uint32_t start_time = 0; uint32_t eof_time = 0; static struct memory_t { @@ -1108,7 +1111,7 @@ void iClass_Authentication(uint8_t *mac) { check[7] = mac[2]; check[8] = mac[3]; //memcpy(check+5, mac, 4); - uint32_t eof_time; + uint32_t eof_time = 0; bool isOK = sendCmdGetResponseWithRetries(check, sizeof(check), resp, sizeof(resp), 4, 3, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); reply_ng(CMD_HF_ICLASS_AUTH, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); } @@ -1220,10 +1223,10 @@ out: // retries 10times. static bool iClass_ReadBlock(uint8_t blockno, uint8_t *data) { uint8_t resp[10]; - uint8_t cmd[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00}; - AddCrc(cmd + 1, 1); - uint32_t eof_time; - bool isOK = sendCmdGetResponseWithRetries(readcmd, sizeof(readcmd), resp, sizeof(resp), 10, 10, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + uint8_t c[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00}; + AddCrc(c + 1, 1); + uint32_t eof_time = 0; + bool isOK = sendCmdGetResponseWithRetries(c, sizeof(c), resp, sizeof(resp), 10, 10, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); memcpy(data, resp, 8); return isOK; } @@ -1241,44 +1244,49 @@ void iClass_ReadBlk(uint8_t blockno) { switch_off(); reply_ng(CMD_HF_ICLASS_READBL, PM3_SUCCESS, (uint8_t *)&result, sizeof(result)); } + +// Dump command seems to dump a block related portion of card memory. +// I suppose it will need to do an authentatication to AA1, read its blocks by calling this. +// then authenticate AA2, and read those blocks by calling this. +// By the looks at it only 2K cards is supported, or first page dumps on larger cards. +// turn off afterwards +void iClass_Dump(uint8_t start_blockno, uint8_t numblks) { -// turn off afterwards -void iClass_Dump(uint8_t blockno, uint8_t numblks) { - BigBuf_free(); - uint8_t *dataout = BigBuf_malloc(0xFF * 8); + BigBuf_free(); + + uint8_t *dataout = BigBuf_malloc(0xFF * 8); if (dataout == NULL) { - DbpString("[!] fail to allocate memory"); + DbpString("fail to allocate memory"); OnError(1); return; } memset(dataout, 0xFF, 0xFF * 8); - uint8_t blockdata[8] = {0}; - uint8_t blkCnt = 0; bool isOK; - for (; blkCnt < numblks; blkCnt++) { - isOK = iClass_ReadBlock(blockno + blkCnt, blockdata); + uint8_t blkcnt = 0; + for (; blkcnt < numblks; blkcnt++) { + isOK = iClass_ReadBlock(start_blockno + blkcnt, dataout + (8 * blkcnt)); - // 0xBB is the internal debug separator byte.. - if (!isOK || (blockdata[0] == 0xBB || blockdata[7] == 0xBB || blockdata[2] == 0xBB)) { //try again - isOK = iClass_ReadBlock(blockno + blkCnt, blockdata); + if (!isOK) { + isOK = iClass_ReadBlock(start_blockno + blkcnt, dataout + (8 * blkcnt)); if (!isOK) { - Dbprintf("[!] block %02X failed to read", blkCnt + blockno); + Dbprintf("failed to read block %02X", start_blockno + blkcnt); break; } } - memcpy(dataout + (blkCnt * 8), blockdata, 8); } switch_off(); - //return pointer to dump memory in arg3 - reply_mix(CMD_ACK, isOK, blkCnt, BigBuf_max_traceLen(), 0, 0); + + // return pointer to dump memory in arg3 + // iceman: why not return | dataout - getbigbuf ? Should give exact location. + reply_mix(CMD_ACK, isOK, blkcnt, BigBuf_max_traceLen(), 0, 0); BigBuf_free(); } static bool iClass_WriteBlock_ext(uint8_t blockno, uint8_t *data) { - uint8_t write[] = { 0x80 | ICLASS_CMD_UPDATE, blockno, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t write[16] = { 0x80 | ICLASS_CMD_UPDATE, blockno }; memcpy(write + 2, data, 12); // data + mac AddCrc(write + 1, 13); @@ -1292,7 +1300,7 @@ static bool iClass_WriteBlock_ext(uint8_t blockno, uint8_t *data) { uint8_t all_ff[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; if (blockno == 2) { // check response. e-purse update swaps first and second half - if (memcmp(data+4, resp, 4) || memcmp(data, resp+4, 4)) { + if (memcmp(data + 4, resp, 4) || memcmp(data, resp + 4, 4)) { return false; } } else if (blockno == 3 || blockno == 4) { From b775b68e4e9a7603169dd55dd6dd86b2bbf59576 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 7 Jul 2020 13:18:53 +0200 Subject: [PATCH 025/139] fpga iclass --- armsrc/appmain.c | 13 ++++++++++-- armsrc/felica.c | 2 +- armsrc/hfsnoop.c | 2 +- armsrc/iclass.c | 53 ++++++++++++++++++++---------------------------- armsrc/iclass.h | 2 ++ 5 files changed, 37 insertions(+), 35 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index a252bbe8e..963d36c78 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1048,7 +1048,11 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_ISO15693_SIMULATE: { - SimTagIso15693(packet->oldarg[0], packet->data.asBytes); + struct p { + uint8_t uid[10]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + SimTagIso15693(payload->uid); break; } #endif @@ -1376,7 +1380,12 @@ static void PacketReceived(PacketCommandNG *packet) { #ifdef WITH_ICLASS // Makes use of ISO14443a FPGA Firmware case CMD_HF_ICLASS_SNIFF: { - SniffIClass(); + struct p { + uint8_t jam_search_len; + uint8_t jam_search_string[]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + SniffIClass(payload->jam_search_len, payload->jam_search_string); break; } case CMD_HF_ICLASS_SIMULATE: { diff --git a/armsrc/felica.c b/armsrc/felica.c index d61719c33..944b89ffe 100644 --- a/armsrc/felica.c +++ b/armsrc/felica.c @@ -464,7 +464,7 @@ static void iso18092_setup(uint8_t fpga_minor_mode) { SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO18092); // LSB transfer. Remember to set it back to MSB with AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); diff --git a/armsrc/hfsnoop.c b/armsrc/hfsnoop.c index e14be0858..1b87d857a 100644 --- a/armsrc/hfsnoop.c +++ b/armsrc/hfsnoop.c @@ -119,7 +119,7 @@ void HfPlotDownload(void) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_GET_TRACE); AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; // Disable DMA Transfer AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) this_buf; // start transfer to this memory address diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 0fb0a4f53..99976566c 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -56,9 +56,6 @@ #include "ticks.h" #include "iso15693.h" -static int SendIClassAnswer(uint8_t *resp, int respLen, uint16_t delay); -int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf); - // The length of a received command will in most cases be no more than 18 bytes. // we expect max 34 bytes as tag answer (response to READ4) #ifndef ICLASS_BUFFER_SIZE @@ -808,7 +805,7 @@ static bool sendCmdGetResponseWithRetries(uint8_t* command, size_t cmdsize, uint * @return false = fail * true = Got all. */ -static bool selectIclassTag(uint8_t *card_data, bool use_credit_key, uint32_t *eof_time) { +static bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t *eof_time) { static uint8_t act_all[] = { ICLASS_CMD_ACTALL }; static uint8_t identify[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x00, 0x73, 0x33 }; @@ -909,7 +906,7 @@ void ReaderIClass(uint8_t flags) { uint32_t start_time = 0; uint32_t eof_time = 0; - int read_status = selectIclassTag(card_data, use_credit_key, &eof_time); + int read_status = select_iclass_tag(card_data, use_credit_key, &eof_time); if (read_status == 0) { reply_mix(CMD_ACK, 0xFF, 0, 0, card_data, 0); switch_off(); @@ -973,8 +970,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { uint8_t card_data[PM3_CMD_DATA_SIZE] = {0}; uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; - uint32_t start_time = 0; - uint32_t eof_time = 0; + bool use_credit_key = false; static struct memory_t { int k16; @@ -984,12 +980,14 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { int keyaccess; } memory; - while (!BUTTON_PRESS()) { + uint32_t start_time = 0; + uint32_t eof_time = 0; + while (BUTTON_PRESS() == false) { WDT_HIT(); - uint8_t read_status = handshakeIclassTag(card_data); - if (read_status < 2) continue; + bool read_status = select_iclass_tag(card_data, use_credit_key, &eof_time); + if (read_status == false) continue; //for now replay captured auth (as cc not updated) memcpy(check + 5, mac, 4); @@ -1130,7 +1128,6 @@ typedef struct iclass_premac { void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { uint8_t i = 0, isOK = 0; - uint8_t lastChunk = ((arg0 >> 8) & 0xFF); bool use_credit_key = ((arg0 >> 16) & 0xFF); @@ -1152,28 +1149,21 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { switch_off(); SpinDelay(20); - uint16_t checked = 0; - int read_status = 0; - uint8_t startup_limit = 10; + + bool read_status = false; + uint32_t start_time = 0; uint32_t eof_time = 0; - while (read_status != 2) { + uint8_t tries = 10; + while (tries-- > 0 || read_status == false) { + read_status = select_iclass_tag(card_data, use_credit_key, &eof_time); + } - if (checked == 1000) { - if (BUTTON_PRESS() || !data_available()) goto out; - checked = 0; - } - ++checked; + // failed to select card 10 times. return fail to client + if (read_status == false) + goto out; - read_status = handshakeIclassTag_ext(card_data, use_credit_key); - if (startup_limit-- == 0) { - Dbprintf("[-] Handshake status | %d (fail 10)", read_status); - isOK = 99; - goto out; - } - }; - // since handshakeIclassTag_ext call sends s readcheck, we start with sending first response. - - checked = 0; + // since select_iclass_tag call sends s readcheck, we start with sending first response. + uint16_t checked = 0; // Keychunk loop for (i = 0; i < keyCount; i++) { @@ -1199,9 +1189,10 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { if (isOK) goto out; + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; // Auth Sequence MUST begin with reading e-purse. (block2) // Card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) - ReaderTransmitIClass(readcheck_cc, sizeof(readcheck_cc)); + ReaderTransmitIClass(readcheck_cc, sizeof(readcheck_cc), &start_time); LED_B_OFF(); } diff --git a/armsrc/iclass.h b/armsrc/iclass.h index 6ce23ceb7..20a862535 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -25,4 +25,6 @@ void iClass_Dump(uint8_t blockno, uint8_t numblks); void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data); void iClass_ReadCheck(uint8_t blockno, uint8_t keytype); + +int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf); #endif From ce24e6acbbc754a4c56a26dc4920f34199356b9b Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 8 Jul 2020 09:45:49 +0200 Subject: [PATCH 026/139] fpga merge hell, all compiles --- armsrc/appmain.c | 9 +++++++-- armsrc/iso15693.h | 2 +- client/src/cmdhf15.c | 7 +++---- include/pm3_cmd.h | 2 +- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 963d36c78..2ed5811fa 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1031,8 +1031,13 @@ static void PacketReceived(PacketCommandNG *packet) { AcquireRawAdcSamplesIso15693(); break; } - case CMD_HF_ISO15693_RAWADC: { - RecordRawAdcSamplesIso15693(); + case CMD_HF_ISO15693_SNIFF: { + struct p { + uint8_t jam_search_len; + uint8_t jam_search_string[]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + SniffIso15693(payload->jam_search_len, payload->jam_search_string); break; } case CMD_HF_ISO15693_COMMAND: { diff --git a/armsrc/iso15693.h b/armsrc/iso15693.h index 1b5405b13..fcca9491c 100644 --- a/armsrc/iso15693.h +++ b/armsrc/iso15693.h @@ -33,7 +33,7 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time); int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeout, uint32_t *eof_time); -void RecordRawAdcSamplesIso15693(void); +//void RecordRawAdcSamplesIso15693(void); void AcquireRawAdcSamplesIso15693(void); void ReaderIso15693(uint32_t parameter); // Simulate an ISO15693 reader - greg void SimTagIso15693(uint8_t *uid); // simulate an ISO15693 tag - greg diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index 2b8812d33..ad69d5615 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -973,12 +973,12 @@ static int CmdHF15Info(const char *Cmd) { // Record Activity without enabling carrier //helptext -static int CmdHF15Record(const char *Cmd) { +static int CmdHF15Sniff(const char *Cmd) { char cmdp = tolower(param_getchar(Cmd, 0)); if (cmdp == 'h') return usage_15_record(); clearCommandBuffer(); - SendCommandNG(CMD_HF_ISO15693_RAWADC, NULL, 0); + SendCommandNG(CMD_HF_ISO15693_SNIFF, NULL, 0); return PM3_SUCCESS; } @@ -1841,9 +1841,8 @@ static command_t CommandTable[] = { {"demod", CmdHF15Demod, AlwaysAvailable, "Demodulate ISO15693 from tag"}, {"dump", CmdHF15Dump, IfPm3Iso15693, "Read all memory pages of an ISO15693 tag, save to file"}, {"info", CmdHF15Info, IfPm3Iso15693, "Tag information"}, -// {"sniff", CmdHF15Sniff, IfPm3Iso15693, "Sniff ISO15693 traffic"}, + {"sniff", CmdHF15Sniff, IfPm3Iso15693, "Sniff ISO15693 traffic"}, {"raw", CmdHF15Raw, IfPm3Iso15693, "Send raw hex data to tag"}, - {"record", CmdHF15Record, IfPm3Iso15693, "Record Samples (ISO15693)"}, {"read", CmdHF15Read, IfPm3Iso15693, "Read a block"}, {"reader", CmdHF15Reader, IfPm3Iso15693, "Act like an ISO15693 reader"}, {"readmulti", CmdHF15Readmulti, IfPm3Iso15693, "Reads multiple Blocks"}, diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index bf16aaca6..492eca798 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -431,7 +431,7 @@ typedef struct { #define CMD_HF_ISO14443B_COMMAND 0x0305 #define CMD_HF_ISO15693_READER 0x0310 #define CMD_HF_ISO15693_SIMULATE 0x0311 -#define CMD_HF_ISO15693_RAWADC 0x0312 +#define CMD_HF_ISO15693_SNIFF 0x0312 #define CMD_HF_ISO15693_COMMAND 0x0313 #define CMD_HF_ISO15693_FINDAFI 0x0315 #define CMD_LF_SNIFF_RAW_ADC 0x0317 From 0d4c537eded79faa9e46b2efa6910ad7be8ae519 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 8 Jul 2020 11:05:04 +0200 Subject: [PATCH 027/139] version info for FeliCa fpga image --- tools/fpga_compress/fpga_compress.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/fpga_compress/fpga_compress.c b/tools/fpga_compress/fpga_compress.c index b03caabf4..5da59f2db 100644 --- a/tools/fpga_compress/fpga_compress.c +++ b/tools/fpga_compress/fpga_compress.c @@ -50,10 +50,13 @@ static int zlib_compress(FILE *infile[], uint8_t num_infiles, FILE *outfile) { if (total_size >= num_infiles * FPGA_CONFIG_SIZE) { fprintf(stderr, "Input files too big (total > %li bytes). These are probably not PM3 FPGA config files.\n" - , num_infiles * FPGA_CONFIG_SIZE); + , num_infiles * FPGA_CONFIG_SIZE + ); + for (uint16_t j = 0; j < num_infiles; j++) { fclose(infile[j]); } + free(fpga_config); return (EXIT_FAILURE); } @@ -188,8 +191,10 @@ static int zlib_decompress(FILE *infile, FILE *outfile) { * length. */ static int bitparse_find_section(FILE *infile, char section_name, unsigned int *section_length) { - int result = 0; + #define MAX_FPGA_BIT_STREAM_HEADER_SEARCH 100 // maximum number of bytes to search for the requested section + + int result = 0; uint16_t numbytes = 0; while (numbytes < MAX_FPGA_BIT_STREAM_HEADER_SEARCH) { char current_name = (char)fgetc(infile); @@ -252,6 +257,8 @@ static int FpgaGatherVersion(FILE *infile, char *infile_name, char *dst, int len strncat(dst, "LF", len - strlen(dst) - 1); else if (!memcmp("fpga_hf", basename(infile_name), 7)) strncat(dst, "HF", len - strlen(dst) - 1); + else if (!memcmp("fpga_felica", basename(infile_name), 7)) + strncat(dst, "HF FeliCa", len - strlen(dst) - 1); strncat(dst, " image built", len - strlen(dst) - 1); if (bitparse_find_section(infile, 'b', &fpga_info_len)) { From 2a30f3b1657bde7bbeeb6695f66d8908d13dd513 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 8 Jul 2020 11:05:54 +0200 Subject: [PATCH 028/139] fgpa felica --- armsrc/fpgaloader.c | 4 +++- armsrc/fpgaloader.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index 8313404bf..f76d1a189 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -351,8 +351,10 @@ static void DownloadFPGA(int bitstream_version, int FpgaImageLen, lz4_streamp co * length. */ static int bitparse_find_section(int bitstream_version, char section_name, uint32_t *section_length, lz4_streamp compressed_fpga_stream, uint8_t *output_buffer) { - int result = 0; + #define MAX_FPGA_BIT_STREAM_HEADER_SEARCH 100 // maximum number of bytes to search for the requested section + + int result = 0; uint16_t numbytes = 0; while (numbytes < MAX_FPGA_BIT_STREAM_HEADER_SEARCH) { char current_name = get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer); diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h index 17bd8bbc2..530dc21b4 100644 --- a/armsrc/fpgaloader.h +++ b/armsrc/fpgaloader.h @@ -20,6 +20,7 @@ // definitions for multiple FPGA config files support #define FPGA_BITSTREAM_LF 1 #define FPGA_BITSTREAM_HF 2 +#define FPGA_BITSTREAM_HF_FELICA 3 /* Communication between ARM / FPGA is done inside armsrc/fpgaloader.c (function FpgaSendCommand) From ec1c72d73cf257be544bc0fbefde4fc856f63a2d Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 8 Jul 2020 23:11:11 +0200 Subject: [PATCH 029/139] fix felica image --- fpga/fpga_felica.bit | Bin 42179 -> 42179 bytes fpga/fpga_felica.v | 2 -- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/fpga_lf.bit | Bin 42175 -> 42175 bytes 4 files changed, 2 deletions(-) diff --git a/fpga/fpga_felica.bit b/fpga/fpga_felica.bit index dc68b740b025bee0adeee9d72a787e1216b84b5c..3b222908030eefbd6206997787560745d21124d8 100644 GIT binary patch literal 42179 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

!d)4;3;X&=szeZm3mZGpN}KNr|#^v`1c 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 diff --git a/fpga/fpga_felica.v b/fpga/fpga_felica.v index 7c1a95b93..84df3b787 100644 --- a/fpga/fpga_felica.v +++ b/fpga/fpga_felica.v @@ -181,7 +181,6 @@ hi_sniffer he( ); // 100 - HF ISO18092 FeliCa -/* hi_flite hfl( ck_1356megb, hfl_pwr_lo, hfl_pwr_hi, hfl_pwr_oe1, hfl_pwr_oe2, hfl_pwr_oe3, hfl_pwr_oe4, @@ -190,7 +189,6 @@ hi_flite hfl( hfl_dbg, minor_mode ); -*/ // 101 - HF get trace hi_get_trace gt( diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 7d20e220fd35db4ff4743ce9abe5571984253151..3651d4db3cf5825da32aa5166789ea1251cc624d 100644 GIT binary patch delta 26 icmdmgl4<`*rU^E@77Qs2oJPi03Kmv|mK#0GmH+^EMhHRx delta 26 icmdmgl4<`*rU^E@MhqzooQ8&0CdO7K<{LfBmH+^EQV2-^ diff --git a/fpga/fpga_lf.bit b/fpga/fpga_lf.bit index dca02afcc2e275d3dcb454e6c3629be3fb1af94a..c5b2ace9d8085e87615ccad1cf4dc24b4b2a8d02 100644 GIT binary patch delta 26 icmdmgl4<`*rU^E@77Qs2oJPi03Kmuh<{LfBmH+^D)(9^E delta 26 icmdmgl4<`*rU^E@MhqzooQ8&0CdO9ArW-xWmH+^ELI_3x From 7ee811bb9e6c86edf901e9fa75353354a29a58c4 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 8 Jul 2020 23:11:29 +0200 Subject: [PATCH 030/139] felica fixes --- armsrc/felica.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/felica.c b/armsrc/felica.c index 944b89ffe..53f78d17b 100644 --- a/armsrc/felica.c +++ b/armsrc/felica.c @@ -445,7 +445,7 @@ static void iso18092_setup(uint8_t fpga_minor_mode) { if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Start iso18092_setup"); LEDsoff(); - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF_FELICA); // allocate command receive buffer BigBuf_free(); From 246144df2a19640660294a1cf0dd4dec31edf43e Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 8 Jul 2020 23:11:50 +0200 Subject: [PATCH 031/139] amiboo default pack --- armsrc/iso14443a.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 225337485..836360740 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1499,10 +1499,13 @@ void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data) { uint8_t pwd[4]; emlGetMemBt(pwd, (pages - 1) * 4 + MFU_DUMP_PREFIX_LENGTH, sizeof(pwd)); if (memcmp(receivedCmd + 1, pwd, 4) == 0) { - uint8_t cmd[4]; - emlGetMemBt(cmd, pages * 4 + MFU_DUMP_PREFIX_LENGTH, 2); - AddCrc14A(cmd, sizeof(cmd) - 2); - EmSendCmd(cmd, sizeof(cmd)); + uint8_t pack[4]; + emlGetMemBt(pack, pages * 4 + MFU_DUMP_PREFIX_LENGTH, 2); + if (memcmp(pack, "\x00\x00\x00\x00", 4) == 0) { + memcpy(pack, "\x80\x80\x00\x00", 4); + } + AddCrc14A(pack, sizeof(pack) - 2); + EmSendCmd(pack, sizeof(pack)); } else { EmSend4bit(CARD_NACK_NA); if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Auth attempt: %08x", bytes_to_num(receivedCmd + 1, 4)); From a76679f7e6e2bd36546e8af4bb0e2289b76884ba Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 9 Jul 2020 00:20:52 +0200 Subject: [PATCH 032/139] hf iclass reader --- armsrc/iclass.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 99976566c..e01c5a217 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -884,13 +884,13 @@ static bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t void ReaderIClass(uint8_t flags) { uint8_t card_data[6 * 8] = {0xFF}; - uint8_t last_csn[8] = {0, 0, 0, 0, 0, 0, 0, 0}; +// uint8_t last_csn[8] = {0, 0, 0, 0, 0, 0, 0, 0}; uint8_t resp[ICLASS_BUFFER_SIZE]; // memset(card_data, 0xFF, sizeof(card_data)); memset(resp, 0xFF, sizeof(resp)); - bool flag_readonce = flags & FLAG_ICLASS_READER_ONLY_ONCE; // flag to read until one tag is found successfully +// bool flag_readonce = flags & FLAG_ICLASS_READER_ONLY_ONCE; // flag to read until one tag is found successfully bool use_credit_key = flags & FLAG_ICLASS_READER_CEDITKEY; // flag to use credit key bool flag_read_aia = flags & FLAG_ICLASS_READER_AIA; // flag to read block5, application issuer area @@ -938,9 +938,11 @@ void ReaderIClass(uint8_t flags) { // with 0xFF:s in block 3 and 4. LED_B_ON(); - + reply_mix(CMD_ACK, result_status, 0, 0, card_data, sizeof(card_data)); + //Send back to client, but don't bother if we already sent this - // only useful if looping in arm (not try_once && not abort_after_read) + /* if (memcmp(last_csn, card_data, 8) != 0) { reply_mix(CMD_ACK, result_status, 0, 0, card_data, sizeof(card_data)); @@ -950,12 +952,13 @@ void ReaderIClass(uint8_t flags) { } LED_B_OFF(); } + */ // if (userCancelled) { // reply_mix(CMD_ACK, 0xFF, 0, 0, card_data, 0); // switch_off(); // } else { - reply_mix(CMD_ACK, 0, 0, 0, card_data, 0); +// reply_mix(CMD_ACK, result_status, 0, 0, card_data, 0); // } switch_off(); } From d67bc94e2004301f01d428361ad279a10ebe508f Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 9 Jul 2020 00:21:26 +0200 Subject: [PATCH 033/139] hf iclass sniff --- client/src/cmdhficlass.c | 60 +++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 21ad93997..f11d65945 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -2,6 +2,7 @@ // Copyright (C) 2010 iZsh , Hagen Fritsch // Copyright (C) 2011 Gerhard de Koning Gans // Copyright (C) 2014 Midnitesnake & Andy Davies & Martin Holst Swende +// Copyright (C) 2019 piwi // Copyright (C) 2020 Iceman // // This code is licensed to you under the terms of the GNU GPL, version 2 or, @@ -13,6 +14,7 @@ #include "cmdhficlass.h" #include +#include "cliparser.h" #include "cmdparser.h" // command_t #include "commonutil.h" // ARRAYLEN #include "cmdtrace.h" @@ -272,15 +274,7 @@ static int usage_hf_iclass_replay(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } -static int usage_hf_iclass_sniff(void) { - PrintAndLogEx(NORMAL, "Sniff the communication between reader and tag\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass sniff [h]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h Show this help"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass sniff")); - return PM3_SUCCESS; -} + static int usage_hf_iclass_loclass(void) { PrintAndLogEx(NORMAL, "Execute the offline part of loclass attack"); PrintAndLogEx(NORMAL, " An iclass dumpfile is assumed to consist of an arbitrary number of"); @@ -518,9 +512,35 @@ static int CmdHFiClassList(const char *Cmd) { } static int CmdHFiClassSniff(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_hf_iclass_sniff(); - SendCommandNG(CMD_HF_ICLASS_SNIFF, NULL, 0); + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass sniff", + "Sniff the communication reader and tag", + "Usage:\n" + _YELLOW_("\thf iclass sniff") "\n" + _YELLOW_("\thf iclass sniff -j") " -> jam e-purse updates\n" + ); + + void* argtable[] = { + arg_param_begin, + arg_lit0("j", "jam", "Jam (prevent) e-purse updates"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool jam_epurse_update = arg_get_lit(ctx, 1); + const uint8_t update_epurse_sequence[2] = {0x87, 0x02}; + + struct { + uint8_t jam_search_len; + uint8_t jam_search_string[2]; + } PACKED payload; + + if (jam_epurse_update) { + payload.jam_search_len = sizeof(update_epurse_sequence); + memcpy(payload.jam_search_string, update_epurse_sequence, sizeof(payload.jam_search_string)); + } + SendCommandNG(CMD_HF_ICLASS_SNIFF, (uint8_t *)&payload, sizeof(payload)); return PM3_SUCCESS; } @@ -2245,9 +2265,9 @@ static int printKeys(void) { PrintAndLogEx(NORMAL, ""); for (uint8_t i = 0; i < ICLASS_KEYS_MAX; i++) { if (memcmp(iClass_Key_Table[i], "\x00\x00\x00\x00\x00\x00\x00\x00", 8) == 0) - PrintAndLogEx(NORMAL, "%u: %s", i, sprint_hex(iClass_Key_Table[i], 8)); + PrintAndLogEx(INFO, "%u: %s", i, sprint_hex(iClass_Key_Table[i], 8)); else - PrintAndLogEx(NORMAL, "%u: "_YELLOW_("%s"), i, sprint_hex(iClass_Key_Table[i], 8)); + PrintAndLogEx(INFO, "%u: "_YELLOW_("%s"), i, sprint_hex(iClass_Key_Table[i], 8)); } PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; @@ -2866,12 +2886,14 @@ static int CmdHFiClassPermuteKey(const char *Cmd) { bool isReverse = false; int len = 0; char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_hf_iclass_permutekey(); + if (strlen(Cmd) == 0 || cmdp == 'h') + return usage_hf_iclass_permutekey(); isReverse = (cmdp == 'r'); param_gethex_ex(Cmd, 1, data, &len); - if (len % 2) return usage_hf_iclass_permutekey(); + if (len % 2) + return usage_hf_iclass_permutekey(); len >>= 1; @@ -2881,12 +2903,12 @@ static int CmdHFiClassPermuteKey(const char *Cmd) { generate_rev(data, len); uint8_t key_std_format[8] = {0}; permutekey_rev(key, key_std_format); - PrintAndLogEx(SUCCESS, "holiman iclass key | %s \n", sprint_hex(key_std_format, 8)); + PrintAndLogEx(SUCCESS, "Standard NIST format key " _YELLOW_("%s") " \n", sprint_hex(key_std_format, 8)); } else { generate(data, len); uint8_t key_iclass_format[8] = {0}; permutekey(key, key_iclass_format); - PrintAndLogEx(SUCCESS, "holiman std key | %s \n", sprint_hex(key_iclass_format, 8)); + PrintAndLogEx(SUCCESS, "HID permuted iCLASS format: %s \n", sprint_hex(key_iclass_format, 8)); } return PM3_SUCCESS; } @@ -2959,7 +2981,7 @@ int readIclass(bool loop, bool verbose) { picopass_hdr *hdr = (picopass_hdr *)data; uint16_t length = resp.length; - if ( length != sizeof(picopass_hdr)) + if (length != sizeof(picopass_hdr)) continue; PrintAndLogEx(NORMAL, ""); From ed5471a895040369fc3aa07299235eadc3196345 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 9 Jul 2020 19:40:54 +0200 Subject: [PATCH 034/139] fpga --- armsrc/BigBuf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index f41801a26..4642d0079 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -158,7 +158,10 @@ uint32_t BigBuf_get_traceLen(void) { annotation of commands/responses. **/ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag) { - if (!tracing) return false; + if (tracing == false) { + if (DBGLEVEL >= DBG_DEBUG) { Dbprintf("trace is turned off"); } + return false; + } uint8_t *trace = BigBuf_get_addr(); tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + traceLen); @@ -168,6 +171,7 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ // Return when trace is full if (TRACELOG_HDR_LEN + iLen + num_paritybytes >= BigBuf_max_traceLen() - traceLen) { tracing = false; // don't trace any more + if (DBGLEVEL >= DBG_DEBUG) { Dbprintf("trace is full"); } return false; } From 05299373fa42119a8e7e802510cdb2f8b221448b Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 9 Jul 2020 19:41:52 +0200 Subject: [PATCH 035/139] fpga --- armsrc/iclass.c | 118 +++++++++++++++++++++++++++++------------------- 1 file changed, 72 insertions(+), 46 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index e01c5a217..478bafa43 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -775,8 +775,9 @@ send: } -/// THE READER CODE -static void ReaderTransmitIClass(uint8_t *frame, int len, uint32_t *start_time) { +// THE READER CODE +// logs. +static void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time) { CodeIso15693AsReader(frame, len); TransmitTo15693Tag(ToSend, ToSendMax, start_time); @@ -785,12 +786,16 @@ static void ReaderTransmitIClass(uint8_t *frame, int len, uint32_t *start_time) LogTrace(frame, len, *start_time * 4, end_time * 4, NULL, true); } -static bool sendCmdGetResponseWithRetries(uint8_t* command, size_t cmdsize, uint8_t* resp, size_t max_resp_size, +static bool iclass_send_cmd_with_retries(uint8_t* cmd, size_t cmdsize, uint8_t* resp, size_t max_resp_size, uint8_t expected_size, uint8_t tries, uint32_t start_time, uint32_t timeout, uint32_t *eof_time) { while (tries-- > 0) { - ReaderTransmitIClass(command, cmdsize, &start_time); + iclass_send_as_reader(cmd, cmdsize, &start_time); + + if (resp == NULL) + return true; + if (expected_size == GetIso15693AnswerFromTag(resp, max_resp_size, timeout, eof_time)) { return true; } @@ -812,30 +817,31 @@ static bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t static uint8_t select[] = { 0x80 | ICLASS_CMD_SELECT, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static uint8_t read_conf[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x01, 0xfa, 0x22 }; static uint8_t read_check_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; + uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; - // Bit 4: K.If this bit equals to one, the READCHECK will use the Credit Key (Kc); if equals to zero, Debit Key (Kd) willbe used + // Bit 4: K.If this bit equals to one, the READCHECK will use the Credit Key (Kc); if equals to zero, Debit Key (Kd) will be used // bit 7: parity. if (use_credit_key) read_check_cc[0] = 0x10 | ICLASS_CMD_READCHECK; - - uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; uint32_t start_time = GetCountSspClk(); - - ReaderTransmitIClass(act_all, 1, &start_time); + iclass_send_as_reader(act_all, sizeof(act_all), &start_time); // card present? - if (GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_ACTALL, eof_time) < 0) + int len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_ACTALL, eof_time); + if (len < 0) { + Dbprintf("Fail act all (%d)", len); return false; + } // send Identify start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - ReaderTransmitIClass(identify, 1, &start_time); + iclass_send_as_reader(identify, 1, &start_time); // expect a 10-byte response here, 8 byte anticollision-CSN and 2 byte CRC - uint8_t len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); - if (len != 10) + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + if (len != 10) return false; // copy the Anti-collision CSN to our select-packet @@ -843,11 +849,11 @@ static bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t // select the card start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - ReaderTransmitIClass(select, sizeof(select), &start_time); + iclass_send_as_reader(select, sizeof(select), &start_time); // expect a 10-byte response here, 8 byte CSN and 2 byte CRC len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); - if (len != 10) + if (len != 10) return false; //Save CSN in response data @@ -855,23 +861,23 @@ static bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t // card selected, now read config (block1) (only 8 bytes no CRC) start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - ReaderTransmitIClass(read_conf, sizeof(read_conf), &start_time); + iclass_send_as_reader(read_conf, sizeof(read_conf), &start_time); // expect a 8-byte response here len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); - if (len != 8) + if (len != 10) return false; - //Save CC (e-purse) in response data + //Save CONF in response data memcpy(card_data + 8, resp, 8); // card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - ReaderTransmitIClass(read_check_cc, sizeof(read_check_cc), &start_time); + iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time); // expect a 8-byte response here len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); - if (len != 8) + if (len != 8) return false; //Save CC (e-purse) in response data @@ -895,31 +901,32 @@ void ReaderIClass(uint8_t flags) { bool flag_read_aia = flags & FLAG_ICLASS_READER_AIA; // flag to read block5, application issuer area if ((flags & FLAG_ICLASS_READER_INIT) == FLAG_ICLASS_READER_INIT) { + switch_off(); Iso15693InitReader(); - } - - if ((flags & FLAG_ICLASS_READER_CLEARTRACE) == FLAG_ICLASS_READER_CLEARTRACE) { - set_tracing(true); - clear_trace(); StartCountSspClk(); } + + if ((flags & FLAG_ICLASS_READER_CLEARTRACE) == FLAG_ICLASS_READER_CLEARTRACE) { + clear_trace(); + } - uint32_t start_time = 0; uint32_t eof_time = 0; - int read_status = select_iclass_tag(card_data, use_credit_key, &eof_time); - if (read_status == 0) { + bool status = select_iclass_tag(card_data, use_credit_key, &eof_time); + if (status == false) { reply_mix(CMD_ACK, 0xFF, 0, 0, card_data, 0); switch_off(); return; } - - uint8_t result_status = FLAG_ICLASS_CSN | FLAG_ICLASS_CONF | FLAG_ICLASS_CC; + + uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + uint8_t result_status = (FLAG_ICLASS_CSN | FLAG_ICLASS_CONF | FLAG_ICLASS_CC); //Read block 5, AIA if (flag_read_aia) { //Read App Issuer Area block CRC(0x05) => 0xde 0x64 uint8_t read_aa[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x05, 0xde, 0x64}; - if (sendCmdGetResponseWithRetries(read_aa, sizeof(read_aa), resp, sizeof(resp), 10, 10, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time)) { + status = iclass_send_cmd_with_retries(read_aa, sizeof(read_aa), resp, sizeof(resp), 10, 10, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + if (status) { result_status |= FLAG_ICLASS_AIA; memcpy(card_data + (8 * 5), resp, 8); } else { @@ -994,8 +1001,9 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { //for now replay captured auth (as cc not updated) memcpy(check + 5, mac, 4); + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - if (sendCmdGetResponseWithRetries(check, sizeof(check), resp, sizeof(resp), 4, 3, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time) == false) { + if (iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time) == false) { if (DBGLEVEL >= DBG_EXTENDED) DbpString("Error: Authentication Fail!"); continue; } @@ -1004,7 +1012,8 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { read[1] = 1; AddCrc(read + 1, 1); - if (sendCmdGetResponseWithRetries(read, sizeof(read), resp, sizeof(resp), 10, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time) == false) { + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + if (iclass_send_cmd_with_retries(read, sizeof(read), resp, sizeof(resp), 10, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time) == false) { if (DBGLEVEL >= DBG_EXTENDED) DbpString("Dump config (block 1) failed"); continue; } @@ -1032,7 +1041,8 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { read[1] = block; AddCrc(read + 1, 1); - if (sendCmdGetResponseWithRetries(read, sizeof(read), resp, sizeof(resp), 10, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time)) { + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + if (iclass_send_cmd_with_retries(read, sizeof(read), resp, sizeof(resp), 10, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time)) { if (DBGLEVEL >= DBG_EXTENDED) { Dbprintf(" %02x: %02x %02x %02x %02x %02x %02x %02x %02x", block, @@ -1095,7 +1105,7 @@ void iClass_ReadCheck(uint8_t blockno, uint8_t keytype) { uint8_t readcheck[] = { keytype, blockno }; uint8_t resp[8] = {0}; uint32_t eof_time = 0; - bool isOK = sendCmdGetResponseWithRetries(readcheck, sizeof(readcheck), resp, sizeof(resp), 8, 3, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + bool isOK = iclass_send_cmd_with_retries(readcheck, sizeof(readcheck), resp, sizeof(resp), 8, 3, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); reply_mix(CMD_ACK, isOK, 0, 0, 0, 0); switch_off(); } @@ -1103,17 +1113,33 @@ void iClass_ReadCheck(uint8_t blockno, uint8_t keytype) { // used with function select_and_auth (cmdhficlass.c) // which needs to authenticate before doing more things like read/write void iClass_Authentication(uint8_t *mac) { - uint8_t check[] = { ICLASS_CMD_CHECK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - uint8_t resp[ICLASS_BUFFER_SIZE]; + + Iso15693InitReader(); + StartCountSspClk(); + + uint8_t card_data[3 * 8] = {0xFF}; + bool use_credit_key = false; + uint32_t eof_time = 0; + + bool isOK = select_iclass_tag(card_data, use_credit_key, &eof_time); + if (isOK == false) { + reply_ng(CMD_HF_ICLASS_AUTH, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); + return; + } + uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + uint8_t check[9] = { ICLASS_CMD_CHECK }; +// uint8_t mac[4]; +// opt_doReaderMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, mac ) // copy MAC to check command (readersignature) check[5] = mac[0]; check[6] = mac[1]; check[7] = mac[2]; check[8] = mac[3]; - //memcpy(check+5, mac, 4); - uint32_t eof_time = 0; - bool isOK = sendCmdGetResponseWithRetries(check, sizeof(check), resp, sizeof(resp), 4, 3, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + + uint8_t resp[ICLASS_BUFFER_SIZE]; + isOK = iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); reply_ng(CMD_HF_ICLASS_AUTH, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); } @@ -1133,9 +1159,9 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { uint8_t i = 0, isOK = 0; uint8_t lastChunk = ((arg0 >> 8) & 0xFF); bool use_credit_key = ((arg0 >> 16) & 0xFF); - uint8_t keyCount = arg1 & 0xFF; - uint8_t check[] = { ICLASS_CMD_CHECK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + uint8_t check[9] = { ICLASS_CMD_CHECK }; uint8_t resp[ICLASS_BUFFER_SIZE]; uint8_t readcheck_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; @@ -1188,14 +1214,14 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { check[8] = keys[i].mac[3]; // expect 4bytes, 3 retries times.. - isOK = sendCmdGetResponseWithRetries(check, sizeof(check), resp, sizeof(resp), 4, 3, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + isOK = iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 3, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); if (isOK) goto out; start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; // Auth Sequence MUST begin with reading e-purse. (block2) // Card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) - ReaderTransmitIClass(readcheck_cc, sizeof(readcheck_cc), &start_time); + iclass_send_as_reader(readcheck_cc, sizeof(readcheck_cc), &start_time); LED_B_OFF(); } @@ -1220,7 +1246,7 @@ static bool iClass_ReadBlock(uint8_t blockno, uint8_t *data) { uint8_t c[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00}; AddCrc(c + 1, 1); uint32_t eof_time = 0; - bool isOK = sendCmdGetResponseWithRetries(c, sizeof(c), resp, sizeof(resp), 10, 10, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + bool isOK = iclass_send_cmd_with_retries(c, sizeof(c), resp, sizeof(resp), 10, 10, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); memcpy(data, resp, 8); return isOK; } @@ -1286,7 +1312,7 @@ static bool iClass_WriteBlock_ext(uint8_t blockno, uint8_t *data) { uint8_t resp[10] = {0}; uint32_t eof_time = 0; - bool isOK = sendCmdGetResponseWithRetries(write, sizeof(write), resp, sizeof(resp), 10, 3, 0, ICLASS_READER_TIMEOUT_UPDATE, &eof_time); + bool isOK = iclass_send_cmd_with_retries(write, sizeof(write), resp, sizeof(resp), 10, 3, 0, ICLASS_READER_TIMEOUT_UPDATE, &eof_time); if (isOK == false) { return false; } From 57d1ff931648559c99116b2e56a294f34aa04d4a Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 9 Jul 2020 19:41:57 +0200 Subject: [PATCH 036/139] fpga --- armsrc/iso15693.c | 2231 +++++++++++++++++++++++---------------------- 1 file changed, 1120 insertions(+), 1111 deletions(-) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 51dc7fac8..f8214b5e4 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -66,7 +66,7 @@ #include "ticks.h" #include "BigBuf.h" #include "crc16.h" - + // Delays in SSP_CLK ticks. // SSP_CLK runs at 13,56MHz / 32 = 423.75kHz when simulating a tag #define DELAY_READER_TO_ARM 8 @@ -124,43 +124,43 @@ static void BuildInventoryResponse(uint8_t *uid); // n ... length of data void CodeIso15693AsReader(uint8_t *cmd, int n) { - ToSendReset(); + ToSendReset(); - // SOF for 1of4 - ToSend[++ToSendMax] = 0x84; //10000100 + // SOF for 1of4 + ToSend[++ToSendMax] = 0x84; //10000100 - // data - for (int i = 0; i < n; i++) { - for (int j = 0; j < 8; j += 2) { - int these = (cmd[i] >> j) & 0x03; - switch(these) { - case 0: - ToSend[++ToSendMax] = 0x40; //01000000 - break; - case 1: - ToSend[++ToSendMax] = 0x10; //00010000 - break; - case 2: - ToSend[++ToSendMax] = 0x04; //00000100 - break; - case 3: - ToSend[++ToSendMax] = 0x01; //00000001 - break; - } - } - } + // data + for (int i = 0; i < n; i++) { + for (int j = 0; j < 8; j += 2) { + int these = (cmd[i] >> j) & 0x03; + switch(these) { + case 0: + ToSend[++ToSendMax] = 0x40; //01000000 + break; + case 1: + ToSend[++ToSendMax] = 0x10; //00010000 + break; + case 2: + ToSend[++ToSendMax] = 0x04; //00000100 + break; + case 3: + ToSend[++ToSendMax] = 0x01; //00000001 + break; + } + } + } - // EOF - ToSend[++ToSendMax] = 0x20; //0010 + 0000 padding + // EOF + ToSend[++ToSendMax] = 0x20; //0010 + 0000 padding - ToSendMax++; + ToSendMax++; } // Encode EOF only static void CodeIso15693AsReaderEOF(void) { - ToSendReset(); - ToSend[++ToSendMax] = 0x20; - ToSendMax++; + ToSendReset(); + ToSend[++ToSendMax] = 0x20; + ToSendMax++; } @@ -169,164 +169,164 @@ static void CodeIso15693AsReaderEOF(void) { // is designed for more robust communication over longer distances static void CodeIso15693AsReader256(uint8_t *cmd, int n) { - ToSendReset(); + ToSendReset(); - // SOF for 1of256 - ToSend[++ToSendMax] = 0x81; //10000001 + // SOF for 1of256 + ToSend[++ToSendMax] = 0x81; //10000001 - // data - for(int i = 0; i < n; i++) { - for (int j = 0; j <= 255; j++) { - if (cmd[i] == j) { - ToSendStuffBit(0); - ToSendStuffBit(1); - } else { - ToSendStuffBit(0); - ToSendStuffBit(0); - } - } - } + // data + for(int i = 0; i < n; i++) { + for (int j = 0; j <= 255; j++) { + if (cmd[i] == j) { + ToSendStuffBit(0); + ToSendStuffBit(1); + } else { + ToSendStuffBit(0); + ToSendStuffBit(0); + } + } + } - // EOF - ToSend[++ToSendMax] = 0x20; //0010 + 0000 padding + // EOF + ToSend[++ToSendMax] = 0x20; //0010 + 0000 padding - ToSendMax++; + ToSendMax++; } static const uint8_t encode_4bits[16] = { 0xaa, 0x6a, 0x9a, 0x5a, 0xa6, 0x66, 0x96, 0x56, 0xa9, 0x69, 0x99, 0x59, 0xa5, 0x65, 0x95, 0x55 }; void CodeIso15693AsTag(uint8_t *cmd, size_t len) { - /* - * SOF comprises 3 parts; - * * An unmodulated time of 56.64 us - * * 24 pulses of 423.75 kHz (fc/32) - * * A logic 1, which starts with an unmodulated time of 18.88us - * followed by 8 pulses of 423.75kHz (fc/32) - * - * EOF comprises 3 parts: - * - A logic 0 (which starts with 8 pulses of fc/32 followed by an unmodulated - * time of 18.88us. - * - 24 pulses of fc/32 - * - An unmodulated time of 56.64 us - * - * A logic 0 starts with 8 pulses of fc/32 - * followed by an unmodulated time of 256/fc (~18,88us). - * - * A logic 0 starts with unmodulated time of 256/fc (~18,88us) followed by - * 8 pulses of fc/32 (also 18.88us) - * - * A bit here becomes 8 pulses of fc/32. Therefore: - * The SOF can be written as 00011101 = 0x1D - * The EOF can be written as 10111000 = 0xb8 - * A logic 1 is 01 - * A logic 0 is 10 - * - * */ + /* + * SOF comprises 3 parts; + * * An unmodulated time of 56.64 us + * * 24 pulses of 423.75 kHz (fc/32) + * * A logic 1, which starts with an unmodulated time of 18.88us + * followed by 8 pulses of 423.75kHz (fc/32) + * + * EOF comprises 3 parts: + * - A logic 0 (which starts with 8 pulses of fc/32 followed by an unmodulated + * time of 18.88us. + * - 24 pulses of fc/32 + * - An unmodulated time of 56.64 us + * + * A logic 0 starts with 8 pulses of fc/32 + * followed by an unmodulated time of 256/fc (~18,88us). + * + * A logic 0 starts with unmodulated time of 256/fc (~18,88us) followed by + * 8 pulses of fc/32 (also 18.88us) + * + * A bit here becomes 8 pulses of fc/32. Therefore: + * The SOF can be written as 00011101 = 0x1D + * The EOF can be written as 10111000 = 0xb8 + * A logic 1 is 01 + * A logic 0 is 10 + * + * */ - ToSendReset(); + ToSendReset(); - // SOF - ToSend[++ToSendMax] = 0x1D; // 00011101 + // SOF + ToSend[++ToSendMax] = 0x1D; // 00011101 - // data - for (int i = 0; i < len; i++) { - ToSend[++ToSendMax] = encode_4bits[cmd[i] & 0xF]; - ToSend[++ToSendMax] = encode_4bits[cmd[i] >> 4]; - } + // data + for (int i = 0; i < len; i++) { + ToSend[++ToSendMax] = encode_4bits[cmd[i] & 0xF]; + ToSend[++ToSendMax] = encode_4bits[cmd[i] >> 4]; + } - // EOF - ToSend[++ToSendMax] = 0xB8; // 10111000 + // EOF + ToSend[++ToSendMax] = 0xB8; // 10111000 - ToSendMax++; + ToSendMax++; } // Transmit the command (to the tag) that was placed in cmd[]. void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_FULL_MOD); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_FULL_MOD); - if (*start_time < DELAY_ARM_TO_TAG) { - *start_time = DELAY_ARM_TO_TAG; - } + if (*start_time < DELAY_ARM_TO_TAG) { + *start_time = DELAY_ARM_TO_TAG; + } - *start_time = (*start_time - DELAY_ARM_TO_TAG) & 0xfffffff0; + *start_time = (*start_time - DELAY_ARM_TO_TAG) & 0xfffffff0; - if (GetCountSspClk() > *start_time) { // we may miss the intended time - *start_time = (GetCountSspClk() + 16) & 0xfffffff0; // next possible time - } + if (GetCountSspClk() > *start_time) { // we may miss the intended time + *start_time = (GetCountSspClk() + 16) & 0xfffffff0; // next possible time + } - while (GetCountSspClk() < *start_time) - /* wait */ ; + while (GetCountSspClk() < *start_time) + /* wait */ ; - LED_B_ON(); - for (int c = 0; c < len; c++) { - uint8_t data = cmd[c]; - for (int i = 0; i < 8; i++) { - uint16_t send_word = (data & 0x80) ? 0xffff : 0x0000; - while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; - AT91C_BASE_SSC->SSC_THR = send_word; - while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; - AT91C_BASE_SSC->SSC_THR = send_word; - data <<= 1; - } - WDT_HIT(); - } - LED_B_OFF(); + LED_B_ON(); + for (int c = 0; c < len; c++) { + uint8_t data = cmd[c]; + for (int i = 0; i < 8; i++) { + uint16_t send_word = (data & 0x80) ? 0xffff : 0x0000; + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + AT91C_BASE_SSC->SSC_THR = send_word; + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + AT91C_BASE_SSC->SSC_THR = send_word; + data <<= 1; + } + WDT_HIT(); + } + LED_B_OFF(); - *start_time = *start_time + DELAY_ARM_TO_TAG; + *start_time = *start_time + DELAY_ARM_TO_TAG; } //----------------------------------------------------------------------------- -// Transmit the command (to the reader) that was placed in cmd[]. +// Transmit the tag response (to the reader) that was placed in cmd[]. //----------------------------------------------------------------------------- void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, uint32_t slot_time, bool slow) { - // don't use the FPGA_HF_SIMULATOR_MODULATE_424K_8BIT minor mode. It would spoil GetCountSspClk() - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_424K); + // don't use the FPGA_HF_SIMULATOR_MODULATE_424K_8BIT minor mode. It would spoil GetCountSspClk() + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_424K); - uint32_t modulation_start_time = *start_time - DELAY_ARM_TO_READER + 3 * 8; // no need to transfer the unmodulated start of SOF + uint32_t modulation_start_time = *start_time - DELAY_ARM_TO_READER + 3 * 8; // no need to transfer the unmodulated start of SOF - while (GetCountSspClk() > (modulation_start_time & 0xfffffff8) + 3) { // we will miss the intended time - if (slot_time) { - modulation_start_time += slot_time; // use next available slot - } else { - modulation_start_time = (modulation_start_time & 0xfffffff8) + 8; // next possible time - } - } + while (GetCountSspClk() > (modulation_start_time & 0xfffffff8) + 3) { // we will miss the intended time + if (slot_time) { + modulation_start_time += slot_time; // use next available slot + } else { + modulation_start_time = (modulation_start_time & 0xfffffff8) + 8; // next possible time + } + } - while (GetCountSspClk() < (modulation_start_time & 0xfffffff8)) - /* wait */ ; + while (GetCountSspClk() < (modulation_start_time & 0xfffffff8)) + /* wait */ ; - uint8_t shift_delay = modulation_start_time & 0x00000007; + uint8_t shift_delay = modulation_start_time & 0x00000007; - *start_time = modulation_start_time + DELAY_ARM_TO_READER - 3 * 8; + *start_time = modulation_start_time + DELAY_ARM_TO_READER - 3 * 8; - LED_C_ON(); - uint8_t bits_to_shift = 0x00; - uint8_t bits_to_send = 0x00; - for (size_t c = 0; c < len; c++) { - for (int i = (c==0?4:7); i >= 0; i--) { - uint8_t cmd_bits = ((cmd[c] >> i) & 0x01) ? 0xff : 0x00; - for (int j = 0; j < (slow?4:1); ) { - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { - bits_to_send = bits_to_shift << (8 - shift_delay) | cmd_bits >> shift_delay; - AT91C_BASE_SSC->SSC_THR = bits_to_send; - bits_to_shift = cmd_bits; - j++; - } - } - } - WDT_HIT(); - } - // send the remaining bits, padded with 0: - bits_to_send = bits_to_shift << (8 - shift_delay); - for ( ; ; ) { - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { - AT91C_BASE_SSC->SSC_THR = bits_to_send; - break; - } - } - LED_C_OFF(); + LED_C_ON(); + uint8_t bits_to_shift = 0x00; + uint8_t bits_to_send = 0x00; + for (size_t c = 0; c < len; c++) { + for (int i = (c==0?4:7); i >= 0; i--) { + uint8_t cmd_bits = ((cmd[c] >> i) & 0x01) ? 0xff : 0x00; + for (int j = 0; j < (slow?4:1); ) { + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { + bits_to_send = bits_to_shift << (8 - shift_delay) | cmd_bits >> shift_delay; + AT91C_BASE_SSC->SSC_THR = bits_to_send; + bits_to_shift = cmd_bits; + j++; + } + } + } + WDT_HIT(); + } + // send the remaining bits, padded with 0: + bits_to_send = bits_to_shift << (8 - shift_delay); + for ( ; ; ) { + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { + AT91C_BASE_SSC->SSC_THR = bits_to_send; + break; + } + } + LED_C_OFF(); } //============================================================================= @@ -347,270 +347,271 @@ void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, #define MAX_PREVIOUS_AMPLITUDE (-1 - NOISE_THRESHOLD) typedef struct DecodeTag { - enum { - STATE_TAG_SOF_LOW, - STATE_TAG_SOF_RISING_EDGE, - STATE_TAG_SOF_HIGH, - STATE_TAG_SOF_HIGH_END, - STATE_TAG_RECEIVING_DATA, - STATE_TAG_EOF, - STATE_TAG_EOF_TAIL - } state; - int bitCount; - int posCount; - enum { - LOGIC0, - LOGIC1, - SOF_PART1, - SOF_PART2 - } lastBit; - uint16_t shiftReg; - uint16_t max_len; - uint8_t *output; - int len; - int sum1, sum2; - int threshold_sof; - int threshold_half; - uint16_t previous_amplitude; + enum { + STATE_TAG_SOF_LOW, + STATE_TAG_SOF_RISING_EDGE, + STATE_TAG_SOF_HIGH, + STATE_TAG_SOF_HIGH_END, + STATE_TAG_RECEIVING_DATA, + STATE_TAG_EOF, + STATE_TAG_EOF_TAIL + } state; + int bitCount; + int posCount; + enum { + LOGIC0, + LOGIC1, + SOF_PART1, + SOF_PART2 + } lastBit; + uint16_t shiftReg; + uint16_t max_len; + uint8_t *output; + int len; + int sum1; + int sum2; + int threshold_sof; + int threshold_half; + uint16_t previous_amplitude; } DecodeTag_t; //----------------------------------------------------------------------------- // DEMODULATE tag answer //----------------------------------------------------------------------------- static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *DecodeTag) { - switch (DecodeTag->state) { - case STATE_TAG_SOF_LOW: - // waiting for a rising edge - if (amplitude > NOISE_THRESHOLD + DecodeTag->previous_amplitude) { - if (DecodeTag->posCount > 10) { - DecodeTag->threshold_sof = amplitude - DecodeTag->previous_amplitude; // to be divided by 2 - DecodeTag->threshold_half = 0; - DecodeTag->state = STATE_TAG_SOF_RISING_EDGE; - } else { - DecodeTag->posCount = 0; - } - } else { - DecodeTag->posCount++; - DecodeTag->previous_amplitude = amplitude; - } - break; + switch (DecodeTag->state) { + case STATE_TAG_SOF_LOW: + // waiting for a rising edge + if (amplitude > NOISE_THRESHOLD + DecodeTag->previous_amplitude) { + if (DecodeTag->posCount > 10) { + DecodeTag->threshold_sof = amplitude - DecodeTag->previous_amplitude; // to be divided by 2 + DecodeTag->threshold_half = 0; + DecodeTag->state = STATE_TAG_SOF_RISING_EDGE; + } else { + DecodeTag->posCount = 0; + } + } else { + DecodeTag->posCount++; + DecodeTag->previous_amplitude = amplitude; + } + break; - case STATE_TAG_SOF_RISING_EDGE: - if (amplitude > DecodeTag->threshold_sof + DecodeTag->previous_amplitude) { // edge still rising - if (amplitude > DecodeTag->threshold_sof + DecodeTag->threshold_sof) { // steeper edge, take this as time reference - DecodeTag->posCount = 1; - } else { - DecodeTag->posCount = 2; - } - DecodeTag->threshold_sof = (amplitude - DecodeTag->previous_amplitude) / 2; - } else { - DecodeTag->posCount = 2; - DecodeTag->threshold_sof = DecodeTag->threshold_sof/2; - } - // DecodeTag->posCount = 2; - DecodeTag->state = STATE_TAG_SOF_HIGH; - break; + case STATE_TAG_SOF_RISING_EDGE: + if (amplitude > DecodeTag->threshold_sof + DecodeTag->previous_amplitude) { // edge still rising + if (amplitude > DecodeTag->threshold_sof + DecodeTag->threshold_sof) { // steeper edge, take this as time reference + DecodeTag->posCount = 1; + } else { + DecodeTag->posCount = 2; + } + DecodeTag->threshold_sof = (amplitude - DecodeTag->previous_amplitude) / 2; + } else { + DecodeTag->posCount = 2; + DecodeTag->threshold_sof = DecodeTag->threshold_sof/2; + } + // DecodeTag->posCount = 2; + DecodeTag->state = STATE_TAG_SOF_HIGH; + break; - case STATE_TAG_SOF_HIGH: - // waiting for 10 times high. Take average over the last 8 - if (amplitude > DecodeTag->threshold_sof) { - DecodeTag->posCount++; - if (DecodeTag->posCount > 2) { - DecodeTag->threshold_half += amplitude; // keep track of average high value - } - if (DecodeTag->posCount == 10) { - DecodeTag->threshold_half >>= 2; // (4 times 1/2 average) - DecodeTag->state = STATE_TAG_SOF_HIGH_END; - } - } else { // high phase was too short - DecodeTag->posCount = 1; - DecodeTag->previous_amplitude = amplitude; - DecodeTag->state = STATE_TAG_SOF_LOW; - } - break; + case STATE_TAG_SOF_HIGH: + // waiting for 10 times high. Take average over the last 8 + if (amplitude > DecodeTag->threshold_sof) { + DecodeTag->posCount++; + if (DecodeTag->posCount > 2) { + DecodeTag->threshold_half += amplitude; // keep track of average high value + } + if (DecodeTag->posCount == 10) { + DecodeTag->threshold_half >>= 2; // (4 times 1/2 average) + DecodeTag->state = STATE_TAG_SOF_HIGH_END; + } + } else { // high phase was too short + DecodeTag->posCount = 1; + DecodeTag->previous_amplitude = amplitude; + DecodeTag->state = STATE_TAG_SOF_LOW; + } + break; - case STATE_TAG_SOF_HIGH_END: - // check for falling edge - if (DecodeTag->posCount == 13 && amplitude < DecodeTag->threshold_sof) { - DecodeTag->lastBit = SOF_PART1; // detected 1st part of SOF (12 samples low and 12 samples high) - DecodeTag->shiftReg = 0; - DecodeTag->bitCount = 0; - DecodeTag->len = 0; - DecodeTag->sum1 = amplitude; - DecodeTag->sum2 = 0; - DecodeTag->posCount = 2; - DecodeTag->state = STATE_TAG_RECEIVING_DATA; - // FpgaDisableTracing(); // DEBUGGING - // Dbprintf("amplitude = %d, threshold_sof = %d, threshold_half/4 = %d, previous_amplitude = %d", - // amplitude, - // DecodeTag->threshold_sof, - // DecodeTag->threshold_half/4, - // DecodeTag->previous_amplitude); // DEBUGGING - LED_C_ON(); - } else { - DecodeTag->posCount++; - if (DecodeTag->posCount > 13) { // high phase too long - DecodeTag->posCount = 0; - DecodeTag->previous_amplitude = amplitude; - DecodeTag->state = STATE_TAG_SOF_LOW; - LED_C_OFF(); - } - } - break; + case STATE_TAG_SOF_HIGH_END: + // check for falling edge + if (DecodeTag->posCount == 13 && amplitude < DecodeTag->threshold_sof) { + DecodeTag->lastBit = SOF_PART1; // detected 1st part of SOF (12 samples low and 12 samples high) + DecodeTag->shiftReg = 0; + DecodeTag->bitCount = 0; + DecodeTag->len = 0; + DecodeTag->sum1 = amplitude; + DecodeTag->sum2 = 0; + DecodeTag->posCount = 2; + DecodeTag->state = STATE_TAG_RECEIVING_DATA; + // FpgaDisableTracing(); // DEBUGGING + // Dbprintf("amplitude = %d, threshold_sof = %d, threshold_half/4 = %d, previous_amplitude = %d", + // amplitude, + // DecodeTag->threshold_sof, + // DecodeTag->threshold_half/4, + // DecodeTag->previous_amplitude); // DEBUGGING + LED_C_ON(); + } else { + DecodeTag->posCount++; + if (DecodeTag->posCount > 13) { // high phase too long + DecodeTag->posCount = 0; + DecodeTag->previous_amplitude = amplitude; + DecodeTag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } + } + break; - case STATE_TAG_RECEIVING_DATA: - // FpgaDisableTracing(); // DEBUGGING - // Dbprintf("amplitude = %d, threshold_sof = %d, threshold_half/4 = %d, previous_amplitude = %d", - // amplitude, - // DecodeTag->threshold_sof, - // DecodeTag->threshold_half/4, - // DecodeTag->previous_amplitude); // DEBUGGING - if (DecodeTag->posCount == 1) { - DecodeTag->sum1 = 0; - DecodeTag->sum2 = 0; - } - if (DecodeTag->posCount <= 4) { - DecodeTag->sum1 += amplitude; - } else { - DecodeTag->sum2 += amplitude; - } - if (DecodeTag->posCount == 8) { - if (DecodeTag->sum1 > DecodeTag->threshold_half && DecodeTag->sum2 > DecodeTag->threshold_half) { // modulation in both halves - if (DecodeTag->lastBit == LOGIC0) { // this was already part of EOF - DecodeTag->state = STATE_TAG_EOF; - } else { - DecodeTag->posCount = 0; - DecodeTag->previous_amplitude = amplitude; - DecodeTag->state = STATE_TAG_SOF_LOW; - LED_C_OFF(); - } - } else if (DecodeTag->sum1 < DecodeTag->threshold_half && DecodeTag->sum2 > DecodeTag->threshold_half) { // modulation in second half - // logic 1 - if (DecodeTag->lastBit == SOF_PART1) { // still part of SOF - DecodeTag->lastBit = SOF_PART2; // SOF completed - } else { - DecodeTag->lastBit = LOGIC1; - DecodeTag->shiftReg >>= 1; - DecodeTag->shiftReg |= 0x80; - DecodeTag->bitCount++; - if (DecodeTag->bitCount == 8) { - DecodeTag->output[DecodeTag->len] = DecodeTag->shiftReg; - DecodeTag->len++; - // if (DecodeTag->shiftReg == 0x12 && DecodeTag->len == 1) FpgaDisableTracing(); // DEBUGGING - if (DecodeTag->len > DecodeTag->max_len) { - // buffer overflow, give up - LED_C_OFF(); - return true; - } - DecodeTag->bitCount = 0; - DecodeTag->shiftReg = 0; - } - } - } else if (DecodeTag->sum1 > DecodeTag->threshold_half && DecodeTag->sum2 < DecodeTag->threshold_half) { // modulation in first half - // logic 0 - if (DecodeTag->lastBit == SOF_PART1) { // incomplete SOF - DecodeTag->posCount = 0; - DecodeTag->previous_amplitude = amplitude; - DecodeTag->state = STATE_TAG_SOF_LOW; - LED_C_OFF(); - } else { - DecodeTag->lastBit = LOGIC0; - DecodeTag->shiftReg >>= 1; - DecodeTag->bitCount++; - if (DecodeTag->bitCount == 8) { - DecodeTag->output[DecodeTag->len] = DecodeTag->shiftReg; - DecodeTag->len++; - // if (DecodeTag->shiftReg == 0x12 && DecodeTag->len == 1) FpgaDisableTracing(); // DEBUGGING - if (DecodeTag->len > DecodeTag->max_len) { - // buffer overflow, give up - DecodeTag->posCount = 0; - DecodeTag->previous_amplitude = amplitude; - DecodeTag->state = STATE_TAG_SOF_LOW; - LED_C_OFF(); - } - DecodeTag->bitCount = 0; - DecodeTag->shiftReg = 0; - } - } - } else { // no modulation - if (DecodeTag->lastBit == SOF_PART2) { // only SOF (this is OK for iClass) - LED_C_OFF(); - return true; - } else { - DecodeTag->posCount = 0; - DecodeTag->state = STATE_TAG_SOF_LOW; - LED_C_OFF(); - } - } - DecodeTag->posCount = 0; - } - DecodeTag->posCount++; - break; + case STATE_TAG_RECEIVING_DATA: + // FpgaDisableTracing(); // DEBUGGING + // Dbprintf("amplitude = %d, threshold_sof = %d, threshold_half/4 = %d, previous_amplitude = %d", + // amplitude, + // DecodeTag->threshold_sof, + // DecodeTag->threshold_half/4, + // DecodeTag->previous_amplitude); // DEBUGGING + if (DecodeTag->posCount == 1) { + DecodeTag->sum1 = 0; + DecodeTag->sum2 = 0; + } + if (DecodeTag->posCount <= 4) { + DecodeTag->sum1 += amplitude; + } else { + DecodeTag->sum2 += amplitude; + } + if (DecodeTag->posCount == 8) { + if (DecodeTag->sum1 > DecodeTag->threshold_half && DecodeTag->sum2 > DecodeTag->threshold_half) { // modulation in both halves + if (DecodeTag->lastBit == LOGIC0) { // this was already part of EOF + DecodeTag->state = STATE_TAG_EOF; + } else { + DecodeTag->posCount = 0; + DecodeTag->previous_amplitude = amplitude; + DecodeTag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } + } else if (DecodeTag->sum1 < DecodeTag->threshold_half && DecodeTag->sum2 > DecodeTag->threshold_half) { // modulation in second half + // logic 1 + if (DecodeTag->lastBit == SOF_PART1) { // still part of SOF + DecodeTag->lastBit = SOF_PART2; // SOF completed + } else { + DecodeTag->lastBit = LOGIC1; + DecodeTag->shiftReg >>= 1; + DecodeTag->shiftReg |= 0x80; + DecodeTag->bitCount++; + if (DecodeTag->bitCount == 8) { + DecodeTag->output[DecodeTag->len] = DecodeTag->shiftReg; + DecodeTag->len++; + // if (DecodeTag->shiftReg == 0x12 && DecodeTag->len == 1) FpgaDisableTracing(); // DEBUGGING + if (DecodeTag->len > DecodeTag->max_len) { + // buffer overflow, give up + LED_C_OFF(); + return true; + } + DecodeTag->bitCount = 0; + DecodeTag->shiftReg = 0; + } + } + } else if (DecodeTag->sum1 > DecodeTag->threshold_half && DecodeTag->sum2 < DecodeTag->threshold_half) { // modulation in first half + // logic 0 + if (DecodeTag->lastBit == SOF_PART1) { // incomplete SOF + DecodeTag->posCount = 0; + DecodeTag->previous_amplitude = amplitude; + DecodeTag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } else { + DecodeTag->lastBit = LOGIC0; + DecodeTag->shiftReg >>= 1; + DecodeTag->bitCount++; + if (DecodeTag->bitCount == 8) { + DecodeTag->output[DecodeTag->len] = DecodeTag->shiftReg; + DecodeTag->len++; + // if (DecodeTag->shiftReg == 0x12 && DecodeTag->len == 1) FpgaDisableTracing(); // DEBUGGING + if (DecodeTag->len > DecodeTag->max_len) { + // buffer overflow, give up + DecodeTag->posCount = 0; + DecodeTag->previous_amplitude = amplitude; + DecodeTag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } + DecodeTag->bitCount = 0; + DecodeTag->shiftReg = 0; + } + } + } else { // no modulation + if (DecodeTag->lastBit == SOF_PART2) { // only SOF (this is OK for iClass) + LED_C_OFF(); + return true; + } else { + DecodeTag->posCount = 0; + DecodeTag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } + } + DecodeTag->posCount = 0; + } + DecodeTag->posCount++; + break; - case STATE_TAG_EOF: - if (DecodeTag->posCount == 1) { - DecodeTag->sum1 = 0; - DecodeTag->sum2 = 0; - } - if (DecodeTag->posCount <= 4) { - DecodeTag->sum1 += amplitude; - } else { - DecodeTag->sum2 += amplitude; - } - if (DecodeTag->posCount == 8) { - if (DecodeTag->sum1 > DecodeTag->threshold_half && DecodeTag->sum2 < DecodeTag->threshold_half) { // modulation in first half - DecodeTag->posCount = 0; - DecodeTag->state = STATE_TAG_EOF_TAIL; - } else { - DecodeTag->posCount = 0; - DecodeTag->previous_amplitude = amplitude; - DecodeTag->state = STATE_TAG_SOF_LOW; - LED_C_OFF(); - } - } - DecodeTag->posCount++; - break; + case STATE_TAG_EOF: + if (DecodeTag->posCount == 1) { + DecodeTag->sum1 = 0; + DecodeTag->sum2 = 0; + } + if (DecodeTag->posCount <= 4) { + DecodeTag->sum1 += amplitude; + } else { + DecodeTag->sum2 += amplitude; + } + if (DecodeTag->posCount == 8) { + if (DecodeTag->sum1 > DecodeTag->threshold_half && DecodeTag->sum2 < DecodeTag->threshold_half) { // modulation in first half + DecodeTag->posCount = 0; + DecodeTag->state = STATE_TAG_EOF_TAIL; + } else { + DecodeTag->posCount = 0; + DecodeTag->previous_amplitude = amplitude; + DecodeTag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } + } + DecodeTag->posCount++; + break; - case STATE_TAG_EOF_TAIL: - if (DecodeTag->posCount == 1) { - DecodeTag->sum1 = 0; - DecodeTag->sum2 = 0; - } - if (DecodeTag->posCount <= 4) { - DecodeTag->sum1 += amplitude; - } else { - DecodeTag->sum2 += amplitude; - } - if (DecodeTag->posCount == 8) { - if (DecodeTag->sum1 < DecodeTag->threshold_half && DecodeTag->sum2 < DecodeTag->threshold_half) { // no modulation in both halves - LED_C_OFF(); - return true; - } else { - DecodeTag->posCount = 0; - DecodeTag->previous_amplitude = amplitude; - DecodeTag->state = STATE_TAG_SOF_LOW; - LED_C_OFF(); - } - } - DecodeTag->posCount++; - break; - } + case STATE_TAG_EOF_TAIL: + if (DecodeTag->posCount == 1) { + DecodeTag->sum1 = 0; + DecodeTag->sum2 = 0; + } + if (DecodeTag->posCount <= 4) { + DecodeTag->sum1 += amplitude; + } else { + DecodeTag->sum2 += amplitude; + } + if (DecodeTag->posCount == 8) { + if (DecodeTag->sum1 < DecodeTag->threshold_half && DecodeTag->sum2 < DecodeTag->threshold_half) { // no modulation in both halves + LED_C_OFF(); + return true; + } else { + DecodeTag->posCount = 0; + DecodeTag->previous_amplitude = amplitude; + DecodeTag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } + } + DecodeTag->posCount++; + break; + } - return false; + return false; } static void DecodeTagInit(DecodeTag_t *DecodeTag, uint8_t *data, uint16_t max_len) { - DecodeTag->previous_amplitude = MAX_PREVIOUS_AMPLITUDE; - DecodeTag->posCount = 0; - DecodeTag->state = STATE_TAG_SOF_LOW; - DecodeTag->output = data; - DecodeTag->max_len = max_len; + DecodeTag->previous_amplitude = MAX_PREVIOUS_AMPLITUDE; + DecodeTag->posCount = 0; + DecodeTag->state = STATE_TAG_SOF_LOW; + DecodeTag->output = data; + DecodeTag->max_len = max_len; } static void DecodeTagReset(DecodeTag_t *DecodeTag) { - DecodeTag->posCount = 0; - DecodeTag->state = STATE_TAG_SOF_LOW; - DecodeTag->previous_amplitude = MAX_PREVIOUS_AMPLITUDE; + DecodeTag->posCount = 0; + DecodeTag->state = STATE_TAG_SOF_LOW; + DecodeTag->previous_amplitude = MAX_PREVIOUS_AMPLITUDE; } @@ -619,90 +620,97 @@ static void DecodeTagReset(DecodeTag_t *DecodeTag) { */ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeout, uint32_t *eof_time) { - int samples = 0; - int ret = 0; + int samples = 0; + int ret = 0; - uint16_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; + uint16_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; - // the Decoder data structure - DecodeTag_t DecodeTag = { 0 }; - DecodeTagInit(&DecodeTag, response, max_len); + // the Decoder data structure + DecodeTag_t DecodeTag = { 0 }; + DecodeTagInit(&DecodeTag, response, max_len); - // wait for last transfer to complete - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); - // And put the FPGA in the appropriate mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_424_KHZ | FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE); + // And put the FPGA in the appropriate mode + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_424_KHZ | FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE); - // Setup and start DMA. - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); - FpgaSetupSscDma((uint8_t*) dmaBuf, ISO15693_DMA_BUFFER_SIZE); - uint32_t dma_start_time = 0; - uint16_t *upTo = dmaBuf; + // Setup and start DMA. + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + FpgaSetupSscDma((uint8_t*) dmaBuf, ISO15693_DMA_BUFFER_SIZE); + uint32_t dma_start_time = 0; + uint16_t *upTo = dmaBuf; - for(;;) { - uint16_t behindBy = ((uint16_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (ISO15693_DMA_BUFFER_SIZE-1); + for(;;) { + uint16_t behindBy = ((uint16_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (ISO15693_DMA_BUFFER_SIZE-1); - if (behindBy == 0) continue; + if (behindBy == 0) continue; - samples++; - if (samples == 1) { - // DMA has transferred the very first data - dma_start_time = GetCountSspClk() & 0xfffffff0; - } + samples++; + if (samples == 1) { + // DMA has transferred the very first data + dma_start_time = GetCountSspClk() & 0xfffffff0; + } - uint16_t tagdata = *upTo++; + uint16_t tagdata = *upTo++; - if(upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. - upTo = dmaBuf; // start reading the circular buffer from the beginning - if (behindBy > (9*ISO15693_DMA_BUFFER_SIZE/10)) { - Dbprintf("About to blow circular buffer - aborted! behindBy=%d", behindBy); - ret = -1; - break; - } - } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO15693_DMA_BUFFER_SIZE; // DMA Next Counter registers - } + if(upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning + if (behindBy > (9 * ISO15693_DMA_BUFFER_SIZE / 10)) { + Dbprintf("About to blow circular buffer - aborted! behindBy=%d", behindBy); + ret = -1; + break; + } + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO15693_DMA_BUFFER_SIZE; // DMA Next Counter registers + } - if (Handle15693SamplesFromTag(tagdata, &DecodeTag)) { - *eof_time = dma_start_time + samples*16 - DELAY_TAG_TO_ARM; // end of EOF - if (DecodeTag.lastBit == SOF_PART2) { - *eof_time -= 8*16; // needed 8 additional samples to confirm single SOF (iCLASS) - } - if (DecodeTag.len > DecodeTag.max_len) { - ret = -2; // buffer overflow - } - break; - } + if (Handle15693SamplesFromTag(tagdata, &DecodeTag)) { + *eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM; // end of EOF + if (DecodeTag.lastBit == SOF_PART2) { + *eof_time -= (8 * 16); // needed 8 additional samples to confirm single SOF (iCLASS) + } + if (DecodeTag.len > DecodeTag.max_len) { + ret = -2; // buffer overflow + } + break; + } - if (samples > timeout && DecodeTag.state < STATE_TAG_RECEIVING_DATA) { - ret = -1; // timeout - break; - } + if (samples > timeout && DecodeTag.state < STATE_TAG_RECEIVING_DATA) { + ret = -1; // timeout + break; + } - } + } - FpgaDisableSscDma(); + FpgaDisableSscDma(); - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("samples = %d, ret = %d, Decoder: state = %d, lastBit = %d, len = %d, bitCount = %d, posCount = %d", - samples, ret, DecodeTag.state, DecodeTag.lastBit, DecodeTag.len, DecodeTag.bitCount, DecodeTag.posCount); + uint32_t sof_time = *eof_time + - DecodeTag.len * 8 * 8 * 16 // time for byte transfers + - 32 * 16 // time for SOF transfer + - (DecodeTag.lastBit != SOF_PART2?32*16:0); // time for EOF transfer - if (ret < 0) { - return ret; - } + if (DBGLEVEL >= DBG_EXTENDED) { + Dbprintf("samples = %d, ret = %d, Decoder: state = %d, lastBit = %d, len = %d, bitCount = %d, posCount = %d", + samples, + ret, + DecodeTag.state, + DecodeTag.lastBit, + DecodeTag.len, + DecodeTag.bitCount, + DecodeTag.posCount + ); + Dbprintf("timing: sof_time = %d, eof_time = %d", (sof_time * 4), (*eof_time * 4)); + } - uint32_t sof_time = *eof_time - - DecodeTag.len * 8 * 8 * 16 // time for byte transfers - - 32 * 16 // time for SOF transfer - - (DecodeTag.lastBit != SOF_PART2?32*16:0); // time for EOF transfer + LogTrace(DecodeTag.output, DecodeTag.len, (sof_time * 4), (*eof_time * 4), NULL, false); - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("timing: sof_time = %d, eof_time = %d", sof_time, *eof_time); - - LogTrace(DecodeTag.output, DecodeTag.len, sof_time*4, *eof_time*4, NULL, false); - - return DecodeTag.len; + if (ret < 0) { + return ret; + } + return DecodeTag.len; } @@ -720,279 +728,279 @@ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeo //============================================================================= typedef struct DecodeReader { - enum { - STATE_READER_UNSYNCD, - STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF, - STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF, - STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF, - STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF, - STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4, - STATE_READER_RECEIVE_DATA_1_OUT_OF_4, - STATE_READER_RECEIVE_DATA_1_OUT_OF_256, - STATE_READER_RECEIVE_JAMMING - } state; - enum { - CODING_1_OUT_OF_4, - CODING_1_OUT_OF_256 - } Coding; - uint8_t shiftReg; - uint8_t bitCount; - int byteCount; - int byteCountMax; - int posCount; - int sum1, sum2; - uint8_t *output; - uint8_t jam_search_len; - uint8_t *jam_search_string; + enum { + STATE_READER_UNSYNCD, + STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF, + STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF, + STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF, + STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF, + STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4, + STATE_READER_RECEIVE_DATA_1_OUT_OF_4, + STATE_READER_RECEIVE_DATA_1_OUT_OF_256, + STATE_READER_RECEIVE_JAMMING + } state; + enum { + CODING_1_OUT_OF_4, + CODING_1_OUT_OF_256 + } Coding; + uint8_t shiftReg; + uint8_t bitCount; + int byteCount; + int byteCountMax; + int posCount; + int sum1, sum2; + uint8_t *output; + uint8_t jam_search_len; + uint8_t *jam_search_string; } DecodeReader_t; static void DecodeReaderInit(DecodeReader_t* DecodeReader, uint8_t *data, uint16_t max_len, uint8_t jam_search_len, uint8_t *jam_search_string) { - DecodeReader->output = data; - DecodeReader->byteCountMax = max_len; - DecodeReader->state = STATE_READER_UNSYNCD; - DecodeReader->byteCount = 0; - DecodeReader->bitCount = 0; - DecodeReader->posCount = 1; - DecodeReader->shiftReg = 0; - DecodeReader->jam_search_len = jam_search_len; - DecodeReader->jam_search_string = jam_search_string; + DecodeReader->output = data; + DecodeReader->byteCountMax = max_len; + DecodeReader->state = STATE_READER_UNSYNCD; + DecodeReader->byteCount = 0; + DecodeReader->bitCount = 0; + DecodeReader->posCount = 1; + DecodeReader->shiftReg = 0; + DecodeReader->jam_search_len = jam_search_len; + DecodeReader->jam_search_string = jam_search_string; } static void DecodeReaderReset(DecodeReader_t* DecodeReader) { - DecodeReader->state = STATE_READER_UNSYNCD; + DecodeReader->state = STATE_READER_UNSYNCD; } static RAMFUNC int Handle15693SampleFromReader(bool bit, DecodeReader_t *DecodeReader) { - switch (DecodeReader->state) { - case STATE_READER_UNSYNCD: - // wait for unmodulated carrier - if (bit) { - DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; - } - break; + switch (DecodeReader->state) { + case STATE_READER_UNSYNCD: + // wait for unmodulated carrier + if (bit) { + DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + } + break; - case STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF: - if (!bit) { - // we went low, so this could be the beginning of a SOF - DecodeReader->posCount = 1; - DecodeReader->state = STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF; - } - break; + case STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF: + if (!bit) { + // we went low, so this could be the beginning of a SOF + DecodeReader->posCount = 1; + DecodeReader->state = STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF; + } + break; - case STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF: - DecodeReader->posCount++; - if (bit) { // detected rising edge - if (DecodeReader->posCount < 4) { // rising edge too early (nominally expected at 5) - DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; - } else { // SOF - DecodeReader->state = STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF; - } - } else { - if (DecodeReader->posCount > 5) { // stayed low for too long - DecodeReaderReset(DecodeReader); - } else { - // do nothing, keep waiting - } - } - break; + case STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF: + DecodeReader->posCount++; + if (bit) { // detected rising edge + if (DecodeReader->posCount < 4) { // rising edge too early (nominally expected at 5) + DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + } else { // SOF + DecodeReader->state = STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF; + } + } else { + if (DecodeReader->posCount > 5) { // stayed low for too long + DecodeReaderReset(DecodeReader); + } else { + // do nothing, keep waiting + } + } + break; - case STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF: - DecodeReader->posCount++; - if (!bit) { // detected a falling edge - if (DecodeReader->posCount < 20) { // falling edge too early (nominally expected at 21 earliest) - DecodeReaderReset(DecodeReader); - } else if (DecodeReader->posCount < 23) { // SOF for 1 out of 4 coding - DecodeReader->Coding = CODING_1_OUT_OF_4; - DecodeReader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; - } else if (DecodeReader->posCount < 28) { // falling edge too early (nominally expected at 29 latest) - DecodeReaderReset(DecodeReader); - } else { // SOF for 1 out of 256 coding - DecodeReader->Coding = CODING_1_OUT_OF_256; - DecodeReader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; - } - } else { - if (DecodeReader->posCount > 29) { // stayed high for too long - DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; - } else { - // do nothing, keep waiting - } - } - break; + case STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF: + DecodeReader->posCount++; + if (!bit) { // detected a falling edge + if (DecodeReader->posCount < 20) { // falling edge too early (nominally expected at 21 earliest) + DecodeReaderReset(DecodeReader); + } else if (DecodeReader->posCount < 23) { // SOF for 1 out of 4 coding + DecodeReader->Coding = CODING_1_OUT_OF_4; + DecodeReader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; + } else if (DecodeReader->posCount < 28) { // falling edge too early (nominally expected at 29 latest) + DecodeReaderReset(DecodeReader); + } else { // SOF for 1 out of 256 coding + DecodeReader->Coding = CODING_1_OUT_OF_256; + DecodeReader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; + } + } else { + if (DecodeReader->posCount > 29) { // stayed high for too long + DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + } else { + // do nothing, keep waiting + } + } + break; - case STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF: - DecodeReader->posCount++; - if (bit) { // detected rising edge - if (DecodeReader->Coding == CODING_1_OUT_OF_256) { - if (DecodeReader->posCount < 32) { // rising edge too early (nominally expected at 33) - DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; - } else { - DecodeReader->posCount = 1; - DecodeReader->bitCount = 0; - DecodeReader->byteCount = 0; - DecodeReader->sum1 = 1; - DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_256; - LED_B_ON(); - } - } else { // CODING_1_OUT_OF_4 - if (DecodeReader->posCount < 24) { // rising edge too early (nominally expected at 25) - DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; - } else { - DecodeReader->posCount = 1; - DecodeReader->state = STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4; - } - } - } else { - if (DecodeReader->Coding == CODING_1_OUT_OF_256) { - if (DecodeReader->posCount > 34) { // signal stayed low for too long - DecodeReaderReset(DecodeReader); - } else { - // do nothing, keep waiting - } - } else { // CODING_1_OUT_OF_4 - if (DecodeReader->posCount > 26) { // signal stayed low for too long - DecodeReaderReset(DecodeReader); - } else { - // do nothing, keep waiting - } - } - } - break; + case STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF: + DecodeReader->posCount++; + if (bit) { // detected rising edge + if (DecodeReader->Coding == CODING_1_OUT_OF_256) { + if (DecodeReader->posCount < 32) { // rising edge too early (nominally expected at 33) + DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + } else { + DecodeReader->posCount = 1; + DecodeReader->bitCount = 0; + DecodeReader->byteCount = 0; + DecodeReader->sum1 = 1; + DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_256; + LED_B_ON(); + } + } else { // CODING_1_OUT_OF_4 + if (DecodeReader->posCount < 24) { // rising edge too early (nominally expected at 25) + DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + } else { + DecodeReader->posCount = 1; + DecodeReader->state = STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4; + } + } + } else { + if (DecodeReader->Coding == CODING_1_OUT_OF_256) { + if (DecodeReader->posCount > 34) { // signal stayed low for too long + DecodeReaderReset(DecodeReader); + } else { + // do nothing, keep waiting + } + } else { // CODING_1_OUT_OF_4 + if (DecodeReader->posCount > 26) { // signal stayed low for too long + DecodeReaderReset(DecodeReader); + } else { + // do nothing, keep waiting + } + } + } + break; - case STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4: - DecodeReader->posCount++; - if (bit) { - if (DecodeReader->posCount == 9) { - DecodeReader->posCount = 1; - DecodeReader->bitCount = 0; - DecodeReader->byteCount = 0; - DecodeReader->sum1 = 1; - DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_4; - LED_B_ON(); - } else { - // do nothing, keep waiting - } - } else { // unexpected falling edge - DecodeReaderReset(DecodeReader); - } - break; + case STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4: + DecodeReader->posCount++; + if (bit) { + if (DecodeReader->posCount == 9) { + DecodeReader->posCount = 1; + DecodeReader->bitCount = 0; + DecodeReader->byteCount = 0; + DecodeReader->sum1 = 1; + DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_4; + LED_B_ON(); + } else { + // do nothing, keep waiting + } + } else { // unexpected falling edge + DecodeReaderReset(DecodeReader); + } + break; - case STATE_READER_RECEIVE_DATA_1_OUT_OF_4: - DecodeReader->posCount++; - if (DecodeReader->posCount == 1) { - DecodeReader->sum1 = bit?1:0; - } else if (DecodeReader->posCount <= 4) { - if (bit) DecodeReader->sum1++; - } else if (DecodeReader->posCount == 5) { - DecodeReader->sum2 = bit?1:0; - } else { - if (bit) DecodeReader->sum2++; - } - if (DecodeReader->posCount == 8) { - DecodeReader->posCount = 0; - if (DecodeReader->sum1 <= 1 && DecodeReader->sum2 >= 3) { // EOF - LED_B_OFF(); // Finished receiving - DecodeReaderReset(DecodeReader); - if (DecodeReader->byteCount != 0) { - return true; - } - } else if (DecodeReader->sum1 >= 3 && DecodeReader->sum2 <= 1) { // detected a 2bit position - DecodeReader->shiftReg >>= 2; - DecodeReader->shiftReg |= (DecodeReader->bitCount << 6); - } - if (DecodeReader->bitCount == 15) { // we have a full byte - DecodeReader->output[DecodeReader->byteCount++] = DecodeReader->shiftReg; - if (DecodeReader->byteCount > DecodeReader->byteCountMax) { - // buffer overflow, give up - LED_B_OFF(); - DecodeReaderReset(DecodeReader); - } - DecodeReader->bitCount = 0; - DecodeReader->shiftReg = 0; - if (DecodeReader->byteCount == DecodeReader->jam_search_len) { - if (!memcmp(DecodeReader->output, DecodeReader->jam_search_string, DecodeReader->jam_search_len)) { - LED_D_ON(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_JAM); - DecodeReader->state = STATE_READER_RECEIVE_JAMMING; - } - } - } else { - DecodeReader->bitCount++; - } - } - break; + case STATE_READER_RECEIVE_DATA_1_OUT_OF_4: + DecodeReader->posCount++; + if (DecodeReader->posCount == 1) { + DecodeReader->sum1 = bit?1:0; + } else if (DecodeReader->posCount <= 4) { + if (bit) DecodeReader->sum1++; + } else if (DecodeReader->posCount == 5) { + DecodeReader->sum2 = bit?1:0; + } else { + if (bit) DecodeReader->sum2++; + } + if (DecodeReader->posCount == 8) { + DecodeReader->posCount = 0; + if (DecodeReader->sum1 <= 1 && DecodeReader->sum2 >= 3) { // EOF + LED_B_OFF(); // Finished receiving + DecodeReaderReset(DecodeReader); + if (DecodeReader->byteCount != 0) { + return true; + } + } else if (DecodeReader->sum1 >= 3 && DecodeReader->sum2 <= 1) { // detected a 2bit position + DecodeReader->shiftReg >>= 2; + DecodeReader->shiftReg |= (DecodeReader->bitCount << 6); + } + if (DecodeReader->bitCount == 15) { // we have a full byte + DecodeReader->output[DecodeReader->byteCount++] = DecodeReader->shiftReg; + if (DecodeReader->byteCount > DecodeReader->byteCountMax) { + // buffer overflow, give up + LED_B_OFF(); + DecodeReaderReset(DecodeReader); + } + DecodeReader->bitCount = 0; + DecodeReader->shiftReg = 0; + if (DecodeReader->byteCount == DecodeReader->jam_search_len) { + if (!memcmp(DecodeReader->output, DecodeReader->jam_search_string, DecodeReader->jam_search_len)) { + LED_D_ON(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_JAM); + DecodeReader->state = STATE_READER_RECEIVE_JAMMING; + } + } + } else { + DecodeReader->bitCount++; + } + } + break; - case STATE_READER_RECEIVE_DATA_1_OUT_OF_256: - DecodeReader->posCount++; - if (DecodeReader->posCount == 1) { - DecodeReader->sum1 = bit?1:0; - } else if (DecodeReader->posCount <= 4) { - if (bit) DecodeReader->sum1++; - } else if (DecodeReader->posCount == 5) { - DecodeReader->sum2 = bit?1:0; - } else if (bit) { - DecodeReader->sum2++; - } - if (DecodeReader->posCount == 8) { - DecodeReader->posCount = 0; - if (DecodeReader->sum1 <= 1 && DecodeReader->sum2 >= 3) { // EOF - LED_B_OFF(); // Finished receiving - DecodeReaderReset(DecodeReader); - if (DecodeReader->byteCount != 0) { - return true; - } - } else if (DecodeReader->sum1 >= 3 && DecodeReader->sum2 <= 1) { // detected the bit position - DecodeReader->shiftReg = DecodeReader->bitCount; - } - if (DecodeReader->bitCount == 255) { // we have a full byte - DecodeReader->output[DecodeReader->byteCount++] = DecodeReader->shiftReg; - if (DecodeReader->byteCount > DecodeReader->byteCountMax) { - // buffer overflow, give up - LED_B_OFF(); - DecodeReaderReset(DecodeReader); - } - if (DecodeReader->byteCount == DecodeReader->jam_search_len) { - if (!memcmp(DecodeReader->output, DecodeReader->jam_search_string, DecodeReader->jam_search_len)) { - LED_D_ON(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_JAM); - DecodeReader->state = STATE_READER_RECEIVE_JAMMING; - } - } - } - DecodeReader->bitCount++; - } - break; + case STATE_READER_RECEIVE_DATA_1_OUT_OF_256: + DecodeReader->posCount++; + if (DecodeReader->posCount == 1) { + DecodeReader->sum1 = bit?1:0; + } else if (DecodeReader->posCount <= 4) { + if (bit) DecodeReader->sum1++; + } else if (DecodeReader->posCount == 5) { + DecodeReader->sum2 = bit?1:0; + } else if (bit) { + DecodeReader->sum2++; + } + if (DecodeReader->posCount == 8) { + DecodeReader->posCount = 0; + if (DecodeReader->sum1 <= 1 && DecodeReader->sum2 >= 3) { // EOF + LED_B_OFF(); // Finished receiving + DecodeReaderReset(DecodeReader); + if (DecodeReader->byteCount != 0) { + return true; + } + } else if (DecodeReader->sum1 >= 3 && DecodeReader->sum2 <= 1) { // detected the bit position + DecodeReader->shiftReg = DecodeReader->bitCount; + } + if (DecodeReader->bitCount == 255) { // we have a full byte + DecodeReader->output[DecodeReader->byteCount++] = DecodeReader->shiftReg; + if (DecodeReader->byteCount > DecodeReader->byteCountMax) { + // buffer overflow, give up + LED_B_OFF(); + DecodeReaderReset(DecodeReader); + } + if (DecodeReader->byteCount == DecodeReader->jam_search_len) { + if (!memcmp(DecodeReader->output, DecodeReader->jam_search_string, DecodeReader->jam_search_len)) { + LED_D_ON(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_JAM); + DecodeReader->state = STATE_READER_RECEIVE_JAMMING; + } + } + } + DecodeReader->bitCount++; + } + break; - case STATE_READER_RECEIVE_JAMMING: - DecodeReader->posCount++; - if (DecodeReader->Coding == CODING_1_OUT_OF_4) { - if (DecodeReader->posCount == 7*16) { // 7 bits jammed - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); // stop jamming - // FpgaDisableTracing(); - LED_D_OFF(); - } else if (DecodeReader->posCount == 8*16) { - DecodeReader->posCount = 0; - DecodeReader->output[DecodeReader->byteCount++] = 0x00; - DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_4; - } - } else { - if (DecodeReader->posCount == 7*256) { // 7 bits jammend - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); // stop jamming - LED_D_OFF(); - } else if (DecodeReader->posCount == 8*256) { - DecodeReader->posCount = 0; - DecodeReader->output[DecodeReader->byteCount++] = 0x00; - DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_256; - } - } - break; + case STATE_READER_RECEIVE_JAMMING: + DecodeReader->posCount++; + if (DecodeReader->Coding == CODING_1_OUT_OF_4) { + if (DecodeReader->posCount == 7*16) { // 7 bits jammed + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); // stop jamming + // FpgaDisableTracing(); + LED_D_OFF(); + } else if (DecodeReader->posCount == 8*16) { + DecodeReader->posCount = 0; + DecodeReader->output[DecodeReader->byteCount++] = 0x00; + DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_4; + } + } else { + if (DecodeReader->posCount == 7*256) { // 7 bits jammend + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); // stop jamming + LED_D_OFF(); + } else if (DecodeReader->posCount == 8*256) { + DecodeReader->posCount = 0; + DecodeReader->output[DecodeReader->byteCount++] = 0x00; + DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_256; + } + } + break; - default: - LED_B_OFF(); - DecodeReaderReset(DecodeReader); - break; - } + default: + LED_B_OFF(); + DecodeReaderReset(DecodeReader); + break; + } - return false; + return false; } //----------------------------------------------------------------------------- @@ -1006,86 +1014,86 @@ static RAMFUNC int Handle15693SampleFromReader(bool bit, DecodeReader_t *DecodeR //----------------------------------------------------------------------------- int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time) { - int samples = 0; - bool gotFrame = false; - uint8_t b; + int samples = 0; + bool gotFrame = false; + uint8_t b; - uint8_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; + uint8_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; - // the decoder data structure - DecodeReader_t DecodeReader = {0}; - DecodeReaderInit(&DecodeReader, received, max_len, 0, NULL); + // the decoder data structure + DecodeReader_t DecodeReader = {0}; + DecodeReaderInit(&DecodeReader, received, max_len, 0, NULL); - // wait for last transfer to complete - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); - LED_D_OFF(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); - // clear receive register and wait for next transfer - uint32_t temp = AT91C_BASE_SSC->SSC_RHR; - (void) temp; - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) ; + // clear receive register and wait for next transfer + uint32_t temp = AT91C_BASE_SSC->SSC_RHR; + (void) temp; + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) ; - uint32_t dma_start_time = GetCountSspClk() & 0xfffffff8; + uint32_t dma_start_time = GetCountSspClk() & 0xfffffff8; - // Setup and start DMA. - FpgaSetupSscDma(dmaBuf, ISO15693_DMA_BUFFER_SIZE); - uint8_t *upTo = dmaBuf; + // Setup and start DMA. + FpgaSetupSscDma(dmaBuf, ISO15693_DMA_BUFFER_SIZE); + uint8_t *upTo = dmaBuf; - for (;;) { - uint16_t behindBy = ((uint8_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (ISO15693_DMA_BUFFER_SIZE-1); + for (;;) { + uint16_t behindBy = ((uint8_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (ISO15693_DMA_BUFFER_SIZE-1); - if (behindBy == 0) continue; + if (behindBy == 0) continue; - b = *upTo++; - if (upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. - upTo = dmaBuf; // start reading the circular buffer from the beginning - if (behindBy > (9*ISO15693_DMA_BUFFER_SIZE/10)) { - Dbprintf("About to blow circular buffer - aborted! behindBy=%d", behindBy); - break; - } - } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO15693_DMA_BUFFER_SIZE; // DMA Next Counter registers - } + b = *upTo++; + if (upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning + if (behindBy > (9*ISO15693_DMA_BUFFER_SIZE/10)) { + Dbprintf("About to blow circular buffer - aborted! behindBy=%d", behindBy); + break; + } + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO15693_DMA_BUFFER_SIZE; // DMA Next Counter registers + } - for (int i = 7; i >= 0; i--) { - if (Handle15693SampleFromReader((b >> i) & 0x01, &DecodeReader)) { - *eof_time = dma_start_time + samples - DELAY_READER_TO_ARM; // end of EOF - gotFrame = true; - break; - } - samples++; - } + for (int i = 7; i >= 0; i--) { + if (Handle15693SampleFromReader((b >> i) & 0x01, &DecodeReader)) { + *eof_time = dma_start_time + samples - DELAY_READER_TO_ARM; // end of EOF + gotFrame = true; + break; + } + samples++; + } - if (gotFrame) { - break; - } + if (gotFrame) { + break; + } - if (BUTTON_PRESS()) { - DecodeReader.byteCount = -1; - break; - } + if (BUTTON_PRESS()) { + DecodeReader.byteCount = -1; + break; + } - WDT_HIT(); - } + WDT_HIT(); + } - FpgaDisableSscDma(); + FpgaDisableSscDma(); - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("samples = %d, gotFrame = %d, Decoder: state = %d, len = %d, bitCount = %d, posCount = %d", - samples, gotFrame, DecodeReader.state, DecodeReader.byteCount, DecodeReader.bitCount, DecodeReader.posCount); + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("samples = %d, gotFrame = %d, Decoder: state = %d, len = %d, bitCount = %d, posCount = %d", + samples, gotFrame, DecodeReader.state, DecodeReader.byteCount, DecodeReader.bitCount, DecodeReader.posCount); - if (DecodeReader.byteCount > 0) { - uint32_t sof_time = *eof_time - - DecodeReader.byteCount * (DecodeReader.Coding==CODING_1_OUT_OF_4?128:2048) // time for byte transfers - - 32 // time for SOF transfer - - 16; // time for EOF transfer - LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time*32, *eof_time*32, NULL, true); - } + if (DecodeReader.byteCount > 0) { + uint32_t sof_time = *eof_time + - DecodeReader.byteCount * (DecodeReader.Coding==CODING_1_OUT_OF_4?128:2048) // time for byte transfers + - 32 // time for SOF transfer + - 16; // time for EOF transfer + LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time*32, *eof_time*32, NULL, true); + } - return DecodeReader.byteCount; + return DecodeReader.byteCount; } //----------------------------------------------------------------------------- @@ -1094,214 +1102,211 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo // so that it can be downloaded to a PC and processed there. //----------------------------------------------------------------------------- void AcquireRawAdcSamplesIso15693(void) { - LED_A_ON(); + LED_A_ON(); + uint8_t *dest = BigBuf_malloc(4000); - //iceman: needs malloc - uint8_t *dest = BigBuf_get_addr(); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); + LED_D_ON(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); - LED_D_ON(); - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + uint8_t cmd[5]; + BuildIdentifyRequest(cmd); + CodeIso15693AsReader(cmd, sizeof(cmd)); - uint8_t cmd[5]; - BuildIdentifyRequest(cmd); - CodeIso15693AsReader(cmd, sizeof(cmd)); + // Give the tags time to energize + SpinDelay(100); - // Give the tags time to energize - SpinDelay(100); + // Now send the command + uint32_t start_time = 0; + TransmitTo15693Tag(ToSend, ToSendMax, &start_time); - // Now send the command - uint32_t start_time = 0; - TransmitTo15693Tag(ToSend, ToSendMax, &start_time); + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) ; - // wait for last transfer to complete - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) ; + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_424_KHZ | FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_424_KHZ | FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE); + for(int c = 0; c < 4000; ) { + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + uint16_t r = AT91C_BASE_SSC->SSC_RHR; + dest[c++] = r >> 5; + } + } - for(int c = 0; c < 4000; ) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - uint16_t r = AT91C_BASE_SSC->SSC_RHR; - dest[c++] = r >> 5; - } - } - - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); } void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { - LED_A_ON(); + LED_A_ON(); - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - clear_trace(); - set_tracing(true); + clear_trace(); + set_tracing(true); - // The DMA buffer, used to stream samples from the FPGA - uint16_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; + // The DMA buffer, used to stream samples from the FPGA + uint16_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; - // Count of samples received so far, so that we can include timing - // information in the trace buffer. - int samples = 0; + // Count of samples received so far, so that we can include timing + // information in the trace buffer. + int samples = 0; - DecodeTag_t DecodeTag = {0}; - uint8_t response[ISO15693_MAX_RESPONSE_LENGTH]; - DecodeTagInit(&DecodeTag, response, sizeof(response)); + DecodeTag_t DecodeTag = {0}; + uint8_t response[ISO15693_MAX_RESPONSE_LENGTH]; + DecodeTagInit(&DecodeTag, response, sizeof(response)); - DecodeReader_t DecodeReader = {0}; - uint8_t cmd[ISO15693_MAX_COMMAND_LENGTH]; - DecodeReaderInit(&DecodeReader, cmd, sizeof(cmd), jam_search_len, jam_search_string); + DecodeReader_t DecodeReader = {0}; + uint8_t cmd[ISO15693_MAX_COMMAND_LENGTH]; + DecodeReaderInit(&DecodeReader, cmd, sizeof(cmd), jam_search_len, jam_search_string); - // Print some debug information about the buffer sizes - if (DBGLEVEL >= DBG_EXTENDED) { - Dbprintf("Sniffing buffers initialized:"); - Dbprintf(" Trace: %i bytes", BigBuf_max_traceLen()); - Dbprintf(" Reader -> tag: %i bytes", ISO15693_MAX_COMMAND_LENGTH); - Dbprintf(" tag -> Reader: %i bytes", ISO15693_MAX_RESPONSE_LENGTH); - Dbprintf(" DMA: %i bytes", ISO15693_DMA_BUFFER_SIZE * sizeof(uint16_t)); - } + // Print some debug information about the buffer sizes + if (DBGLEVEL >= DBG_EXTENDED) { + Dbprintf("Sniffing buffers initialized:"); + Dbprintf(" Trace: %i bytes", BigBuf_max_traceLen()); + Dbprintf(" Reader -> tag: %i bytes", ISO15693_MAX_COMMAND_LENGTH); + Dbprintf(" tag -> Reader: %i bytes", ISO15693_MAX_RESPONSE_LENGTH); + Dbprintf(" DMA: %i bytes", ISO15693_DMA_BUFFER_SIZE * sizeof(uint16_t)); + } - Dbprintf("Sniff started. Press PM3 Button to stop."); + Dbprintf("Sniff started. Press PM3 Button to stop."); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); - LED_D_OFF(); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); - StartCountSspClk(); - FpgaSetupSscDma((uint8_t*) dmaBuf, ISO15693_DMA_BUFFER_SIZE); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); + LED_D_OFF(); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + StartCountSspClk(); + FpgaSetupSscDma((uint8_t*) dmaBuf, ISO15693_DMA_BUFFER_SIZE); - bool TagIsActive = false; - bool ReaderIsActive = false; - bool ExpectTagAnswer = false; - uint32_t dma_start_time = 0; - uint16_t *upTo = dmaBuf; + bool TagIsActive = false; + bool ReaderIsActive = false; + bool ExpectTagAnswer = false; + uint32_t dma_start_time = 0; + uint16_t *upTo = dmaBuf; - uint16_t max_behindBy = 0; - - // And now we loop, receiving samples. - for(;;) { - uint16_t behindBy = ((uint16_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (ISO15693_DMA_BUFFER_SIZE-1); - if (behindBy > max_behindBy) { - max_behindBy = behindBy; - } - - if (behindBy == 0) continue; + uint16_t max_behindBy = 0; + + // And now we loop, receiving samples. + for(;;) { + uint16_t behindBy = ((uint16_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (ISO15693_DMA_BUFFER_SIZE-1); + if (behindBy > max_behindBy) { + max_behindBy = behindBy; + } + + if (behindBy == 0) continue; - samples++; - if (samples == 1) { - // DMA has transferred the very first data - dma_start_time = GetCountSspClk() & 0xfffffff0; - } + samples++; + if (samples == 1) { + // DMA has transferred the very first data + dma_start_time = GetCountSspClk() & 0xfffffff0; + } - uint16_t sniffdata = *upTo++; + uint16_t sniffdata = *upTo++; - if (upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. - upTo = dmaBuf; // start reading the circular buffer from the beginning - if (behindBy > (9*ISO15693_DMA_BUFFER_SIZE/10)) { + if (upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning + if (behindBy > (9*ISO15693_DMA_BUFFER_SIZE/10)) { - Dbprintf("About to blow circular buffer - aborted! behindBy=%d, samples=%d", behindBy, samples); - break; - } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO15693_DMA_BUFFER_SIZE; // DMA Next Counter registers - WDT_HIT(); - if (BUTTON_PRESS()) { - DbpString("Sniff stopped."); - break; - } - } - } + Dbprintf("About to blow circular buffer - aborted! behindBy=%d, samples=%d", behindBy, samples); + break; + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO15693_DMA_BUFFER_SIZE; // DMA Next Counter registers + WDT_HIT(); + if (BUTTON_PRESS()) { + DbpString("Sniff stopped."); + break; + } + } + } - if (!TagIsActive) { // no need to try decoding reader data if the tag is sending - if (Handle15693SampleFromReader(sniffdata & 0x02, &DecodeReader)) { + if (!TagIsActive) { // no need to try decoding reader data if the tag is sending + if (Handle15693SampleFromReader(sniffdata & 0x02, &DecodeReader)) { - uint32_t eof_time = dma_start_time + samples*16 + 8 - DELAY_READER_TO_ARM_SNIFF; // end of EOF - if (DecodeReader.byteCount > 0) { - uint32_t sof_time = eof_time - - DecodeReader.byteCount * (DecodeReader.Coding==CODING_1_OUT_OF_4?128*16:2048*16) // time for byte transfers - - 32*16 // time for SOF transfer - - 16*16; // time for EOF transfer - LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time*4, eof_time*4, NULL, true); - } - // And ready to receive another command. - DecodeReaderReset(&DecodeReader); - // And also reset the demod code, which might have been - // false-triggered by the commands from the reader. - DecodeTagReset(&DecodeTag); - ReaderIsActive = false; - ExpectTagAnswer = true; + uint32_t eof_time = dma_start_time + samples*16 + 8 - DELAY_READER_TO_ARM_SNIFF; // end of EOF + if (DecodeReader.byteCount > 0) { + uint32_t sof_time = eof_time + - DecodeReader.byteCount * (DecodeReader.Coding==CODING_1_OUT_OF_4?128*16:2048*16) // time for byte transfers + - 32*16 // time for SOF transfer + - 16*16; // time for EOF transfer + LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time*4, eof_time*4, NULL, true); + } + // And ready to receive another command. + DecodeReaderReset(&DecodeReader); + // And also reset the demod code, which might have been + // false-triggered by the commands from the reader. + DecodeTagReset(&DecodeTag); + ReaderIsActive = false; + ExpectTagAnswer = true; - } else if (Handle15693SampleFromReader(sniffdata & 0x01, &DecodeReader)) { + } else if (Handle15693SampleFromReader(sniffdata & 0x01, &DecodeReader)) { - uint32_t eof_time = dma_start_time + samples*16 + 16 - DELAY_READER_TO_ARM_SNIFF; // end of EOF - if (DecodeReader.byteCount > 0) { - uint32_t sof_time = eof_time - - DecodeReader.byteCount * (DecodeReader.Coding==CODING_1_OUT_OF_4?128*16:2048*16) // time for byte transfers - - 32*16 // time for SOF transfer - - 16*16; // time for EOF transfer - LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time*4, eof_time*4, NULL, true); - } - // And ready to receive another command - DecodeReaderReset(&DecodeReader); + uint32_t eof_time = dma_start_time + samples*16 + 16 - DELAY_READER_TO_ARM_SNIFF; // end of EOF + if (DecodeReader.byteCount > 0) { + uint32_t sof_time = eof_time + - DecodeReader.byteCount * (DecodeReader.Coding==CODING_1_OUT_OF_4?128*16:2048*16) // time for byte transfers + - 32*16 // time for SOF transfer + - 16*16; // time for EOF transfer + LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time*4, eof_time*4, NULL, true); + } + // And ready to receive another command + DecodeReaderReset(&DecodeReader); - // And also reset the demod code, which might have been - // false-triggered by the commands from the reader. - DecodeTagReset(&DecodeTag); - ReaderIsActive = false; - ExpectTagAnswer = true; + // And also reset the demod code, which might have been + // false-triggered by the commands from the reader. + DecodeTagReset(&DecodeTag); + ReaderIsActive = false; + ExpectTagAnswer = true; - } else { - ReaderIsActive = (DecodeReader.state >= STATE_READER_RECEIVE_DATA_1_OUT_OF_4); - } - } + } else { + ReaderIsActive = (DecodeReader.state >= STATE_READER_RECEIVE_DATA_1_OUT_OF_4); + } + } - if (!ReaderIsActive && ExpectTagAnswer) { // no need to try decoding tag data if the reader is currently sending or no answer expected yet - if (Handle15693SamplesFromTag(sniffdata >> 2, &DecodeTag)) { + if (!ReaderIsActive && ExpectTagAnswer) { // no need to try decoding tag data if the reader is currently sending or no answer expected yet + if (Handle15693SamplesFromTag(sniffdata >> 2, &DecodeTag)) { - uint32_t eof_time = dma_start_time + samples*16 - DELAY_TAG_TO_ARM_SNIFF; // end of EOF - if (DecodeTag.lastBit == SOF_PART2) { - eof_time -= 8*16; // needed 8 additional samples to confirm single SOF (iCLASS) - } - uint32_t sof_time = eof_time - - DecodeTag.len * 8 * 8 * 16 // time for byte transfers - - 32 * 16 // time for SOF transfer - - (DecodeTag.lastBit != SOF_PART2?32*16:0); // time for EOF transfer - LogTrace(DecodeTag.output, DecodeTag.len, sof_time*4, eof_time*4, NULL, false); - // And ready to receive another response. - DecodeTagReset(&DecodeTag); - DecodeReaderReset(&DecodeReader); - ExpectTagAnswer = false; - TagIsActive = false; - } else { - TagIsActive = (DecodeTag.state >= STATE_TAG_RECEIVING_DATA); - } - } + uint32_t eof_time = dma_start_time + samples*16 - DELAY_TAG_TO_ARM_SNIFF; // end of EOF + if (DecodeTag.lastBit == SOF_PART2) { + eof_time -= 8*16; // needed 8 additional samples to confirm single SOF (iCLASS) + } + uint32_t sof_time = eof_time + - DecodeTag.len * 8 * 8 * 16 // time for byte transfers + - 32 * 16 // time for SOF transfer + - (DecodeTag.lastBit != SOF_PART2?32*16:0); // time for EOF transfer + LogTrace(DecodeTag.output, DecodeTag.len, sof_time*4, eof_time*4, NULL, false); + // And ready to receive another response. + DecodeTagReset(&DecodeTag); + DecodeReaderReset(&DecodeReader); + ExpectTagAnswer = false; + TagIsActive = false; + } else { + TagIsActive = (DecodeTag.state >= STATE_TAG_RECEIVING_DATA); + } + } - } + } - FpgaDisableSscDma(); + FpgaDisableSscDma(); - DbpString("Sniff statistics:"); - Dbprintf(" ExpectTagAnswer: %d, TagIsActive: %d, ReaderIsActive: %d", ExpectTagAnswer, TagIsActive, ReaderIsActive); - Dbprintf(" DecodeTag State: %d", DecodeTag.state); - Dbprintf(" DecodeTag byteCnt: %d", DecodeTag.len); - Dbprintf(" DecodeTag posCount: %d", DecodeTag.posCount); - Dbprintf(" DecodeReader State: %d", DecodeReader.state); - Dbprintf(" DecodeReader byteCnt: %d", DecodeReader.byteCount); - Dbprintf(" DecodeReader posCount: %d", DecodeReader.posCount); - Dbprintf(" Trace length: %d", BigBuf_get_traceLen()); - Dbprintf(" Max behindBy: %d", max_behindBy); + DbpString("Sniff statistics:"); + Dbprintf(" ExpectTagAnswer: %d, TagIsActive: %d, ReaderIsActive: %d", ExpectTagAnswer, TagIsActive, ReaderIsActive); + Dbprintf(" DecodeTag State: %d", DecodeTag.state); + Dbprintf(" DecodeTag byteCnt: %d", DecodeTag.len); + Dbprintf(" DecodeTag posCount: %d", DecodeTag.posCount); + Dbprintf(" DecodeReader State: %d", DecodeReader.state); + Dbprintf(" DecodeReader byteCnt: %d", DecodeReader.byteCount); + Dbprintf(" DecodeReader posCount: %d", DecodeReader.posCount); + Dbprintf(" Trace length: %d", BigBuf_get_traceLen()); + Dbprintf(" Max behindBy: %d", max_behindBy); } -// Initialize the proxmark as iso15k reader -// (this might produces glitches that confuse some tags +// Initialize Proxmark3 as ISO15693 reader void Iso15693InitReader(void) { - + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Start from off (no field generated) @@ -1309,16 +1314,20 @@ void Iso15693InitReader(void) { LEDsoff(); SpinDelay(10); - // switch field on - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); - LED_D_ON(); - - // initialize SSC and select proper AD input - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + // switch field on + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); + LED_D_ON(); + + // initialize SSC and select proper AD input + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - // give tags some time to energize - SpinDelay(250); + // give tags some time to energize + SpinDelay(250); + + set_tracing(true); + + StartCountSspClk(); } /////////////////////////////////////////////////////////////////////// @@ -1333,10 +1342,10 @@ void Iso15693InitReader(void) { static void BuildIdentifyRequest(uint8_t *cmd) { // flags cmd[0] = ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1; - // inventory command code - cmd[1] = ISO15_CMD_INVENTORY; - // no mask - cmd[2] = 0x00; + // inventory command code + cmd[1] = ISO15_CMD_INVENTORY; + // no mask + cmd[2] = 0x00; // CRC AddCrc15(cmd, 3); } @@ -1376,49 +1385,41 @@ static void BuildInventoryResponse(uint8_t *uid) { int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time) { - if (init) { - Iso15693InitReader(); - StartCountSspClk(); - } + if (init) { + Iso15693InitReader(); + } - int answerLen = 0; + if (speed_fast) { + // high speed (1 out of 4) + CodeIso15693AsReader(send, sendlen); + } else { + // low speed (1 out of 256) + CodeIso15693AsReader256(send, sendlen); + } - if (speed_fast) { - // high speed (1 out of 4) - CodeIso15693AsReader(send, sendlen); - } else { - // low speed (1 out of 256) - CodeIso15693AsReader256(send, sendlen); - } + TransmitTo15693Tag(ToSend, ToSendMax, &start_time); + uint32_t end_time = start_time + 32 * (8 * ToSendMax -4); // substract the 4 padding bits after EOF + LogTrace(send, sendlen, (start_time * 4), (end_time * 4), NULL, true); - TransmitTo15693Tag(ToSend, ToSendMax, &start_time); - uint32_t end_time = start_time + 32*(8*ToSendMax-4); // substract the 4 padding bits after EOF - LogTrace(send, sendlen, start_time*4, end_time*4, NULL, true); - - // Now wait for a response - if (recv != NULL) { - answerLen = GetIso15693AnswerFromTag(recv, max_recv_len, timeout, eof_time); - } - - return answerLen; + int res = 0; + if (recv != NULL) { + res = GetIso15693AnswerFromTag(recv, max_recv_len, timeout, eof_time); + } + return res; } int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time) { - int answerLen = 0; + CodeIso15693AsReaderEOF(); + TransmitTo15693Tag(ToSend, ToSendMax, &start_time); + uint32_t end_time = start_time + 32 * (8 * ToSendMax - 4); // substract the 4 padding bits after EOF + LogTrace(NULL, 0, (start_time * 4), (end_time * 4), NULL, true); - CodeIso15693AsReaderEOF(); - - TransmitTo15693Tag(ToSend, ToSendMax, &start_time); - uint32_t end_time = start_time + 32*(8*ToSendMax-4); // substract the 4 padding bits after EOF - LogTrace(NULL, 0, start_time*4, end_time*4, NULL, true); - - // Now wait for a response - if (recv != NULL) { - answerLen = GetIso15693AnswerFromTag(recv, max_recv_len, timeout, eof_time); - } - - return answerLen; + int res = 0; + if (recv != NULL) { + res = GetIso15693AnswerFromTag(recv, max_recv_len, timeout, eof_time); + } + return res; } // -------------------------------------------------------------------- @@ -1496,22 +1497,22 @@ static void DbdecodeIso15693Answer(int len, uint8_t *d) { // parameter is unused !?! void ReaderIso15693(uint32_t parameter) { - LED_A_ON(); - set_tracing(true); + LED_A_ON(); + set_tracing(true); - uint8_t *answer = BigBuf_malloc(ISO15693_MAX_RESPONSE_LENGTH); + uint8_t *answer = BigBuf_malloc(ISO15693_MAX_RESPONSE_LENGTH); memset(answer, 0x00, ISO15693_MAX_RESPONSE_LENGTH); - // FIRST WE RUN AN INVENTORY TO GET THE TAG UID - // THIS MEANS WE CAN PRE-BUILD REQUESTS TO SAVE CPU TIME + // FIRST WE RUN AN INVENTORY TO GET THE TAG UID + // THIS MEANS WE CAN PRE-BUILD REQUESTS TO SAVE CPU TIME - // Send the IDENTIFY command - uint8_t cmd[5] = {0}; - BuildIdentifyRequest(cmd); - uint32_t start_time = 0; - uint32_t eof_time; - int answerLen = SendDataTag(cmd, sizeof(cmd), true, true, answer, ISO15693_MAX_RESPONSE_LENGTH, start_time, ISO15693_READER_TIMEOUT, &eof_time); - start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + // Send the IDENTIFY command + uint8_t cmd[5] = {0}; + BuildIdentifyRequest(cmd); + uint32_t start_time = 0; + uint32_t eof_time; + int answerLen = SendDataTag(cmd, sizeof(cmd), true, true, answer, ISO15693_MAX_RESPONSE_LENGTH, start_time, ISO15693_READER_TIMEOUT, &eof_time); + start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; // we should do a better check than this if (answerLen >= 12) { @@ -1551,19 +1552,30 @@ void ReaderIso15693(uint32_t parameter) { // When SIM: initialize the Proxmark3 as ISO15693 tag void Iso15693InitTag(void) { - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); - StartCountSspClk(); + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + // Start from off (no field generated) + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + SpinDelay(10); + + // switch simulation FPGA + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); + + // initialize SSC and select proper AD input + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + // turn on clock + StartCountSspClk(); } // Simulate an ISO15693 TAG, perform anti-collision and then print any reader commands // all demodulation performed in arm rather than host. - greg void SimTagIso15693(uint8_t *uid) { - LEDsoff(); - Iso15693InitTag(); + Iso15693InitTag(); LED_A_ON(); @@ -1582,21 +1594,21 @@ void SimTagIso15693(uint8_t *uid) { WDT_HIT(); // Listen to reader - uint8_t cmd[ISO15693_MAX_COMMAND_LENGTH]; - uint32_t eof_time = 0, start_time = 0; - int cmd_len = GetIso15693CommandFromReader(cmd, sizeof(cmd), &eof_time); + uint8_t cmd[ISO15693_MAX_COMMAND_LENGTH]; + uint32_t eof_time = 0, start_time = 0; + int cmd_len = GetIso15693CommandFromReader(cmd, sizeof(cmd), &eof_time); - if ((cmd_len >= 5) && (cmd[0] & ISO15_REQ_INVENTORY) && (cmd[1] == ISO15_CMD_INVENTORY)) { // TODO: check more flags - bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH); - start_time = eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM; - TransmitTo15693Reader(ToSend, ToSendMax, &start_time, 0, slow); - } + if ((cmd_len >= 5) && (cmd[0] & ISO15_REQ_INVENTORY) && (cmd[1] == ISO15_CMD_INVENTORY)) { // TODO: check more flags + bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH); + start_time = eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM; + TransmitTo15693Reader(ToSend, ToSendMax, &start_time, 0, slow); + } if (DBGLEVEL >= DBG_EXTENDED) { Dbprintf(" %d bytes read from reader:", cmd_len); Dbhexdump(cmd_len, cmd, false); } - } + } switch_off(); } @@ -1619,9 +1631,9 @@ void BruteforceIso15693Afi(uint32_t speed) { int datalen = 5; uint32_t eof_time = 0; - uint32_t start_time = GetCountSspClk(); - int recvlen = SendDataTag(data, datalen, true, speed, recv, sizeof(recv), 0, ISO15693_READER_TIMEOUT, &eof_time); - start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + uint32_t start_time = GetCountSspClk(); + int recvlen = SendDataTag(data, datalen, true, speed, recv, sizeof(recv), 0, ISO15693_READER_TIMEOUT, &eof_time); + start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; WDT_HIT(); @@ -1643,8 +1655,8 @@ void BruteforceIso15693Afi(uint32_t speed) { data[2] = i & 0xFF; AddCrc15(data, 4); - recvlen = SendDataTag(data, datalen, false, speed, recv, sizeof(recv), start_time, ISO15693_READER_TIMEOUT, &eof_time); - start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + recvlen = SendDataTag(data, datalen, false, speed, recv, sizeof(recv), start_time, ISO15693_READER_TIMEOUT, &eof_time); + start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; WDT_HIT(); @@ -1674,63 +1686,60 @@ void BruteforceIso15693Afi(uint32_t speed) { // OBS: doesn't turn off rf field afterwards. void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint8_t *data) { - LED_A_ON(); + LED_A_ON(); - int recvlen = 0; - uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; - uint32_t eof_time; - uint16_t timeout; + int recvlen = 0; + uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; + uint32_t eof_time; + uint16_t timeout; bool request_answer = false; - - switch (data[1]) { - case ISO15_CMD_WRITE: - case ISO15_CMD_LOCK: - case ISO15_CMD_WRITEMULTI: - case ISO15_CMD_WRITEAFI: - case ISO15_CMD_LOCKAFI: - case ISO15_CMD_WRITEDSFID: - case ISO15_CMD_LOCKDSFID: - timeout = ISO15693_READER_TIMEOUT_WRITE; - request_answer = data[0] & ISO15_REQ_OPTION; - break; - default: - timeout = ISO15693_READER_TIMEOUT; - } + + switch (data[1]) { + case ISO15_CMD_WRITE: + case ISO15_CMD_LOCK: + case ISO15_CMD_WRITEMULTI: + case ISO15_CMD_WRITEAFI: + case ISO15_CMD_LOCKAFI: + case ISO15_CMD_WRITEDSFID: + case ISO15_CMD_LOCKDSFID: + timeout = ISO15693_READER_TIMEOUT_WRITE; + request_answer = data[0] & ISO15_REQ_OPTION; + break; + default: + timeout = ISO15693_READER_TIMEOUT; + } - if (DBGLEVEL >= DBG_EXTENDED) { - Dbprintf("SEND:"); - Dbhexdump(datalen, data, false); - } + if (DBGLEVEL >= DBG_EXTENDED) { + Dbprintf("SEND:"); + Dbhexdump(datalen, data, false); + } - recvlen = SendDataTag(data, datalen, true, speed, (recv ? recvbuf : NULL), sizeof(recvbuf), 0, timeout, &eof_time); + recvlen = SendDataTag(data, datalen, true, speed, (recv ? recvbuf : NULL), sizeof(recvbuf), 0, timeout, &eof_time); // send a single EOF to get the tag response - if (request_answer) { - recvlen = SendDataTagEOF((recv ? recvbuf : NULL), sizeof(recvbuf), 0, ISO15693_READER_TIMEOUT, &eof_time); - } - - // for the time being, switch field off to protect rdv4.0 - // note: this prevents using hf 15 cmd with s option - which isn't implemented yet anyway - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LED_D_OFF(); + if (request_answer) { + recvlen = SendDataTagEOF((recv ? recvbuf : NULL), sizeof(recvbuf), 0, ISO15693_READER_TIMEOUT, &eof_time); + } + + // for the time being, switch field off to protect rdv4.0 + // note: this prevents using hf 15 cmd with s option - which isn't implemented yet anyway + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); if (recv) { - if (recvlen > ISO15693_MAX_RESPONSE_LENGTH) { - recvlen = ISO15693_MAX_RESPONSE_LENGTH; - } - reply_mix(CMD_ACK, recvlen, 0, 0, recvbuf, ISO15693_MAX_RESPONSE_LENGTH); - - - if (DBGLEVEL >= DBG_EXTENDED) { - Dbprintf("RECV:"); - if (recvlen > 0) { - Dbhexdump(recvlen, recvbuf, false); - DbdecodeIso15693Answer(recvlen, recvbuf); - } - } - + if (recvlen > ISO15693_MAX_RESPONSE_LENGTH) { + recvlen = ISO15693_MAX_RESPONSE_LENGTH; + } + reply_mix(CMD_ACK, recvlen, 0, 0, recvbuf, ISO15693_MAX_RESPONSE_LENGTH); + if (DBGLEVEL >= DBG_EXTENDED) { + Dbprintf("RECV:"); + if (recvlen > 0) { + Dbhexdump(recvlen, recvbuf, false); + DbdecodeIso15693Answer(recvlen, recvbuf); + } + } } else { reply_mix(CMD_ACK, 1, 0, 0, 0, 0); } @@ -1745,55 +1754,55 @@ void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint // Set the UID on Magic ISO15693 tag (based on Iceman's LUA-script). void SetTag15693Uid(uint8_t *uid) { - LED_A_ON(); + LED_A_ON(); - uint8_t cmd[4][9] = { - {ISO15_REQ_DATARATE_HIGH, ISO15_CMD_WRITE, 0x3e, 0x00, 0x00, 0x00, 0x00}, - {ISO15_REQ_DATARATE_HIGH, ISO15_CMD_WRITE, 0x3f, 0x69, 0x96, 0x00, 0x00}, - {ISO15_REQ_DATARATE_HIGH, ISO15_CMD_WRITE, 0x38}, - {ISO15_REQ_DATARATE_HIGH, ISO15_CMD_WRITE, 0x39} - }; + uint8_t cmd[4][9] = { + {ISO15_REQ_DATARATE_HIGH, ISO15_CMD_WRITE, 0x3e, 0x00, 0x00, 0x00, 0x00}, + {ISO15_REQ_DATARATE_HIGH, ISO15_CMD_WRITE, 0x3f, 0x69, 0x96, 0x00, 0x00}, + {ISO15_REQ_DATARATE_HIGH, ISO15_CMD_WRITE, 0x38}, + {ISO15_REQ_DATARATE_HIGH, ISO15_CMD_WRITE, 0x39} + }; - int recvlen = 0; - uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; - uint32_t eof_time; + int recvlen = 0; + uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; + uint32_t eof_time; - // Command 3 : 022138u8u7u6u5 (where uX = uid byte X) - cmd[2][3] = uid[7]; - cmd[2][4] = uid[6]; - cmd[2][5] = uid[5]; - cmd[2][6] = uid[4]; + // Command 3 : 022138u8u7u6u5 (where uX = uid byte X) + cmd[2][3] = uid[7]; + cmd[2][4] = uid[6]; + cmd[2][5] = uid[5]; + cmd[2][6] = uid[4]; - // Command 4 : 022139u4u3u2u1 (where uX = uid byte X) - cmd[3][3] = uid[3]; - cmd[3][4] = uid[2]; - cmd[3][5] = uid[1]; - cmd[3][6] = uid[0]; + // Command 4 : 022139u4u3u2u1 (where uX = uid byte X) + cmd[3][3] = uid[3]; + cmd[3][4] = uid[2]; + cmd[3][5] = uid[1]; + cmd[3][6] = uid[0]; AddCrc15(cmd[0], 7); AddCrc15(cmd[1], 7); AddCrc15(cmd[2], 7); AddCrc15(cmd[3], 7); - uint32_t start_time = 0; - - for (int i = 0; i < 4; i++) { - - recvlen = SendDataTag(cmd[i], sizeof(cmd[i]), i == 0 ? true : false, true, recvbuf, sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT_WRITE, &eof_time); - start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + uint32_t start_time = 0; + + for (int i = 0; i < 4; i++) { + + recvlen = SendDataTag(cmd[i], sizeof(cmd[i]), i == 0 ? true : false, true, recvbuf, sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT_WRITE, &eof_time); + start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; - if (DBGLEVEL >= DBG_EXTENDED) { - Dbprintf("SEND:"); - Dbhexdump(sizeof(cmd[i]), cmd[i], false); - Dbprintf("RECV:"); - if (recvlen > 0) { - Dbhexdump(recvlen, recvbuf, false); - DbdecodeIso15693Answer(recvlen, recvbuf); - } - } - } + if (DBGLEVEL >= DBG_EXTENDED) { + Dbprintf("SEND:"); + Dbhexdump(sizeof(cmd[i]), cmd[i], false); + Dbprintf("RECV:"); + if (recvlen > 0) { + Dbhexdump(recvlen, recvbuf, false); + DbdecodeIso15693Answer(recvlen, recvbuf); + } + } + } - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); - reply_mix(CMD_ACK, recvlen, 0, 0, recvbuf, recvlen); + reply_mix(CMD_ACK, recvlen, 0, 0, recvbuf, recvlen); } From a0d23f586eb6147dfc141bd968217bf3b6216c35 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 9 Jul 2020 19:42:04 +0200 Subject: [PATCH 037/139] fpga --- armsrc/optimized_cipher.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/armsrc/optimized_cipher.c b/armsrc/optimized_cipher.c index 0f762a8d6..24bb8d7ee 100644 --- a/armsrc/optimized_cipher.c +++ b/armsrc/optimized_cipher.c @@ -245,7 +245,6 @@ void opt_doReaderMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]) { uint8_t dest [] = {0, 0, 0, 0, 0, 0, 0, 0}; opt_MAC(div_key_p, cc_nr_p, dest); memcpy(mac, dest, 4); - return; } void opt_doTagMAC(uint8_t *cc_p, const uint8_t *div_key_p, uint8_t mac[4]) { @@ -257,7 +256,6 @@ void opt_doTagMAC(uint8_t *cc_p, const uint8_t *div_key_p, uint8_t mac[4]) { }; opt_suc(div_key_p, &_init, cc_p, 12, true); opt_output(div_key_p, &_init, mac); - return; } /** @@ -291,5 +289,4 @@ State opt_doTagMAC_1(uint8_t *cc_p, const uint8_t *div_key_p) { void opt_doTagMAC_2(State _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p) { opt_suc(div_key_p, &_init, nr, 4, true); opt_output(div_key_p, &_init, mac); - return; } From b627d91fc2fc18d1f435a155989b69cc50f43f3c Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 9 Jul 2020 19:42:34 +0200 Subject: [PATCH 038/139] adapt --- client/src/cmdhficlass.c | 41 +++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index f11d65945..1eda5678b 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -1121,36 +1121,38 @@ static void Calc_wb_mac(uint8_t blockno, uint8_t *data, uint8_t *div_key, uint8_ static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool use_credit_key, bool verbose) { PacketResponseNG resp; - uint8_t flags = FLAG_ICLASS_READER_ONLY_ONCE; + uint8_t flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE); if (use_credit_key) flags |= FLAG_ICLASS_READER_CEDITKEY; clearCommandBuffer(); SendCommandMIX(CMD_HF_ICLASS_READER, flags, 0, 0, NULL, 0); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { PrintAndLogEx(WARNING, "command execute timeout"); return false; } uint8_t isOK = resp.oldarg[0] & 0xff; - uint8_t *data = resp.data.asBytes; - memcpy(CSN, data, 8); - - if (CCNR != NULL) - memcpy(CCNR, data + 16, 8); - - if (isOK > 0 && verbose) { - PrintAndLogEx(SUCCESS, "CSN | %s", sprint_hex(CSN, 8)); - PrintAndLogEx(SUCCESS, "CCNR | %s", sprint_hex(CCNR, 8)); + if (isOK == 0xFF) { + if (verbose) { + PrintAndLogEx(FAILED, "failed tag-select, aborting... (%d)", isOK); + } + return false; } - if (isOK <= 1) { - if (verbose) - PrintAndLogEx(FAILED, "failed to obtain CC! Tag-select is aborting... (%d)", isOK); + picopass_hdr *hdr = (picopass_hdr *)resp.data.asBytes; - return false; + if (CSN != NULL) + memcpy(CSN, hdr->csn, 8); + + if (CCNR != NULL) + memcpy(CCNR, hdr->epurse, 8); + + if (verbose) { + PrintAndLogEx(SUCCESS, "CSN %s", sprint_hex(CSN, 8)); + PrintAndLogEx(SUCCESS, "epurse %s", sprint_hex(CCNR, 8)); } return true; } @@ -1159,12 +1161,12 @@ static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool u uint8_t CSN[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - if (!select_only(CSN, CCNR, use_credit_key, verbose)) { + if (select_only(CSN, CCNR, use_credit_key, verbose) == false) { if (verbose) PrintAndLogEx(FAILED, "selecting tag failed"); - // DropField(); return false; } + //get div_key if (rawkey) memcpy(div_key, KEY, 8); @@ -1177,7 +1179,6 @@ static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool u PacketResponseNG resp; clearCommandBuffer(); - SendCommandNG(CMD_HF_ICLASS_AUTH, MAC, 4); if (WaitForResponseTimeout(CMD_HF_ICLASS_AUTH, &resp, 2000) == 0) { if (verbose) PrintAndLogEx(WARNING, "Command execute timeout"); @@ -2967,10 +2968,12 @@ int readIclass(bool loop, bool verbose) { SendCommandMIX(CMD_HF_ICLASS_READER, flags, 0, 0, NULL, 0); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 4500)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { uint8_t readStatus = resp.oldarg[0] & 0xff; + PrintAndLogEx(NORMAL, "ICE: %x", readStatus); + // no tag found or button pressed if ((readStatus == 0 && !loop) || readStatus == 0xFF) { DropField(); From 1feb34617be5ef590234be72beaf275fd55f34e6 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 9 Jul 2020 19:43:00 +0200 Subject: [PATCH 039/139] hunting down why logtrace is missing --- client/src/cmdtrace.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index 1b172bfb3..81840dfc1 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -181,7 +181,10 @@ static uint16_t printHexLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trac static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, uint8_t protocol, bool showWaitCycles, bool markCRCBytes) { // sanity check - if (is_last_record(tracepos, traceLen)) return traceLen; + if (is_last_record(tracepos, traceLen)) { + PrintAndLogEx(DEBUG, "last record triggered. t-pos: %u t-len %u", tracepos, traceLen); + return traceLen; + } uint32_t duration; uint16_t data_len; From a25a5322dd91e498c87794ed661be8969660ea24 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 10 Jul 2020 16:37:56 +0200 Subject: [PATCH 040/139] fpga: added iCLASS on deviceside --- armsrc/BigBuf.c | 81 +++++---- armsrc/Makefile | 2 +- armsrc/iclass.c | 55 ++++-- armsrc/iso15693.c | 47 ++--- armsrc/optimized_cipher.c | 28 +++ armsrc/optimized_cipher.h | 2 + armsrc/optimized_cipherutils.c | 140 ++++++++++++++ armsrc/optimized_cipherutils.h | 66 +++++++ armsrc/optimized_elite.c | 238 ++++++++++++++++++++++++ armsrc/optimized_elite.h | 62 +++++++ armsrc/optimized_ikeys.c | 324 +++++++++++++++++++++++++++++++++ armsrc/optimized_ikeys.h | 69 +++++++ client/src/cmdhficlass.c | 57 +++--- 13 files changed, 1072 insertions(+), 99 deletions(-) create mode 100644 armsrc/optimized_cipherutils.c create mode 100644 armsrc/optimized_cipherutils.h create mode 100644 armsrc/optimized_elite.c create mode 100644 armsrc/optimized_elite.h create mode 100644 armsrc/optimized_ikeys.c create mode 100644 armsrc/optimized_ikeys.h diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 4642d0079..8637443a0 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -22,29 +22,29 @@ extern uint8_t _stack_start, __bss_end__; static uint8_t *BigBuf = &__bss_end__; /* BigBuf memory layout: -Pointer to highest available memory: BigBuf_hi - high BigBuf_size - reserved = BigBuf_malloc() subtracts amount from BigBuf_hi, +Pointer to highest available memory: s_bigbuf_hi + high s_bigbuf_size + reserved = BigBuf_malloc() subtracts amount from s_bigbuf_hi, low 0x00 */ -static uint32_t BigBuf_size = 0; +static uint32_t s_bigbuf_size = 0; // High memory mark -static uint32_t BigBuf_hi = 0; +static uint32_t s_bigbuf_hi = 0; // pointer to the emulator memory. static uint8_t *emulator_memory = NULL; // trace related variables -static uint32_t traceLen = 0; +static uint32_t trace_len = 0; static bool tracing = true; // compute the available size for BigBuf void BigBuf_initialize(void) { - BigBuf_size = (uint32_t)&_stack_start - (uint32_t)&__bss_end__; - BigBuf_hi = BigBuf_size; - traceLen = 0; + s_bigbuf_size = (uint32_t)&_stack_start - (uint32_t)&__bss_end__; + s_bigbuf_hi = s_bigbuf_size; + trace_len = 0; } // get the address of BigBuf @@ -53,7 +53,7 @@ uint8_t *BigBuf_get_addr(void) { } uint32_t BigBuf_get_size(void) { - return BigBuf_size; + return s_bigbuf_size; } // get the address of the emulator memory. Allocate part of Bigbuf for it, if not yet done @@ -64,6 +64,11 @@ uint8_t *BigBuf_get_EM_addr(void) { return emulator_memory; } +/* +uint32_t BigBuf_get_EM_size(void) { + return CARD_MEMORY_SIZE; +} +*/ // clear ALL of BigBuf void BigBuf_Clear(void) { @@ -72,9 +77,9 @@ void BigBuf_Clear(void) { // clear ALL of BigBuf void BigBuf_Clear_ext(bool verbose) { - memset(BigBuf, 0, BigBuf_size); + memset(BigBuf, 0, s_bigbuf_size); if (verbose) - Dbprintf("Buffer cleared (%i bytes)", BigBuf_size); + Dbprintf("Buffer cleared (%i bytes)", s_bigbuf_size); } void BigBuf_Clear_EM(void) { @@ -82,23 +87,23 @@ void BigBuf_Clear_EM(void) { } void BigBuf_Clear_keep_EM(void) { - memset(BigBuf, 0, BigBuf_hi); + memset(BigBuf, 0, s_bigbuf_hi); } // allocate a chunk of memory from BigBuf. We allocate high memory first. The unallocated memory // at the beginning of BigBuf is always for traces/samples uint8_t *BigBuf_malloc(uint16_t chunksize) { - if (BigBuf_hi < chunksize) + if (s_bigbuf_hi < chunksize) return NULL; // no memory left chunksize = (chunksize + 3) & 0xfffc; // round to next multiple of 4 - BigBuf_hi -= chunksize; // aligned to 4 Byte boundary - return (uint8_t *)BigBuf + BigBuf_hi; + s_bigbuf_hi -= chunksize; // aligned to 4 Byte boundary + return (uint8_t *)BigBuf + s_bigbuf_hi; } // free ALL allocated chunks. The whole BigBuf is available for traces or samples again. void BigBuf_free(void) { - BigBuf_hi = BigBuf_size; + s_bigbuf_hi = s_bigbuf_size; emulator_memory = NULL; // shouldn't this empty BigBuf also? } @@ -106,33 +111,33 @@ void BigBuf_free(void) { // free allocated chunks EXCEPT the emulator memory void BigBuf_free_keep_EM(void) { if (emulator_memory != NULL) - BigBuf_hi = emulator_memory - (uint8_t *)BigBuf; + s_bigbuf_hi = emulator_memory - (uint8_t *)BigBuf; else - BigBuf_hi = BigBuf_size; + s_bigbuf_hi = s_bigbuf_size; // shouldn't this empty BigBuf also? } void BigBuf_print_status(void) { DbpString(_CYAN_("Memory")); - Dbprintf(" BigBuf_size.............%d", BigBuf_size); - Dbprintf(" Available memory........%d", BigBuf_hi); + Dbprintf(" BigBuf_size.............%d", s_bigbuf_size); + Dbprintf(" Available memory........%d", s_bigbuf_hi); DbpString(_CYAN_("Tracing")); Dbprintf(" tracing ................%d", tracing); - Dbprintf(" traceLen ...............%d", traceLen); + Dbprintf(" traceLen ...............%d", trace_len); } // return the maximum trace length (i.e. the unallocated size of BigBuf) uint16_t BigBuf_max_traceLen(void) { - return BigBuf_hi; + return s_bigbuf_hi; } void clear_trace(void) { - traceLen = 0; + trace_len = 0; } void set_tracelen(uint32_t value) { - traceLen = value; + trace_len = value; } void set_tracing(bool enable) { @@ -148,7 +153,7 @@ bool get_tracing(void) { * @return */ uint32_t BigBuf_get_traceLen(void) { - return traceLen; + return trace_len; } /** @@ -164,12 +169,12 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ } uint8_t *trace = BigBuf_get_addr(); - tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + traceLen); + tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + trace_len); uint32_t num_paritybytes = (iLen - 1) / 8 + 1; // number of valid paritybytes in *parity // Return when trace is full - if (TRACELOG_HDR_LEN + iLen + num_paritybytes >= BigBuf_max_traceLen() - traceLen) { + if (TRACELOG_HDR_LEN + iLen + num_paritybytes >= BigBuf_max_traceLen() - trace_len) { tracing = false; // don't trace any more if (DBGLEVEL >= DBG_DEBUG) { Dbprintf("trace is full"); } return false; @@ -185,33 +190,35 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ if (duration > 0x7FFF) { if (DBGLEVEL >= DBG_DEBUG) { Dbprintf("Error in LogTrace: duration too long for 15 bits encoding: 0x%08x start:0x%08x end:0x%08x", duration, timestamp_start, timestamp_end); - Dbprintf("Forcing duration = 0"); +// Dbprintf("Forcing duration = 0"); } - duration = 0; + + duration /= 32; + // duration >>= 5; +// duration = 0; } hdr->timestamp = timestamp_start; hdr->duration = duration; hdr->data_len = iLen; hdr->isResponse = !readerToTag; - traceLen += TRACELOG_HDR_LEN; + trace_len += TRACELOG_HDR_LEN; // data bytes if (btBytes != NULL && iLen != 0) { - memcpy(trace + traceLen, btBytes, iLen); + memcpy(trace + trace_len, btBytes, iLen); } - traceLen += iLen; + trace_len += iLen; // parity bytes if (num_paritybytes != 0) { if (parity != NULL) { - memcpy(trace + traceLen, parity, num_paritybytes); + memcpy(trace + trace_len, parity, num_paritybytes); } else { - memset(trace + traceLen, 0x00, num_paritybytes); + memset(trace + trace_len, 0x00, num_paritybytes); } } - traceLen += num_paritybytes; - + trace_len += num_paritybytes; return true; } diff --git a/armsrc/Makefile b/armsrc/Makefile index 44ca3ab6b..2768ab3a4 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -30,7 +30,7 @@ SRC_ISO14443b = iso14443b.c SRC_FELICA = felica.c SRC_CRAPTO1 = crypto1.c des.c desfire_crypto.c mifaredesfire.c aes.c platform_util.c SRC_CRC = crc.c crc16.c crc32.c -SRC_ICLASS = iclass.c optimized_cipher.c +SRC_ICLASS = iclass.c optimized_cipherutils.c optimized_ikeys.c optimized_elite.c optimized_cipher.c SRC_LEGIC = legicrf.c legicrfsim.c legic_prng.c SRC_NFCBARCODE = thinfilm.c diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 478bafa43..a68565830 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -781,9 +781,10 @@ static void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time) CodeIso15693AsReader(frame, len); TransmitTo15693Tag(ToSend, ToSendMax, start_time); - uint32_t end_time = *start_time + 32 * (8 * ToSendMax - 4); // substract the 4 padding bits after EOF + uint32_t end_time = *start_time + (32 * ((8 * ToSendMax) - 4)); // substract the 4 padding bits after EOF - LogTrace(frame, len, *start_time * 4, end_time * 4, NULL, true); + if (LogTrace(frame, len, (*start_time * 4), (end_time * 4), NULL, true) == false) + DbpString("send_as_reader: failed logtrace"); } static bool iclass_send_cmd_with_retries(uint8_t* cmd, size_t cmdsize, uint8_t* resp, size_t max_resp_size, @@ -1112,35 +1113,57 @@ void iClass_ReadCheck(uint8_t blockno, uint8_t keytype) { // used with function select_and_auth (cmdhficlass.c) // which needs to authenticate before doing more things like read/write -void iClass_Authentication(uint8_t *mac) { +// selects and authenticate to a card, sends back div_key and mac to client. +void iClass_Authentication(uint8_t *bytes) { + + struct p { + uint8_t key[8]; + bool use_raw; + bool use_elite; + bool use_credit_key; + } PACKED; + struct p *payload = (struct p *)bytes; + + // device response message + struct { + bool isOK; + uint8_t div_key[8]; + uint8_t mac[4]; + } PACKED packet; Iso15693InitReader(); StartCountSspClk(); uint8_t card_data[3 * 8] = {0xFF}; - bool use_credit_key = false; uint32_t eof_time = 0; - bool isOK = select_iclass_tag(card_data, use_credit_key, &eof_time); - if (isOK == false) { - reply_ng(CMD_HF_ICLASS_AUTH, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); + packet.isOK = select_iclass_tag(card_data, payload->use_credit_key, &eof_time); + if (packet.isOK == false) { + reply_ng(CMD_HF_ICLASS_AUTH, PM3_SUCCESS, (uint8_t *)&packet, sizeof(packet)); return; } uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; uint8_t check[9] = { ICLASS_CMD_CHECK }; -// uint8_t mac[4]; -// opt_doReaderMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, mac ) + uint8_t ccnr[12] = {0}; + memcpy(ccnr, card_data + 16, 8); + + if (payload->use_raw) + memcpy(packet.div_key, payload->key, 8); + else + iclass_calc_div_key(card_data, payload->key, packet.div_key, payload->use_elite); + + opt_doReaderMAC(ccnr, packet.div_key, packet.mac); // copy MAC to check command (readersignature) - check[5] = mac[0]; - check[6] = mac[1]; - check[7] = mac[2]; - check[8] = mac[3]; + check[5] = packet.mac[0]; + check[6] = packet.mac[1]; + check[7] = packet.mac[2]; + check[8] = packet.mac[3]; uint8_t resp[ICLASS_BUFFER_SIZE]; - isOK = iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); - reply_ng(CMD_HF_ICLASS_AUTH, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); + packet.isOK = iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + reply_ng(CMD_HF_ICLASS_AUTH, PM3_SUCCESS, (uint8_t *)&packet, sizeof(packet)); } typedef struct iclass_premac { @@ -1300,6 +1323,8 @@ void iClass_Dump(uint8_t start_blockno, uint8_t numblks) { // return pointer to dump memory in arg3 // iceman: why not return | dataout - getbigbuf ? Should give exact location. + Dbprintf("ICE:: dataout, %u max trace %u, bb start %u, data-bb %u ", dataout, BigBuf_max_traceLen(), BigBuf_get_addr(), dataout - BigBuf_get_addr() ); + Dbprintf("ICE:: bb size %u, malloced %u (255*8)", BigBuf_get_size(), BigBuf_get_size() - (dataout - BigBuf_get_addr()) ); reply_mix(CMD_ACK, isOK, blkcnt, BigBuf_max_traceLen(), 0, 0); BigBuf_free(); } diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index f8214b5e4..39a64a8c4 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -688,9 +688,9 @@ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeo FpgaDisableSscDma(); uint32_t sof_time = *eof_time - - DecodeTag.len * 8 * 8 * 16 // time for byte transfers - - 32 * 16 // time for SOF transfer - - (DecodeTag.lastBit != SOF_PART2?32*16:0); // time for EOF transfer + - (DecodeTag.len * 8 * 8 * 16) // time for byte transfers + - (32 * 16) // time for SOF transfer + - (DecodeTag.lastBit != SOF_PART2 ? (32 * 16) : 0); // time for EOF transfer if (DBGLEVEL >= DBG_EXTENDED) { Dbprintf("samples = %d, ret = %d, Decoder: state = %d, lastBit = %d, len = %d, bitCount = %d, posCount = %d", @@ -705,7 +705,8 @@ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeo Dbprintf("timing: sof_time = %d, eof_time = %d", (sof_time * 4), (*eof_time * 4)); } - LogTrace(DecodeTag.output, DecodeTag.len, (sof_time * 4), (*eof_time * 4), NULL, false); + if (LogTrace(DecodeTag.output, DecodeTag.len, (sof_time * 4), (*eof_time * 4), NULL, false) == false) + DbpString("GetIso15693AnswerFromTag: failed logtrace"); if (ret < 0) { return ret; @@ -1090,7 +1091,8 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo - DecodeReader.byteCount * (DecodeReader.Coding==CODING_1_OUT_OF_4?128:2048) // time for byte transfers - 32 // time for SOF transfer - 16; // time for EOF transfer - LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time*32, *eof_time*32, NULL, true); + if (LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time*32, *eof_time*32, NULL, true) == false) + DbpString("GetIso15693CommandFromReader: failed logtrace"); } return DecodeReader.byteCount; @@ -1223,16 +1225,18 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { } } - if (!TagIsActive) { // no need to try decoding reader data if the tag is sending + // no need to try decoding reader data if the tag is sending + if (TagIsActive == false) { + if (Handle15693SampleFromReader(sniffdata & 0x02, &DecodeReader)) { - uint32_t eof_time = dma_start_time + samples*16 + 8 - DELAY_READER_TO_ARM_SNIFF; // end of EOF + uint32_t eof_time = dma_start_time + (samples * 16) + 8 - DELAY_READER_TO_ARM_SNIFF; // end of EOF if (DecodeReader.byteCount > 0) { uint32_t sof_time = eof_time - - DecodeReader.byteCount * (DecodeReader.Coding==CODING_1_OUT_OF_4?128*16:2048*16) // time for byte transfers - - 32*16 // time for SOF transfer - - 16*16; // time for EOF transfer - LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time*4, eof_time*4, NULL, true); + - DecodeReader.byteCount * (DecodeReader.Coding == CODING_1_OUT_OF_4 ? 128 * 16 : 2048 * 16) // time for byte transfers + - 32 * 16 // time for SOF transfer + - 16 * 16; // time for EOF transfer + LogTrace(DecodeReader.output, DecodeReader.byteCount, (sof_time * 4), (eof_time * 4), NULL, true); } // And ready to receive another command. DecodeReaderReset(&DecodeReader); @@ -1244,13 +1248,13 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { } else if (Handle15693SampleFromReader(sniffdata & 0x01, &DecodeReader)) { - uint32_t eof_time = dma_start_time + samples*16 + 16 - DELAY_READER_TO_ARM_SNIFF; // end of EOF + uint32_t eof_time = dma_start_time + (samples * 16) + 16 - DELAY_READER_TO_ARM_SNIFF; // end of EOF if (DecodeReader.byteCount > 0) { uint32_t sof_time = eof_time - - DecodeReader.byteCount * (DecodeReader.Coding==CODING_1_OUT_OF_4?128*16:2048*16) // time for byte transfers - - 32*16 // time for SOF transfer - - 16*16; // time for EOF transfer - LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time*4, eof_time*4, NULL, true); + - DecodeReader.byteCount * (DecodeReader.Coding == CODING_1_OUT_OF_4 ? 128 * 16 : 2048 * 16) // time for byte transfers + - 32 * 16 // time for SOF transfer + - 16 * 16; // time for EOF transfer + LogTrace(DecodeReader.output, DecodeReader.byteCount, (sof_time * 4), (eof_time * 4), NULL, true); } // And ready to receive another command DecodeReaderReset(&DecodeReader); @@ -1269,15 +1273,16 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { if (!ReaderIsActive && ExpectTagAnswer) { // no need to try decoding tag data if the reader is currently sending or no answer expected yet if (Handle15693SamplesFromTag(sniffdata >> 2, &DecodeTag)) { - uint32_t eof_time = dma_start_time + samples*16 - DELAY_TAG_TO_ARM_SNIFF; // end of EOF + uint32_t eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM_SNIFF; // end of EOF if (DecodeTag.lastBit == SOF_PART2) { - eof_time -= 8*16; // needed 8 additional samples to confirm single SOF (iCLASS) + eof_time -= (8 * 16); // needed 8 additional samples to confirm single SOF (iCLASS) } uint32_t sof_time = eof_time - DecodeTag.len * 8 * 8 * 16 // time for byte transfers - - 32 * 16 // time for SOF transfer - - (DecodeTag.lastBit != SOF_PART2?32*16:0); // time for EOF transfer - LogTrace(DecodeTag.output, DecodeTag.len, sof_time*4, eof_time*4, NULL, false); + - (32 * 16) // time for SOF transfer + - (DecodeTag.lastBit != SOF_PART2 ? (32 * 16) : 0); // time for EOF transfer + + LogTrace(DecodeTag.output, DecodeTag.len, (sof_time * 4), (eof_time * 4), NULL, false); // And ready to receive another response. DecodeTagReset(&DecodeTag); DecodeReaderReset(&DecodeReader); diff --git a/armsrc/optimized_cipher.c b/armsrc/optimized_cipher.c index 24bb8d7ee..021b8ae22 100644 --- a/armsrc/optimized_cipher.c +++ b/armsrc/optimized_cipher.c @@ -77,7 +77,15 @@ -- piwi 2019 **/ +/** + add the possibility to do iCLASS on device only + -- iceman 2020 +**/ + #include "optimized_cipher.h" +#include "optimized_elite.h" +#include "optimized_ikeys.h" +#include "optimized_cipherutils.h" static const uint8_t opt_select_LUT[256] = { 00, 03, 02, 01, 02, 03, 00, 01, 04, 07, 07, 04, 06, 07, 05, 04, @@ -290,3 +298,23 @@ void opt_doTagMAC_2(State _init, uint8_t *nr, uint8_t mac[4], const uint8_t *di opt_suc(div_key_p, &_init, nr, 4, true); opt_output(div_key_p, &_init, mac); } + + +void iclass_calc_div_key(uint8_t *csn, uint8_t *key, uint8_t *div_key, bool elite) { + if (elite) { + uint8_t keytable[128] = {0}; + uint8_t key_index[8] = {0}; + uint8_t key_sel[8] = { 0 }; + uint8_t key_sel_p[8] = { 0 }; + hash2(key, keytable); + hash1(csn, key_index); + for (uint8_t i = 0; i < 8 ; i++) + key_sel[i] = keytable[key_index[i]]; + + //Permute from iclass format to standard format + permutekey_rev(key_sel, key_sel_p); + diversifyKey(csn, key_sel_p, div_key); + } else { + diversifyKey(csn, key, div_key); + } +} diff --git a/armsrc/optimized_cipher.h b/armsrc/optimized_cipher.h index c6df25ab8..e65b6c4cb 100644 --- a/armsrc/optimized_cipher.h +++ b/armsrc/optimized_cipher.h @@ -46,4 +46,6 @@ State opt_doTagMAC_1(uint8_t *cc_p, const uint8_t *div_key_p); */ void opt_doTagMAC_2(State _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p); + +void iclass_calc_div_key(uint8_t *csn, uint8_t *key, uint8_t *div_key, bool elite); #endif // OPTIMIZED_CIPHER_H diff --git a/armsrc/optimized_cipherutils.c b/armsrc/optimized_cipherutils.c new file mode 100644 index 000000000..c51f83f9b --- /dev/null +++ b/armsrc/optimized_cipherutils.c @@ -0,0 +1,140 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file 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 loclass. If not, see . + * + * + ****************************************************************************/ +#include "optimized_cipherutils.h" +#include + +/** + * + * @brief Return and remove the first bit (x0) in the stream : + * @param stream + * @return + */ +bool headBit(BitstreamIn *stream) { + int bytepos = stream->position >> 3; // divide by 8 + int bitpos = (stream->position++) & 7; // mask out 00000111 + return (*(stream->buffer + bytepos) >> (7 - bitpos)) & 1; +} +/** + * @brief Return and remove the last bit (xn) in the stream: + * @param stream + * @return + */ +bool tailBit(BitstreamIn *stream) { + int bitpos = stream->numbits - 1 - (stream->position++); + + int bytepos = bitpos >> 3; + bitpos &= 7; + return (*(stream->buffer + bytepos) >> (7 - bitpos)) & 1; +} +/** + * @brief Pushes bit onto the stream + * @param stream + * @param bit + */ +void pushBit(BitstreamOut *stream, bool bit) { + int bytepos = stream->position >> 3; // divide by 8 + int bitpos = stream->position & 7; + *(stream->buffer + bytepos) |= (bit) << (7 - bitpos); + stream->position++; + stream->numbits++; +} + +/** + * @brief Pushes the lower six bits onto the stream + * as b0 b1 b2 b3 b4 b5 b6 + * @param stream + * @param bits + */ +void push6bits(BitstreamOut *stream, uint8_t bits) { + pushBit(stream, bits & 0x20); + pushBit(stream, bits & 0x10); + pushBit(stream, bits & 0x08); + pushBit(stream, bits & 0x04); + pushBit(stream, bits & 0x02); + pushBit(stream, bits & 0x01); +} + +/** + * @brief bitsLeft + * @param stream + * @return number of bits left in stream + */ +int bitsLeft(BitstreamIn *stream) { + return stream->numbits - stream->position; +} +/** + * @brief numBits + * @param stream + * @return Number of bits stored in stream + */ +void x_num_to_bytes(uint64_t n, size_t len, uint8_t *dest) { + while (len--) { + dest[len] = (uint8_t) n; + n >>= 8; + } +} + +uint64_t x_bytes_to_num(uint8_t *src, size_t len) { + uint64_t num = 0; + while (len--) { + num = (num << 8) | (*src); + src++; + } + return num; +} + +uint8_t reversebytes(uint8_t b) { + b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; + b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + return b; +} + +void reverse_arraybytes(uint8_t *arr, size_t len) { + uint8_t i; + for (i = 0; i < len ; i++) { + arr[i] = reversebytes(arr[i]); + } +} + +void reverse_arraycopy(uint8_t *arr, uint8_t *dest, size_t len) { + uint8_t i; + for (i = 0; i < len ; i++) { + dest[i] = reversebytes(arr[i]); + } +} + diff --git a/armsrc/optimized_cipherutils.h b/armsrc/optimized_cipherutils.h new file mode 100644 index 000000000..63ba8b8aa --- /dev/null +++ b/armsrc/optimized_cipherutils.h @@ -0,0 +1,66 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file 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 loclass. If not, see . + * + * + ****************************************************************************/ +#ifndef CIPHERUTILS_H +#define CIPHERUTILS_H +#include +#include +#include + +typedef struct { + uint8_t *buffer; + uint8_t numbits; + uint8_t position; +} BitstreamIn; + +typedef struct { + uint8_t *buffer; + uint8_t numbits; + uint8_t position; +} BitstreamOut; + +bool headBit(BitstreamIn *stream); +bool tailBit(BitstreamIn *stream); +void pushBit(BitstreamOut *stream, bool bit); +int bitsLeft(BitstreamIn *stream); + +void push6bits(BitstreamOut *stream, uint8_t bits); +void x_num_to_bytes(uint64_t n, size_t len, uint8_t *dest); +uint64_t x_bytes_to_num(uint8_t *src, size_t len); +uint8_t reversebytes(uint8_t b); +void reverse_arraybytes(uint8_t *arr, size_t len); +void reverse_arraycopy(uint8_t *arr, uint8_t *dest, size_t len); +#endif // CIPHERUTILS_H diff --git a/armsrc/optimized_elite.c b/armsrc/optimized_elite.c new file mode 100644 index 000000000..d2c57ac68 --- /dev/null +++ b/armsrc/optimized_elite.c @@ -0,0 +1,238 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file 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 loclass. If not, see . + * + * + * + ****************************************************************************/ +#include "optimized_elite.h" + +#include +#include +#include +#include "mbedtls/des.h" +#include "optimized_ikeys.h" + +/** + * @brief Permutes a key from standard NIST format to Iclass specific format + * from http://www.proxmark.org/forum/viewtopic.php?pid=11220#p11220 + * + * If you permute [6c 8d 44 f9 2a 2d 01 bf] you get [8a 0d b9 88 bb a7 90 ea] as shown below. + * + * 1 0 1 1 1 1 1 1 bf + * 0 0 0 0 0 0 0 1 01 + * 0 0 1 0 1 1 0 1 2d + * 0 0 1 0 1 0 1 0 2a + * 1 1 1 1 1 0 0 1 f9 + * 0 1 0 0 0 1 0 0 44 + * 1 0 0 0 1 1 0 1 8d + * 0 1 1 0 1 1 0 0 6c + * + * 8 0 b 8 b a 9 e + * a d 9 8 b 7 0 a + * + * @param key + * @param dest + */ +void permutekey(uint8_t key[8], uint8_t dest[8]) { + int i; + for (i = 0 ; i < 8 ; i++) { + dest[i] = (((key[7] & (0x80 >> i)) >> (7 - i)) << 7) | + (((key[6] & (0x80 >> i)) >> (7 - i)) << 6) | + (((key[5] & (0x80 >> i)) >> (7 - i)) << 5) | + (((key[4] & (0x80 >> i)) >> (7 - i)) << 4) | + (((key[3] & (0x80 >> i)) >> (7 - i)) << 3) | + (((key[2] & (0x80 >> i)) >> (7 - i)) << 2) | + (((key[1] & (0x80 >> i)) >> (7 - i)) << 1) | + (((key[0] & (0x80 >> i)) >> (7 - i)) << 0); + } +} +/** + * Permutes a key from iclass specific format to NIST format + * @brief permutekey_rev + * @param key + * @param dest + */ +void permutekey_rev(uint8_t key[8], uint8_t dest[8]) { + int i; + for (i = 0 ; i < 8 ; i++) { + dest[7 - i] = (((key[0] & (0x80 >> i)) >> (7 - i)) << 7) | + (((key[1] & (0x80 >> i)) >> (7 - i)) << 6) | + (((key[2] & (0x80 >> i)) >> (7 - i)) << 5) | + (((key[3] & (0x80 >> i)) >> (7 - i)) << 4) | + (((key[4] & (0x80 >> i)) >> (7 - i)) << 3) | + (((key[5] & (0x80 >> i)) >> (7 - i)) << 2) | + (((key[6] & (0x80 >> i)) >> (7 - i)) << 1) | + (((key[7] & (0x80 >> i)) >> (7 - i)) << 0); + } +} + +/** + * Helper function for hash1 + * @brief rr + * @param val + * @return + */ +static inline uint8_t rr(uint8_t val) { + return val >> 1 | ((val & 1) << 7); +} + +/** + * Helper function for hash1 + * @brief rl + * @param val + * @return + */ +static inline uint8_t rl(uint8_t val) { + return val << 1 | ((val & 0x80) >> 7); +} + +/** + * Helper function for hash1 + * @brief swap + * @param val + * @return + */ +static inline uint8_t swap(uint8_t val) { + return ((val >> 4) & 0xFF) | ((val & 0xFF) << 4); +} + +/** + * Hash1 takes CSN as input, and determines what bytes in the keytable will be used + * when constructing the K_sel. + * @param csn the CSN used + * @param k output + */ +void hash1(uint8_t csn[], uint8_t k[]) { + k[0] = csn[0] ^ csn[1] ^ csn[2] ^ csn[3] ^ csn[4] ^ csn[5] ^ csn[6] ^ csn[7]; + k[1] = csn[0] + csn[1] + csn[2] + csn[3] + csn[4] + csn[5] + csn[6] + csn[7]; + k[2] = rr(swap(csn[2] + k[1])); + k[3] = rl(swap(csn[3] + k[0])); + k[4] = ~rr(csn[4] + k[2]) + 1; + k[5] = ~rl(csn[5] + k[3]) + 1; + k[6] = rr(csn[6] + (k[4] ^ 0x3c)); + k[7] = rl(csn[7] + (k[5] ^ 0xc3)); + + k[7] &= 0x7F; + k[6] &= 0x7F; + k[5] &= 0x7F; + k[4] &= 0x7F; + k[3] &= 0x7F; + k[2] &= 0x7F; + k[1] &= 0x7F; + k[0] &= 0x7F; +} +/** +Definition 14. Define the rotate key function rk : (F 82 ) 8 × N → (F 82 ) 8 as +rk(x [0] . . . x [7] , 0) = x [0] . . . x [7] +rk(x [0] . . . x [7] , n + 1) = rk(rl(x [0] ) . . . rl(x [7] ), n) +**/ +static void rk(uint8_t *key, uint8_t n, uint8_t *outp_key) { + memcpy(outp_key, key, 8); + uint8_t j; + while (n-- > 0) { + for (j = 0; j < 8 ; j++) + outp_key[j] = rl(outp_key[j]); + } + return; +} + +static mbedtls_des_context ctx_enc; +static mbedtls_des_context ctx_dec; + +static void desdecrypt_iclass(uint8_t *iclass_key, uint8_t *input, uint8_t *output) { + uint8_t key_std_format[8] = {0}; + permutekey_rev(iclass_key, key_std_format); + mbedtls_des_setkey_dec(&ctx_dec, key_std_format); + mbedtls_des_crypt_ecb(&ctx_dec, input, output); +} + +static void desencrypt_iclass(uint8_t *iclass_key, uint8_t *input, uint8_t *output) { + uint8_t key_std_format[8] = {0}; + permutekey_rev(iclass_key, key_std_format); + mbedtls_des_setkey_enc(&ctx_enc, key_std_format); + mbedtls_des_crypt_ecb(&ctx_enc, input, output); +} + +/** + * @brief Insert uint8_t[8] custom master key to calculate hash2 and return key_select. + * @param key unpermuted custom key + * @param hash1 hash1 + * @param key_sel output key_sel=h[hash1[i]] + */ +void hash2(uint8_t *key64, uint8_t *outp_keytable) { + /** + *Expected: + * High Security Key Table + + 00 F1 35 59 A1 0D 5A 26 7F 18 60 0B 96 8A C0 25 C1 + 10 BF A1 3B B0 FF 85 28 75 F2 1F C6 8F 0E 74 8F 21 + 20 14 7A 55 16 C8 A9 7D B3 13 0C 5D C9 31 8D A9 B2 + 30 A3 56 83 0F 55 7E DE 45 71 21 D2 6D C1 57 1C 9C + 40 78 2F 64 51 42 7B 64 30 FA 26 51 76 D3 E0 FB B6 + 50 31 9F BF 2F 7E 4F 94 B4 BD 4F 75 91 E3 1B EB 42 + 60 3F 88 6F B8 6C 2C 93 0D 69 2C D5 20 3C C1 61 95 + 70 43 08 A0 2F FE B3 26 D7 98 0B 34 7B 47 70 A0 AB + + **** The 64-bit HS Custom Key Value = 5B7C62C491C11B39 ******/ + uint8_t key64_negated[8] = {0}; + uint8_t z[8][8] = {{0}, {0}}; + uint8_t temp_output[8] = {0}; + + //calculate complement of key + int i; + for (i = 0; i < 8; i++) + key64_negated[i] = ~key64[i]; + + // Once again, key is on iclass-format + desencrypt_iclass(key64, key64_negated, z[0]); + + uint8_t y[8][8] = {{0}, {0}}; + + // y[0]=DES_dec(z[0],~key) + // Once again, key is on iclass-format + desdecrypt_iclass(z[0], key64_negated, y[0]); + + for (i = 1; i < 8; i++) { + rk(key64, i, temp_output); + desdecrypt_iclass(temp_output, z[i - 1], z[i]); + desencrypt_iclass(temp_output, y[i - 1], y[i]); + } + + if (outp_keytable != NULL) { + for (i = 0 ; i < 8 ; i++) { + memcpy(outp_keytable + i * 16, y[i], 8); + memcpy(outp_keytable + 8 + i * 16, z[i], 8); + } + } +} diff --git a/armsrc/optimized_elite.h b/armsrc/optimized_elite.h new file mode 100644 index 000000000..281ecf0bb --- /dev/null +++ b/armsrc/optimized_elite.h @@ -0,0 +1,62 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file 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 loclass. If not, see . + * + * + ****************************************************************************/ + + +#ifndef ELITE_CRACK_H +#define ELITE_CRACK_H + +#include +#include + +void permutekey(uint8_t key[8], uint8_t dest[8]); +/** + * Permutes a key from iclass specific format to NIST format + * @brief permutekey_rev + * @param key + * @param dest + */ +void permutekey_rev(uint8_t key[8], uint8_t dest[8]); +/** + * Hash1 takes CSN as input, and determines what bytes in the keytable will be used + * when constructing the K_sel. + * @param csn the CSN used + * @param k output + */ +void hash1(uint8_t *csn, uint8_t *k); +void hash2(uint8_t *key64, uint8_t *outp_keytable); + +#endif diff --git a/armsrc/optimized_ikeys.c b/armsrc/optimized_ikeys.c new file mode 100644 index 000000000..eeb00e562 --- /dev/null +++ b/armsrc/optimized_ikeys.c @@ -0,0 +1,324 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file 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 loclass. If not, see . + * + * + ****************************************************************************/ + +/** +From "Dismantling iclass": + This section describes in detail the built-in key diversification algorithm of iClass. + Besides the obvious purpose of deriving a card key from a master key, this + algorithm intends to circumvent weaknesses in the cipher by preventing the + usage of certain ‘weak’ keys. In order to compute a diversified key, the iClass + reader first encrypts the card identity id with the master key K, using single + DES. The resulting ciphertext is then input to a function called hash0 which + outputs the diversified key k. + + k = hash0(DES enc (id, K)) + + Here the DES encryption of id with master key K outputs a cryptogram c + of 64 bits. These 64 bits are divided as c = x, y, z [0] , . . . , z [7] ∈ F 82 × F 82 × (F 62 ) 8 + which is used as input to the hash0 function. This function introduces some + obfuscation by performing a number of permutations, complement and modulo + operations, see Figure 2.5. Besides that, it checks for and removes patterns like + similar key bytes, which could produce a strong bias in the cipher. Finally, the + output of hash0 is the diversified card key k = k [0] , . . . , k [7] ∈ (F 82 ) 8 . + +**/ +#include "optimized_ikeys.h" + +#include +#include +#include +#include "mbedtls/des.h" +#include "optimized_cipherutils.h" + +uint8_t pi[35] = { + 0x0F, 0x17, 0x1B, 0x1D, 0x1E, 0x27, 0x2B, 0x2D, + 0x2E, 0x33, 0x35, 0x39, 0x36, 0x3A, 0x3C, 0x47, + 0x4B, 0x4D, 0x4E, 0x53, 0x55, 0x56, 0x59, 0x5A, + 0x5C, 0x63, 0x65, 0x66, 0x69, 0x6A, 0x6C, 0x71, + 0x72, 0x74, 0x78 +}; + +static mbedtls_des_context ctx_enc; + +/** + * @brief The key diversification algorithm uses 6-bit bytes. + * This implementation uses 64 bit uint to pack seven of them into one + * variable. When they are there, they are placed as follows: + * XXXX XXXX N0 .... N7, occupying the last 48 bits. + * + * This function picks out one from such a collection + * @param all + * @param n bitnumber + * @return + */ +static uint8_t getSixBitByte(uint64_t c, int n) { + return (c >> (42 - 6 * n)) & 0x3F; +} + +/** + * @brief Puts back a six-bit 'byte' into a uint64_t. + * @param c buffer + * @param z the value to place there + * @param n bitnumber. + */ +static void pushbackSixBitByte(uint64_t *c, uint8_t z, int n) { + //0x XXXX YYYY ZZZZ ZZZZ ZZZZ + // ^z0 ^z7 + //z0: 1111 1100 0000 0000 + + uint64_t masked = z & 0x3F; + uint64_t eraser = 0x3F; + masked <<= 42 - 6 * n; + eraser <<= 42 - 6 * n; + + //masked <<= 6*n; + //eraser <<= 6*n; + + eraser = ~eraser; + (*c) &= eraser; + (*c) |= masked; + +} +/** + * @brief Swaps the z-values. + * If the input value has format XYZ0Z1...Z7, the output will have the format + * XYZ7Z6...Z0 instead + * @param c + * @return + */ +static uint64_t swapZvalues(uint64_t c) { + uint64_t newz = 0; + pushbackSixBitByte(&newz, getSixBitByte(c, 0), 7); + pushbackSixBitByte(&newz, getSixBitByte(c, 1), 6); + pushbackSixBitByte(&newz, getSixBitByte(c, 2), 5); + pushbackSixBitByte(&newz, getSixBitByte(c, 3), 4); + pushbackSixBitByte(&newz, getSixBitByte(c, 4), 3); + pushbackSixBitByte(&newz, getSixBitByte(c, 5), 2); + pushbackSixBitByte(&newz, getSixBitByte(c, 6), 1); + pushbackSixBitByte(&newz, getSixBitByte(c, 7), 0); + newz |= (c & 0xFFFF000000000000); + return newz; +} + +/** +* @return 4 six-bit bytes chunked into a uint64_t,as 00..00a0a1a2a3 +*/ +static uint64_t ck(int i, int j, uint64_t z) { + if (i == 1 && j == -1) { + // ck(1, −1, z [0] . . . z [3] ) = z [0] . . . z [3] + return z; + } else if (j == -1) { + // ck(i, −1, z [0] . . . z [3] ) = ck(i − 1, i − 2, z [0] . . . z [3] ) + return ck(i - 1, i - 2, z); + } + + if (getSixBitByte(z, i) == getSixBitByte(z, j)) { + //ck(i, j − 1, z [0] . . . z [i] ← j . . . z [3] ) + uint64_t newz = 0; + int c; + for (c = 0; c < 4; c++) { + uint8_t val = getSixBitByte(z, c); + if (c == i) + pushbackSixBitByte(&newz, j, c); + else + pushbackSixBitByte(&newz, val, c); + } + return ck(i, j - 1, newz); + } else { + return ck(i, j - 1, z); + } +} +/** + + Definition 8. + Let the function check : (F 62 ) 8 → (F 62 ) 8 be defined as + check(z [0] . . . z [7] ) = ck(3, 2, z [0] . . . z [3] ) · ck(3, 2, z [4] . . . z [7] ) + + where ck : N × N × (F 62 ) 4 → (F 62 ) 4 is defined as + + ck(1, −1, z [0] . . . z [3] ) = z [0] . . . z [3] + ck(i, −1, z [0] . . . z [3] ) = ck(i − 1, i − 2, z [0] . . . z [3] ) + ck(i, j, z [0] . . . z [3] ) = + ck(i, j − 1, z [0] . . . z [i] ← j . . . z [3] ), if z [i] = z [j] ; + ck(i, j − 1, z [0] . . . z [3] ), otherwise + + otherwise. +**/ + +static uint64_t check(uint64_t z) { + //These 64 bits are divided as c = x, y, z [0] , . . . , z [7] + + // ck(3, 2, z [0] . . . z [3] ) + uint64_t ck1 = ck(3, 2, z); + + // ck(3, 2, z [4] . . . z [7] ) + uint64_t ck2 = ck(3, 2, z << 24); + + //The ck function will place the values + // in the middle of z. + ck1 &= 0x00000000FFFFFF000000; + ck2 &= 0x00000000FFFFFF000000; + + return ck1 | ck2 >> 24; +} + +static void permute(BitstreamIn *p_in, uint64_t z, int l, int r, BitstreamOut *out) { + if (bitsLeft(p_in) == 0) + return; + + bool pn = tailBit(p_in); + if (pn) { // pn = 1 + uint8_t zl = getSixBitByte(z, l); + + push6bits(out, zl + 1); + permute(p_in, z, l + 1, r, out); + } else { // otherwise + uint8_t zr = getSixBitByte(z, r); + + push6bits(out, zr); + permute(p_in, z, l, r + 1, out); + } +} + +/** + * @brief + *Definition 11. Let the function hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as + * hash0(x, y, z [0] . . . z [7] ) = k [0] . . . k [7] where + * z'[i] = (z[i] mod (63-i)) + i i = 0...3 + * z'[i+4] = (z[i+4] mod (64-i)) + i i = 0...3 + * ẑ = check(z'); + * @param c + * @param k this is where the diversified key is put (should be 8 bytes) + * @return + */ +void hash0(uint64_t c, uint8_t k[8]) { + c = swapZvalues(c); + + //These 64 bits are divided as c = x, y, z [0] , . . . , z [7] + // x = 8 bits + // y = 8 bits + // z0-z7 6 bits each : 48 bits + uint8_t x = (c & 0xFF00000000000000) >> 56; + uint8_t y = (c & 0x00FF000000000000) >> 48; + uint64_t zP = 0; + + for (int n = 0; n < 4 ; n++) { + uint8_t zn = getSixBitByte(c, n); + uint8_t zn4 = getSixBitByte(c, n + 4); + uint8_t _zn = (zn % (63 - n)) + n; + uint8_t _zn4 = (zn4 % (64 - n)) + n; + pushbackSixBitByte(&zP, _zn, n); + pushbackSixBitByte(&zP, _zn4, n + 4); + } + + uint64_t zCaret = check(zP); + uint8_t p = pi[x % 35]; + + if (x & 1) //Check if x7 is 1 + p = ~p; + + BitstreamIn p_in = { &p, 8, 0 }; + uint8_t outbuffer[] = {0, 0, 0, 0, 0, 0, 0, 0}; + BitstreamOut out = {outbuffer, 0, 0}; + permute(&p_in, zCaret, 0, 4, &out); //returns 48 bits? or 6 8-bytes + + //Out is now a buffer containing six-bit bytes, should be 48 bits + // if all went well + //Shift z-values down onto the lower segment + + uint64_t zTilde = x_bytes_to_num(outbuffer, sizeof(outbuffer)); + + zTilde >>= 16; + + for (int i = 0; i < 8; i++) { + // the key on index i is first a bit from y + // then six bits from z, + // then a bit from p + + // Init with zeroes + k[i] = 0; + // First, place yi leftmost in k + //k[i] |= (y << i) & 0x80 ; + + // First, place y(7-i) leftmost in k + k[i] |= (y << (7 - i)) & 0x80 ; + + uint8_t zTilde_i = getSixBitByte(zTilde, i); + // zTildeI is now on the form 00XXXXXX + // with one leftshift, it'll be + // 0XXXXXX0 + // So after leftshift, we can OR it into k + // However, when doing complement, we need to + // again MASK 0XXXXXX0 (0x7E) + zTilde_i <<= 1; + + //Finally, add bit from p or p-mod + //Shift bit i into rightmost location (mask only after complement) + uint8_t p_i = p >> i & 0x1; + + if (k[i]) { // yi = 1 + k[i] |= ~zTilde_i & 0x7E; + k[i] |= p_i & 1; + k[i] += 1; + + } else { // otherwise + k[i] |= zTilde_i & 0x7E; + k[i] |= (~p_i) & 1; + } + } +} +/** + * @brief Performs Elite-class key diversification + * @param csn + * @param key + * @param div_key + */ +void diversifyKey(uint8_t *csn, uint8_t *key, uint8_t *div_key) { + // Prepare the DES key + mbedtls_des_setkey_enc(&ctx_enc, key); + + uint8_t crypted_csn[8] = {0}; + + // Calculate DES(CSN, KEY) + mbedtls_des_crypt_ecb(&ctx_enc, csn, crypted_csn); + + //Calculate HASH0(DES)) + uint64_t c_csn = x_bytes_to_num(crypted_csn, sizeof(crypted_csn)); + + hash0(c_csn, div_key); +} + diff --git a/armsrc/optimized_ikeys.h b/armsrc/optimized_ikeys.h new file mode 100644 index 000000000..91fa406ad --- /dev/null +++ b/armsrc/optimized_ikeys.h @@ -0,0 +1,69 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file 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 loclass. If not, see . + * + * + ****************************************************************************/ + +#ifndef IKEYS_H +#define IKEYS_H + +#include + +/** + * @brief + *Definition 11. Let the function hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as + * hash0(x, y, z [0] . . . z [7] ) = k [0] . . . k [7] where + * z'[i] = (z[i] mod (63-i)) + i i = 0...3 + * z'[i+4] = (z[i+4] mod (64-i)) + i i = 0...3 + * ẑ = check(z'); + * @param c + * @param k this is where the diversified key is put (should be 8 bytes) + * @return + */ +void hash0(uint64_t c, uint8_t k[8]); +/** + * @brief Performs Elite-class key diversification + * @param csn + * @param key + * @param div_key + */ + +void diversifyKey(uint8_t csn[8], uint8_t key[8], uint8_t div_key[8]); +/** + * @brief Permutes a key from standard NIST format to Iclass specific format + * @param key + * @param dest + */ + +#endif // IKEYS_H diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 1eda5678b..62ba0f70a 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -1158,28 +1158,22 @@ static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool use_credit_key, bool v } static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool use_credit_key, bool elite, bool rawkey, bool verbose) { - uint8_t CSN[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - if (select_only(CSN, CCNR, use_credit_key, verbose) == false) { - if (verbose) PrintAndLogEx(FAILED, "selecting tag failed"); -// DropField(); - return false; - } - - //get div_key - if (rawkey) - memcpy(div_key, KEY, 8); - else - HFiClassCalcDivKey(CSN, KEY, div_key, elite); - - if (verbose) PrintAndLogEx(SUCCESS, "authing with %s: %s", rawkey ? "raw key" : "diversified key", sprint_hex(div_key, 8)); - - doMAC(CCNR, div_key, MAC); - + + struct { + uint8_t key[8]; + bool use_raw; + bool use_elite; + bool use_credit_key; + } PACKED payload; + + memcpy(payload.key, KEY, 8); + payload.use_raw = rawkey; + payload.use_elite = elite; + payload.use_credit_key = use_credit_key; + + SendCommandNG(CMD_HF_ICLASS_AUTH, (uint8_t*)&payload, sizeof(payload)); PacketResponseNG resp; - clearCommandBuffer(); - SendCommandNG(CMD_HF_ICLASS_AUTH, MAC, 4); + clearCommandBuffer(); if (WaitForResponseTimeout(CMD_HF_ICLASS_AUTH, &resp, 2000) == 0) { if (verbose) PrintAndLogEx(WARNING, "Command execute timeout"); return false; @@ -1190,12 +1184,25 @@ static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool u return false; } - uint8_t isOK = resp.data.asBytes[0]; - if (isOK == 0) { + struct p { + bool isOK; + uint8_t div_key[8]; + uint8_t mac[4]; + } PACKED; + struct p *packet = (struct p *)resp.data.asBytes; + + if (packet->isOK == 0) { if (verbose) PrintAndLogEx(FAILED, "authentication error"); return false; } - + + if (div_key) + memcpy(div_key, packet->div_key, sizeof(packet->div_key)); + + if (MAC) + memcpy(MAC, packet->mac, sizeof(packet->mac)); + + if (verbose) PrintAndLogEx(SUCCESS, "authing with %s: %s", rawkey ? "raw key" : "diversified key", sprint_hex(div_key, 8)); return true; } @@ -2972,7 +2979,7 @@ int readIclass(bool loop, bool verbose) { uint8_t readStatus = resp.oldarg[0] & 0xff; - PrintAndLogEx(NORMAL, "ICE: %x", readStatus); +// PrintAndLogEx(NORMAL, "ICE: %x", readStatus); // no tag found or button pressed if ((readStatus == 0 && !loop) || readStatus == 0xFF) { From f866a847fb0896b6639024f893ffb09b64004897 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sat, 11 Jul 2020 20:46:37 +0200 Subject: [PATCH 041/139] fix coverity 294114 --- client/src/cmdlfem4x50.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index 8198bdc74..77476e5d9 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -608,10 +608,11 @@ int CmdEM4x50Dump(const char *Cmd) { em4x50_data_t etd; etd.pwd_given = false; etd.addr_given = false; - + etd.newpwd_given = false; + char filename[FILE_PATH_SIZE] = {0x00}; char *fptr = filename; - + bool errors = false; uint8_t cmdp = 0; while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { @@ -655,9 +656,9 @@ int CmdEM4x50Dump(const char *Cmd) { bool success = (resp.status & STATUS_SUCCESS) >> 1; if (success == false) { PrintAndLogEx(FAILED, "reading tag " _RED_("failed")); - return PM3_ESOFT; + return PM3_ESOFT; } - + // structured format em4x50_word_t words[EM4X50_NO_WORDS]; prepare_result(resp.data.asBytes, 0, EM4X50_NO_WORDS - 1, words); From ebb7eb9f3ed75f206141eab6a510226cbd46d0da Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sat, 11 Jul 2020 20:53:07 +0200 Subject: [PATCH 042/139] fix coverity 294113 --- armsrc/em4x50.c | 71 +++++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 40 deletions(-) diff --git a/armsrc/em4x50.c b/armsrc/em4x50.c index 63b58e5bd..2b9ede4bf 100644 --- a/armsrc/em4x50.c +++ b/armsrc/em4x50.c @@ -100,7 +100,6 @@ int gLow = 0; static void init_tag(void) { // initialize global tag structure - for (int i = 0; i < 34; i++) for (int j = 0; j < 7; j++) tag.sectors[i][j] = 0x00; @@ -109,9 +108,7 @@ static void init_tag(void) { static uint8_t bits2byte(uint8_t *bits, int length) { // converts separate bits into a single "byte" - uint8_t byte = 0; - for (int i = 0; i < length; i++) { byte |= bits[i]; @@ -124,11 +121,10 @@ static uint8_t bits2byte(uint8_t *bits, int length) { } static void msb2lsb_word(uint8_t *word) { - + // reorders given according to EM4x50 datasheet (msb -> lsb) - + uint8_t buff[4]; - buff[0] = reflect8(word[3]); buff[1] = reflect8(word[2]); buff[2] = reflect8(word[1]); @@ -141,13 +137,12 @@ static void msb2lsb_word(uint8_t *word) { } static void save_word(int pos, uint8_t bits[EM4X50_TAG_WORD]) { - + // split "raw" word into data, row and column parity bits and stop bit and // save them in global tag structure - uint8_t row_parity[4]; uint8_t col_parity[8]; - + // data and row parities for (int i = 0; i < 4; i++) { tag.sectors[pos][i] = bits2byte(&bits[9*i],8); @@ -161,7 +156,7 @@ static void save_word(int pos, uint8_t bits[EM4X50_TAG_WORD]) { col_parity[i] = bits[36+i]; tag.sectors[pos][5] = bits2byte(col_parity,8); - + // stop bit tag.sectors[pos][6] = bits[44]; } @@ -169,7 +164,7 @@ static void save_word(int pos, uint8_t bits[EM4X50_TAG_WORD]) { static void wait_timer(int timer, uint32_t period) { // do nothing for using timer - + if (timer == FPGA_TIMER_0) { AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; @@ -184,9 +179,8 @@ static void wait_timer(int timer, uint32_t period) { } static void em4x50_setup_read(void) { - + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); // 50ms for the resonant antenna to settle. @@ -195,12 +189,12 @@ static void em4x50_setup_read(void) { FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER); // start a 1.5ticks is 1us StartTicks(); - + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); // Connect the A/D to the peak-detected low-frequency path. SetAdcMuxFor(GPIO_MUXSEL_LOPKD); - + // Steal this pin from the SSP (SPI communication channel with fpga) and // use it to control the modulation AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; @@ -208,7 +202,7 @@ static void em4x50_setup_read(void) { // Disable modulation at default, which means enable the field LOW(GPIO_SSC_DOUT); - + // Enable Peripheral Clock for // TIMER_CLOCK0, used to measure exact timing before answering // TIMER_CLOCK1, used to capture edges of the tag frames @@ -221,17 +215,17 @@ static void em4x50_setup_read(void) { // TC0: Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), no triggers AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK; - + // TC1: Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), no triggers AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK; - + // Enable and reset counters AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - + // synchronized startup procedure while (AT91C_BASE_TC0->TC_CV > 0) {}; // wait until TC1 returned to zero - + // Watchdog hit WDT_HIT(); } @@ -239,21 +233,21 @@ static void em4x50_setup_read(void) { // functions for "reader" use case static bool get_signalproperties(void) { - + // calculate signal properties (mean amplitudes) from measured data: // 32 amplitudes (maximum values) -> mean amplitude value -> gHigh -> gLow - + bool signal_found = false; int no_periods = 32, pct = 75, noise = 140; - uint8_t sample = 0, sample_ref = 127; + uint8_t sample_ref = 127; uint8_t sample_max_mean = 0; uint8_t sample_max[no_periods]; uint32_t sample_max_sum = 0; + memcpy(sample_max, 0x00, sizeof(sample_max)); - // wait until signal/noise > 1 (max. 32 periods) for (int i = 0; i < T0 * no_periods; i++) { - + // about 2 samples per bit period wait_timer(0, T0 * EM4X50_T_TAG_HALF_PERIOD); @@ -261,9 +255,9 @@ static bool get_signalproperties(void) { signal_found = true; break; } - + } - + if (!signal_found) return false; @@ -273,39 +267,36 @@ static bool get_signalproperties(void) { AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; while (AT91C_BASE_TC0->TC_CV < T0 * 3 * EM4X50_T_TAG_FULL_PERIOD) { - - sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - + + volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (sample > sample_max[i]) sample_max[i] = sample; - + } - + sample_max_sum += sample_max[i]; } - + sample_max_mean = sample_max_sum / no_periods; - + // set global envelope variables gHigh = sample_ref + pct * (sample_max_mean - sample_ref) / 100; gLow = sample_ref - pct * (sample_max_mean - sample_ref) / 100; - return true; } static int get_next_bit(void) { - + // returns bit value (or EM4X50_BIT_OTHER -> no bit pattern) by evaluating // a single sample within a bit period (given there is no LIW, ACK or NAK) // This function is not used for decoding, it is only used for identifying // a listen window (return value = EM4X50_BIT_OTHER) in functions // "find_double_listen_window" and "check_ack" - - uint8_t sample; // get sample at 3/4 of bit period wait_timer(0, T0 * EM4X50_T_TAG_THREE_QUARTER_PERIOD); - sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; // wait until end of bit period wait_timer(0, T0 * EM4X50_T_TAG_QUARTER_PERIOD); @@ -315,7 +306,7 @@ static int get_next_bit(void) { return EM4X50_BIT_0; else if (sample < gLow) return EM4X50_BIT_1; - + return EM4X50_BIT_OTHER; } From 86538912a840a1cec8730e2a5c6dc746d16e3cb4 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 12:18:26 +0200 Subject: [PATCH 043/139] make sure uninitialized vars doesnt end up on COMMON_section --- armsrc/Makefile | 2 +- bootrom/Makefile | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/armsrc/Makefile b/armsrc/Makefile index 2768ab3a4..8b1fbfc2b 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -20,7 +20,7 @@ endif #in the next section to remove that particular feature from compilation. # NO space,TABs after the "\" sign. APP_CFLAGS = $(PLATFORM_DEFS) \ - -ffunction-sections -fdata-sections + -ffunction-sections -fdata-sections -fno-common SRC_LF = lfops.c lfsampling.c pcf7931.c lfdemod.c lfadc.c SRC_ISO15693 = iso15693.c iso15693tools.c diff --git a/bootrom/Makefile b/bootrom/Makefile index 75a4e5ced..9a0359d2f 100644 --- a/bootrom/Makefile +++ b/bootrom/Makefile @@ -25,8 +25,8 @@ VERSIONSRC = version.c # stdint.h provided locally until GCC 4.5 becomes C99 compliant APP_CFLAGS = -I. -ffunction-sections -fdata-sections -# stack-protect , no-pie reduces size on Gentoo Hardened 8.2 gcc -APP_CFLAGS += -fno-stack-protector -fno-pie +# stack-protect , no-pie reduces size on Gentoo Hardened 8.2 gcc, no-common makes sure uninitalized vars doesn't end up in COMMON area +APP_CFLAGS += -fno-stack-protector -fno-pie -fno-common # Do not move this inclusion before the definition of {THUMB,ASM,ARM}SRC From 1f8ab9c2fbb17c085cb30e99fffd090ce58f0616 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 12:19:06 +0200 Subject: [PATCH 044/139] better logtext for debug, iclass dump -> NG --- armsrc/appmain.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index a50c0ba7d..e35dc8ae2 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -69,13 +69,11 @@ //============================================================================= #define TOSEND_BUFFER_SIZE (9*MAX_FRAME_SIZE + 1 + 1 + 2) // 8 data bits and 1 parity bit per payload byte, 1 correction bit, 1 SOC bit, 2 EOC bits -uint8_t ToSend[TOSEND_BUFFER_SIZE]; +uint8_t ToSend[TOSEND_BUFFER_SIZE] = {0}; int ToSendMax = -1; +static int ToSendBit; extern uint32_t _stack_start, _stack_end; - - -static int ToSendBit; struct common_area common_area __attribute__((section(".commonarea"))); static int button_status = BUTTON_NO_CLICK; static bool allow_send_wtx = false; @@ -375,7 +373,26 @@ static void SendStatus(void) { print_stack_usage(); - Dbprintf(" DBGLEVEL................%d", DBGLEVEL); + char dbglvlstr[20] = {0}; + switch(DBGLEVEL) { + case DBG_NONE: + sprintf(dbglvlstr, "NONE"); + break; + case DBG_ERROR: + sprintf(dbglvlstr, "ERROR"); + break; + case DBG_INFO: + sprintf(dbglvlstr, "INFO"); + break; + case DBG_DEBUG: + sprintf(dbglvlstr, "DEBUG"); + break; + case DBG_EXTENDED: + sprintf(dbglvlstr, "EXTENDED"); + break; + } + Dbprintf(" DBGLEVEL................%d ( " _YELLOW_("%s")" )", DBGLEVEL, dbglvlstr); + Dbprintf(" ToSendMax...............%d", ToSendMax); Dbprintf(" ToSendBit...............%d", ToSendBit); Dbprintf(" ToSend BUFFERSIZE.......%d", TOSEND_BUFFER_SIZE); @@ -1453,7 +1470,12 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_ICLASS_DUMP: { - iClass_Dump(packet->oldarg[0], packet->oldarg[1]); + struct p { + uint8_t start_blockno; + uint8_t numblks; + } PACKED; + struct p *payload = (struct p *)packet->data.asBytes; + iClass_Dump(payload->start_blockno, payload->numblks); break; } case CMD_HF_ICLASS_CLONE: { From 27c4d994ac12bbe5e50e56f31fc3ed90bb198514 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 12:20:12 +0200 Subject: [PATCH 045/139] remove some debugstatments, use hdr.frame instead --- armsrc/BigBuf.c | 22 +++++++++------------- armsrc/BigBuf.h | 2 +- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 8637443a0..24fe45e57 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -164,7 +164,7 @@ uint32_t BigBuf_get_traceLen(void) { **/ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag) { if (tracing == false) { - if (DBGLEVEL >= DBG_DEBUG) { Dbprintf("trace is turned off"); } + Dbprintf("trace is turned off"); return false; } @@ -175,8 +175,7 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ // Return when trace is full if (TRACELOG_HDR_LEN + iLen + num_paritybytes >= BigBuf_max_traceLen() - trace_len) { - tracing = false; // don't trace any more - if (DBGLEVEL >= DBG_DEBUG) { Dbprintf("trace is full"); } + tracing = false; return false; } @@ -188,27 +187,24 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ } if (duration > 0x7FFF) { - if (DBGLEVEL >= DBG_DEBUG) { - Dbprintf("Error in LogTrace: duration too long for 15 bits encoding: 0x%08x start:0x%08x end:0x%08x", duration, timestamp_start, timestamp_end); -// Dbprintf("Forcing duration = 0"); + if (DBGLEVEL >= DBG_ERROR) { //DBG_DEBUG + Dbprintf("Error in LogTrace: duration too long for 15 bits encoding: 0x%08x start: 0x%08x end: 0x%08x", duration, timestamp_start, timestamp_end); } - duration /= 32; - // duration >>= 5; // duration = 0; } - + hdr->timestamp = timestamp_start; - hdr->duration = duration; + hdr->duration = duration & 0x7FFF; hdr->data_len = iLen; hdr->isResponse = !readerToTag; trace_len += TRACELOG_HDR_LEN; // data bytes if (btBytes != NULL && iLen != 0) { - memcpy(trace + trace_len, btBytes, iLen); + memcpy(hdr->frame, btBytes, iLen); + trace_len += iLen; } - trace_len += iLen; // parity bytes if (num_paritybytes != 0) { @@ -217,8 +213,8 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ } else { memset(trace + trace_len, 0x00, num_paritybytes); } + trace_len += num_paritybytes; } - trace_len += num_paritybytes; return true; } diff --git a/armsrc/BigBuf.h b/armsrc/BigBuf.h index 60857e82a..b89b94242 100644 --- a/armsrc/BigBuf.h +++ b/armsrc/BigBuf.h @@ -19,7 +19,7 @@ #define MAX_MIFARE_FRAME_SIZE 18 // biggest Mifare frame is answer to a read (one block = 16 Bytes) + 2 Bytes CRC #define MAX_MIFARE_PARITY_SIZE 3 // need 18 parity bits for the 18 Byte above. 3 Bytes are enough to store these #define CARD_MEMORY_SIZE 4096 -#define DMA_BUFFER_SIZE 256 //128 (how big is the dma?!? +#define DMA_BUFFER_SIZE 128 uint8_t *BigBuf_get_addr(void); uint32_t BigBuf_get_size(void); From 77aa5c614208e6801cd69a51ea31fe786ad4c96b Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 12:20:35 +0200 Subject: [PATCH 046/139] style --- armsrc/iso14443a.c | 1 - 1 file changed, 1 deletion(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 836360740..0dae3c872 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2554,7 +2554,6 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { hf_field_active = true; } - void hf_field_off(void) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); From 713301226dd5ff6049898115ca9804930c9261cd Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 12:21:42 +0200 Subject: [PATCH 047/139] fgpa merge hell p.N --- armsrc/iclass.c | 510 +++++++++++++++++++++++------------------------- 1 file changed, 246 insertions(+), 264 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index a68565830..58cf36a0d 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -15,27 +15,6 @@ // // Please feel free to contribute and extend iClass support!! //----------------------------------------------------------------------------- -// -// FIX: -// ==== -// We still have sometimes a demodulation error when sniffing iClass communication. -// The resulting trace of a read-block-03 command may look something like this: -// -// + 22279: : 0c 03 e8 01 -// -// ...with an incorrect answer... -// -// + 85: 0: TAG ff! ff! ff! ff! ff! ff! ff! ff! bb 33 bb 00 01! 0e! 04! bb !crc -// -// We still left the error signalling bytes in the traces like 0xbb -// -// A correct trace should look like this: -// -// + 21112: : 0c 03 e8 01 -// + 85: 0: TAG ff ff ff ff ff ff ff ff ea f5 -// -//----------------------------------------------------------------------------- - #include "iclass.h" #include "proxmark3_arm.h" @@ -78,11 +57,6 @@ #define AddCrc(data, len) compute_crc(CRC_ICLASS, (data), (len), (data)+(len), (data)+(len)+1) -static void OnError(uint8_t reason) { - reply_mix(CMD_ACK, 0, reason, 0, 0, 0); - switch_off(); -} - /* * CARD TO READER * in ISO15693-2 mode - Manchester @@ -116,9 +90,9 @@ static void rotateCSN(uint8_t *original_csn, uint8_t *rotated_csn) { // Encode SOF only static void CodeIClassTagSOF(void) { - ToSendReset(); - ToSend[++ToSendMax] = 0x1D; - ToSendMax++; + ToSendReset(); + ToSend[++ToSendMax] = 0x1D; + ToSendMax++; } @@ -177,7 +151,7 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain LEDsoff(); - Iso15693InitTag(); + Iso15693InitTag(); clear_trace(); set_tracing(true); @@ -280,13 +254,13 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { // free eventually allocated BigBuf memory BigBuf_free_keep_EM(); - uint16_t page_size = 32 * 8; - uint8_t current_page = 0; + uint16_t page_size = 32 * 8; + uint8_t current_page = 0; - // maintain cipher states for both credit and debit key for each page - State cipher_state_KD[8]; - State cipher_state_KC[8]; - State *cipher_state = &cipher_state_KD[0]; + // maintain cipher states for both credit and debit key for each page + State cipher_state_KD[8]; + State cipher_state_KC[8]; + State *cipher_state = &cipher_state_KD[0]; uint8_t *emulator = BigBuf_get_EM_addr(); uint8_t *csn = emulator; @@ -303,12 +277,12 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { AddCrc(anticoll_data, 8); AddCrc(csn_data, 8); - uint8_t diversified_kd[8] = { 0 }; - uint8_t diversified_kc[8] = { 0 }; - uint8_t *diversified_key = diversified_kd; + uint8_t diversified_kd[8] = { 0 }; + uint8_t diversified_kc[8] = { 0 }; + uint8_t *diversified_key = diversified_kd; - // configuration block - uint8_t conf_block[10] = {0x12, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0xFF, 0x3C, 0x00, 0x00}; + // configuration block + uint8_t conf_block[10] = {0x12, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0xFF, 0x3C, 0x00, 0x00}; // e-Purse uint8_t card_challenge_data[8] = { 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; @@ -318,10 +292,10 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { if (simulationMode == ICLASS_SIM_MODE_FULL) { - memcpy(conf_block, emulator + (8 * 1), 8); // blk 1 - memcpy(card_challenge_data, emulator + (8 * 2), 8); // e-purse, blk 2 - memcpy(diversified_kd, emulator + (8 * 3), 8); // Kd, blk 3 - memcpy(diversified_kc, emulator + (8 * 4), 8); // Kc, blk 4 + memcpy(conf_block, emulator + (8 * 1), 8); // blk 1 + memcpy(card_challenge_data, emulator + (8 * 2), 8); // e-purse, blk 2 + memcpy(diversified_kd, emulator + (8 * 3), 8); // Kd, blk 3 + memcpy(diversified_kc, emulator + (8 * 4), 8); // Kc, blk 4 // (iceman) this only works for 2KS / 16KS tags. @@ -338,36 +312,36 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { memcpy(reader_mac_buf, card_challenge_data, 8); } - if ((conf_block[5] & 0x80) == 0x80) { - page_size = 256 * 8; - } + if ((conf_block[5] & 0x80) == 0x80) { + page_size = 256 * 8; + } - // From PicoPass DS: - // When the page is in personalization mode this bit is equal to 1. - // Once the application issuer has personalized and coded its dedicated areas, this bit must be set to 0: - // the page is then "in application mode". - bool personalization_mode = conf_block[7] & 0x80; + // From PicoPass DS: + // When the page is in personalization mode this bit is equal to 1. + // Once the application issuer has personalized and coded its dedicated areas, this bit must be set to 0: + // the page is then "in application mode". + bool personalization_mode = conf_block[7] & 0x80; - // chip memory may be divided in 8 pages - uint8_t max_page = ((conf_block[4] & 0x10) == 0x10) ? 0 : 7; + // chip memory may be divided in 8 pages + uint8_t max_page = ((conf_block[4] & 0x10) == 0x10) ? 0 : 7; - // Precalculate the cipher states, feeding it the CC - cipher_state_KD[0] = opt_doTagMAC_1(card_challenge_data, diversified_kd); - cipher_state_KC[0] = opt_doTagMAC_1(card_challenge_data, diversified_kc); + // Precalculate the cipher states, feeding it the CC + cipher_state_KD[0] = opt_doTagMAC_1(card_challenge_data, diversified_kd); + cipher_state_KC[0] = opt_doTagMAC_1(card_challenge_data, diversified_kc); - if (simulationMode == ICLASS_SIM_MODE_FULL) { + if (simulationMode == ICLASS_SIM_MODE_FULL) { - for (int i = 1; i < max_page; i++) { + for (int i = 1; i < max_page; i++) { // does all pages has their own epurse??) - uint8_t *epurse = emulator + (i * page_size) + (8 * 2); - uint8_t *kd = emulator + (i * page_size) + (8 * 3); - uint8_t *kc = emulator + (i * page_size) + (8 * 4); + uint8_t *epurse = emulator + (i * page_size) + (8 * 2); + uint8_t *kd = emulator + (i * page_size) + (8 * 3); + uint8_t *kc = emulator + (i * page_size) + (8 * 4); - cipher_state_KD[i] = opt_doTagMAC_1(epurse, kd); - cipher_state_KC[i] = opt_doTagMAC_1(epurse, kc); - } - } + cipher_state_KD[i] = opt_doTagMAC_1(epurse, kd); + cipher_state_KC[i] = opt_doTagMAC_1(epurse, kc); + } + } // Anti-collision process: // Reader 0a @@ -405,11 +379,11 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { uint8_t *resp_cc = BigBuf_malloc(28); int resp_cc_len; - // Kd, Kc (blocks 3 and 4). Cannot be read. Always respond with 0xff bytes only - uint8_t *resp_ff = BigBuf_malloc(22); - int resp_ff_len; - uint8_t ff_data[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; - AddCrc(ff_data, 8); + // Kd, Kc (blocks 3 and 4). Cannot be read. Always respond with 0xff bytes only + uint8_t *resp_ff = BigBuf_malloc(22); + int resp_ff_len; + uint8_t ff_data[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; + AddCrc(ff_data, 8); // Application Issuer Area (blk 5) uint8_t *resp_aia = BigBuf_malloc(28); @@ -446,10 +420,10 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { memcpy(resp_cc, ToSend, ToSendMax); resp_cc_len = ToSendMax; - // Kd, Kc (blocks 3 and 4) - CodeIso15693AsTag(ff_data, sizeof(ff_data)); - memcpy(resp_ff, ToSend, ToSendMax); - resp_ff_len = ToSendMax; + // Kd, Kc (blocks 3 and 4) + CodeIso15693AsTag(ff_data, sizeof(ff_data)); + memcpy(resp_ff, ToSend, ToSendMax); + resp_ff_len = ToSendMax; // Application Issuer Area (block 5) CodeIso15693AsTag(aia_data, sizeof(aia_data)); @@ -464,7 +438,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { //Each bit is doubled when modulated for FPGA, and we also have SOF and EOF (2 bytes) uint8_t *data_response = BigBuf_malloc((32 + 2) * 2 + 2); - enum { IDLE, ACTIVATED, SELECTED, HALTED } chip_state = IDLE; + enum { IDLE, ACTIVATED, SELECTED, HALTED } chip_state = IDLE; bool button_pressed = false; uint8_t cmd, options, block; @@ -474,20 +448,20 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { while (exit_loop == false) { WDT_HIT(); - uint32_t reader_eof_time = 0; - len = GetIso15693CommandFromReader(receivedCmd, MAX_FRAME_SIZE, &reader_eof_time); + uint32_t reader_eof_time = 0; + len = GetIso15693CommandFromReader(receivedCmd, MAX_FRAME_SIZE, &reader_eof_time); if (len < 0) { button_pressed = true; exit_loop = true; continue; } - // Now look at the reader command and provide appropriate responses - // default is no response: - modulated_response = NULL; - modulated_response_size = 0; - trace_data = NULL; - trace_data_size = 0; + // Now look at the reader command and provide appropriate responses + // default is no response: + modulated_response = NULL; + modulated_response_size = 0; + trace_data = NULL; + trace_data_size = 0; // extra response data cmd = receivedCmd[0] & 0xF; @@ -499,7 +473,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { if (chip_state != HALTED) { modulated_response = resp_sof; modulated_response_size = resp_sof_len; - chip_state = ACTIVATED; + chip_state = ACTIVATED; goto send; } @@ -544,13 +518,13 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } goto send; } - case 3: - case 4: { // Kd, Kc, always respond with 0xff bytes - modulated_response = resp_ff; - modulated_response_size = resp_ff_len; - trace_data = ff_data; - trace_data_size = sizeof(ff_data); - goto send; + case 3: + case 4: { // Kd, Kc, always respond with 0xff bytes + modulated_response = resp_ff; + modulated_response_size = resp_ff_len; + trace_data = ff_data; + trace_data_size = sizeof(ff_data); + goto send; } case 5: { // Application Issuer Area (0c 05) modulated_response = resp_aia; @@ -588,7 +562,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } else if (cmd == ICLASS_CMD_READCHECK) { // 0x88 // Read e-purse KD (88 02) KC (18 02) - if (chip_state == SELECTED) { + if (chip_state == SELECTED) { if ( ICLASS_DEBIT(cmd) ){ cipher_state = &cipher_state_KD[current_page]; diversified_key = diversified_kd; @@ -601,7 +575,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { trace_data = card_challenge_data; trace_data_size = sizeof(card_challenge_data); goto send; - } + } } else if (cmd == ICLASS_CMD_CHECK) { // 0x05 // Reader random and reader MAC!!! @@ -645,7 +619,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } else if (cmd == ICLASS_CMD_HALT && options == 0 && len == 1) { - if (chip_state == SELECTED) { + if (chip_state == SELECTED) { // Reader ends the session modulated_response = resp_sof; modulated_response_size = resp_sof_len; @@ -655,7 +629,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } else if (simulationMode == ICLASS_SIM_MODE_FULL && cmd == ICLASS_CMD_READ4 && len == 4) { // 0x06 - if (chip_state == SELECTED) { + if (chip_state == SELECTED) { //Read block memcpy(data_generic_trace, emulator + (current_page * page_size) + (block * 8), 8 * 4); AddCrc(data_generic_trace, 8 * 4); @@ -669,86 +643,86 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } else if (simulationMode == ICLASS_SIM_MODE_FULL && cmd == ICLASS_CMD_UPDATE && (len == 12 || len == 14)) { - // We're expected to respond with the data+crc, exactly what's already in the receivedCmd - // receivedCmd is now UPDATE 1b | ADDRESS 1b | DATA 8b | Signature 4b or CRC 2b - if (chip_state == SELECTED) { + // We're expected to respond with the data+crc, exactly what's already in the receivedCmd + // receivedCmd is now UPDATE 1b | ADDRESS 1b | DATA 8b | Signature 4b or CRC 2b + if (chip_state == SELECTED) { - if (block == 2) { // update e-purse - memcpy(card_challenge_data, receivedCmd + 2, 8); - CodeIso15693AsTag(card_challenge_data, sizeof(card_challenge_data)); - memcpy(resp_cc, ToSend, ToSendMax); - resp_cc_len = ToSendMax; - cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd); - cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kc); + if (block == 2) { // update e-purse + memcpy(card_challenge_data, receivedCmd + 2, 8); + CodeIso15693AsTag(card_challenge_data, sizeof(card_challenge_data)); + memcpy(resp_cc, ToSend, ToSendMax); + resp_cc_len = ToSendMax; + cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd); + cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kc); } else if (block == 3) { // update Kd - for (int i = 0; i < 8; i++) { - if (personalization_mode) { - diversified_kd[i] = receivedCmd[2 + i]; - } else { - diversified_kd[i] ^= receivedCmd[2 + i]; - } - } - cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd); + for (int i = 0; i < 8; i++) { + if (personalization_mode) { + diversified_kd[i] = receivedCmd[2 + i]; + } else { + diversified_kd[i] ^= receivedCmd[2 + i]; + } + } + cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd); - } else if (block == 4) { // update Kc - for (int i = 0; i < 8; i++) { - if (personalization_mode) { - diversified_kc[i] = receivedCmd[2 + i]; - } else { - diversified_kc[i] ^= receivedCmd[2 + i]; - } - } - cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kc); + } else if (block == 4) { // update Kc + for (int i = 0; i < 8; i++) { + if (personalization_mode) { + diversified_kc[i] = receivedCmd[2 + i]; + } else { + diversified_kc[i] ^= receivedCmd[2 + i]; + } + } + cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kc); } // update emulator memcpy(emulator + (current_page * page_size) + (8 * block), receivedCmd + 2, 8); - memcpy(data_generic_trace, receivedCmd + 2, 8); - AddCrc(data_generic_trace, 8); - trace_data = data_generic_trace; - trace_data_size = 10; - CodeIso15693AsTag(trace_data, trace_data_size); - memcpy(data_response, ToSend, ToSendMax); - modulated_response = data_response; - modulated_response_size = ToSendMax; - } + memcpy(data_generic_trace, receivedCmd + 2, 8); + AddCrc(data_generic_trace, 8); + trace_data = data_generic_trace; + trace_data_size = 10; + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ToSend, ToSendMax); + modulated_response = data_response; + modulated_response_size = ToSendMax; + } goto send; } else if (receivedCmd[0] == ICLASS_CMD_PAGESEL && len == 4) { // 0x84 - // Pagesel, + // Pagesel, // - enables to select a page in the selected chip memory and return its configuration block - // Chips with a single page will not answer to this command - // Otherwise, we should answer 8bytes (conf block 1) + 2bytes CRC - if (chip_state == SELECTED) { + // Chips with a single page will not answer to this command + // Otherwise, we should answer 8bytes (conf block 1) + 2bytes CRC + if (chip_state == SELECTED) { - if (simulationMode == ICLASS_SIM_MODE_FULL && max_page > 0) { + if (simulationMode == ICLASS_SIM_MODE_FULL && max_page > 0) { - current_page = receivedCmd[1]; + current_page = receivedCmd[1]; - memcpy(data_generic_trace, emulator + (current_page * page_size) + (8 * 1), 8); - memcpy(diversified_kd, emulator + (current_page * page_size) + (8 * 3), 8); - memcpy(diversified_kc, emulator + (current_page * page_size) + (8 * 4), 8); + memcpy(data_generic_trace, emulator + (current_page * page_size) + (8 * 1), 8); + memcpy(diversified_kd, emulator + (current_page * page_size) + (8 * 3), 8); + memcpy(diversified_kc, emulator + (current_page * page_size) + (8 * 4), 8); - cipher_state = &cipher_state_KD[current_page]; + cipher_state = &cipher_state_KD[current_page]; - personalization_mode = data_generic_trace[7] & 0x80; - AddCrc(data_generic_trace, 8); + personalization_mode = data_generic_trace[7] & 0x80; + AddCrc(data_generic_trace, 8); - trace_data = data_generic_trace; - trace_data_size = 10; + trace_data = data_generic_trace; + trace_data_size = 10; - CodeIso15693AsTag(trace_data, trace_data_size); - memcpy(data_response, ToSend, ToSendMax); - modulated_response = data_response; - modulated_response_size = ToSendMax; - } - } + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ToSend, ToSendMax); + modulated_response = data_response; + modulated_response_size = ToSendMax; + } + } // } else if(receivedCmd[0] == ICLASS_CMD_DETECT) { // 0x0F - } else if (receivedCmd[0] == 0x26 && len == 5) { - // standard ISO15693 INVENTORY command. Ignore. + } else if (receivedCmd[0] == 0x26 && len == 5) { + // standard ISO15693 INVENTORY command. Ignore. } else { // Never seen this command before if (DBGLEVEL >= DBG_EXTENDED) @@ -762,7 +736,7 @@ send: if (modulated_response_size > 0) { uint32_t response_time = reader_eof_time + DELAY_ICLASS_VCD_TO_VICC_SIM; TransmitTo15693Reader(modulated_response, modulated_response_size, &response_time, 0, false); - LogTrace(trace_data, trace_data_size, response_time * 32, (response_time * 32) + (modulated_response_size * 32 * 64), NULL, false); + LogTrace(trace_data, trace_data_size, response_time * 32, (response_time * 32) + (modulated_response_size * 32 * 64), NULL, false); } } @@ -776,20 +750,16 @@ send: // THE READER CODE -// logs. static void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time) { - - CodeIso15693AsReader(frame, len); - TransmitTo15693Tag(ToSend, ToSendMax, start_time); - uint32_t end_time = *start_time + (32 * ((8 * ToSendMax) - 4)); // substract the 4 padding bits after EOF - - if (LogTrace(frame, len, (*start_time * 4), (end_time * 4), NULL, true) == false) - DbpString("send_as_reader: failed logtrace"); + CodeIso15693AsReader(frame, len); + TransmitTo15693Tag(ToSend, ToSendMax, start_time); + uint32_t end_time = *start_time + (32 * ((8 * ToSendMax) - 4)); // substract the 4 padding bits after EOF + LogTrace(frame, len, (*start_time * 4), (end_time * 4), NULL, true); } static bool iclass_send_cmd_with_retries(uint8_t* cmd, size_t cmdsize, uint8_t* resp, size_t max_resp_size, - uint8_t expected_size, uint8_t tries, uint32_t start_time, - uint32_t timeout, uint32_t *eof_time) { + uint8_t expected_size, uint8_t tries, uint32_t start_time, + uint16_t timeout, uint32_t *eof_time) { while (tries-- > 0) { iclass_send_as_reader(cmd, cmdsize, &start_time); @@ -797,9 +767,10 @@ static bool iclass_send_cmd_with_retries(uint8_t* cmd, size_t cmdsize, uint8_t* if (resp == NULL) return true; - if (expected_size == GetIso15693AnswerFromTag(resp, max_resp_size, timeout, eof_time)) { + if (expected_size == GetIso15693AnswerFromTag(resp, max_resp_size, timeout, eof_time)) { return true; } + start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; } return false; } @@ -816,7 +787,7 @@ static bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t static uint8_t act_all[] = { ICLASS_CMD_ACTALL }; static uint8_t identify[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x00, 0x73, 0x33 }; static uint8_t select[] = { 0x80 | ICLASS_CMD_SELECT, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - static uint8_t read_conf[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x01, 0xfa, 0x22 }; + static uint8_t read_conf[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x01, 0xfa, 0x22 }; static uint8_t read_check_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; @@ -826,19 +797,26 @@ static bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t if (use_credit_key) read_check_cc[0] = 0x10 | ICLASS_CMD_READCHECK; - uint32_t start_time = GetCountSspClk(); - iclass_send_as_reader(act_all, sizeof(act_all), &start_time); + set_tracing(true); - // card present? - int len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_ACTALL, eof_time); - if (len < 0) { - Dbprintf("Fail act all (%d)", len); - return false; - } + int len; + uint32_t start_time; + uint8_t tries = 10; + do { + // wakeup + start_time = GetCountSspClk(); + iclass_send_as_reader(act_all, 1, &start_time); + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_ACTALL, eof_time); + if (len >= 0) { + break; + } else if (len == -2) { + return false; + } + } while (tries-- > 0); // send Identify start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(identify, 1, &start_time); + iclass_send_as_reader(identify, 1, &start_time); // expect a 10-byte response here, 8 byte anticollision-CSN and 2 byte CRC len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); @@ -849,36 +827,36 @@ static bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t memcpy(&select[1], resp, 8); // select the card - start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(select, sizeof(select), &start_time); + start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(select, sizeof(select), &start_time); // expect a 10-byte response here, 8 byte CSN and 2 byte CRC - len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); - if (len != 10) + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + if (len != 10) return false; //Save CSN in response data memcpy(card_data, resp, 8); // card selected, now read config (block1) (only 8 bytes no CRC) - start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(read_conf, sizeof(read_conf), &start_time); + start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_conf, sizeof(read_conf), &start_time); // expect a 8-byte response here - len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); - if (len != 10) + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + if (len != 10) return false; //Save CONF in response data memcpy(card_data + 8, resp, 8); // card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) - start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time); + start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time); // expect a 8-byte response here - len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); - if (len != 8) + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + if (len != 8) return false; //Save CC (e-purse) in response data @@ -893,25 +871,23 @@ void ReaderIClass(uint8_t flags) { uint8_t card_data[6 * 8] = {0xFF}; // uint8_t last_csn[8] = {0, 0, 0, 0, 0, 0, 0, 0}; uint8_t resp[ICLASS_BUFFER_SIZE]; - -// memset(card_data, 0xFF, sizeof(card_data)); memset(resp, 0xFF, sizeof(resp)); // bool flag_readonce = flags & FLAG_ICLASS_READER_ONLY_ONCE; // flag to read until one tag is found successfully bool use_credit_key = flags & FLAG_ICLASS_READER_CEDITKEY; // flag to use credit key bool flag_read_aia = flags & FLAG_ICLASS_READER_AIA; // flag to read block5, application issuer area - if ((flags & FLAG_ICLASS_READER_INIT) == FLAG_ICLASS_READER_INIT) { - switch_off(); - Iso15693InitReader(); - StartCountSspClk(); - } - - if ((flags & FLAG_ICLASS_READER_CLEARTRACE) == FLAG_ICLASS_READER_CLEARTRACE) { - clear_trace(); - } + if ((flags & FLAG_ICLASS_READER_INIT) == FLAG_ICLASS_READER_INIT) { + Iso15693InitReader(); + } - uint32_t eof_time = 0; + set_tracing(true); + + if ((flags & FLAG_ICLASS_READER_CLEARTRACE) == FLAG_ICLASS_READER_CLEARTRACE) { + clear_trace(); + } + + uint32_t eof_time = 0; bool status = select_iclass_tag(card_data, use_credit_key, &eof_time); if (status == false) { reply_mix(CMD_ACK, 0xFF, 0, 0, card_data, 0); @@ -926,7 +902,7 @@ void ReaderIClass(uint8_t flags) { if (flag_read_aia) { //Read App Issuer Area block CRC(0x05) => 0xde 0x64 uint8_t read_aa[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x05, 0xde, 0x64}; - status = iclass_send_cmd_with_retries(read_aa, sizeof(read_aa), resp, sizeof(resp), 10, 10, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + status = iclass_send_cmd_with_retries(read_aa, sizeof(read_aa), resp, sizeof(resp), 10, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); if (status) { result_status |= FLAG_ICLASS_AIA; memcpy(card_data + (8 * 5), resp, 8); @@ -944,7 +920,7 @@ void ReaderIClass(uint8_t flags) { // //Then we can 'ship' back the 6 * 8 bytes of data, // with 0xFF:s in block 3 and 4. - + LED_B_ON(); reply_mix(CMD_ACK, result_status, 0, 0, card_data, sizeof(card_data)); @@ -968,7 +944,8 @@ void ReaderIClass(uint8_t flags) { // } else { // reply_mix(CMD_ACK, result_status, 0, 0, card_data, 0); // } - switch_off(); + + switch_off(); } // turn off afterwards @@ -991,8 +968,8 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { int keyaccess; } memory; - uint32_t start_time = 0; - uint32_t eof_time = 0; + uint32_t start_time = 0; + uint32_t eof_time = 0; while (BUTTON_PRESS() == false) { WDT_HIT(); @@ -1105,8 +1082,9 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { void iClass_ReadCheck(uint8_t blockno, uint8_t keytype) { uint8_t readcheck[] = { keytype, blockno }; uint8_t resp[8] = {0}; - uint32_t eof_time = 0; - bool isOK = iclass_send_cmd_with_retries(readcheck, sizeof(readcheck), resp, sizeof(resp), 8, 3, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + uint32_t eof_time = 0; +// start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + bool isOK = iclass_send_cmd_with_retries(readcheck, sizeof(readcheck), resp, sizeof(resp), 8, 3, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); reply_mix(CMD_ACK, isOK, 0, 0, 0, 0); switch_off(); } @@ -1131,15 +1109,14 @@ void iClass_Authentication(uint8_t *bytes) { uint8_t mac[4]; } PACKED packet; - Iso15693InitReader(); - StartCountSspClk(); + Iso15693InitReader(); uint8_t card_data[3 * 8] = {0xFF}; uint32_t eof_time = 0; packet.isOK = select_iclass_tag(card_data, payload->use_credit_key, &eof_time); if (packet.isOK == false) { - reply_ng(CMD_HF_ICLASS_AUTH, PM3_SUCCESS, (uint8_t *)&packet, sizeof(packet)); + reply_ng(CMD_HF_ICLASS_AUTH, PM3_ESOFT, (uint8_t *)&packet, sizeof(packet)); return; } uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; @@ -1163,7 +1140,8 @@ void iClass_Authentication(uint8_t *bytes) { uint8_t resp[ICLASS_BUFFER_SIZE]; packet.isOK = iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); - reply_ng(CMD_HF_ICLASS_AUTH, PM3_SUCCESS, (uint8_t *)&packet, sizeof(packet)); + + reply_ng(CMD_HF_ICLASS_AUTH, (packet.isOK)? PM3_SUCCESS : PM3_ESOFT, (uint8_t *)&packet, sizeof(packet)); } typedef struct iclass_premac { @@ -1198,21 +1176,18 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { LED_A_ON(); + // fresh start switch_off(); SpinDelay(20); + + Iso15693InitReader(); - - bool read_status = false; uint32_t start_time = 0; uint32_t eof_time = 0; - uint8_t tries = 10; - while (tries-- > 0 || read_status == false) { - read_status = select_iclass_tag(card_data, use_credit_key, &eof_time); - } - - // failed to select card 10 times. return fail to client - if (read_status == false) + if (select_iclass_tag(card_data, use_credit_key, &eof_time) == false) goto out; + + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; // since select_iclass_tag call sends s readcheck, we start with sending first response. uint16_t checked = 0; @@ -1222,7 +1197,7 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { // Allow button press / usb cmd to interrupt device if (checked == 1000) { - if (BUTTON_PRESS() || !data_available()) goto out; + if (BUTTON_PRESS() || data_available()) goto out; checked = 0; } ++checked; @@ -1251,25 +1226,26 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { out: // send keyindex. - reply_mix(CMD_ACK, isOK, i, 0, 0, 0); + reply_mix(CMD_HF_ICLASS_CHKKEYS, isOK, i, 0, 0, 0); if (isOK >= 1 || lastChunk) { - switch_off(); LED_A_OFF(); } + switch_off(); + LED_B_OFF(); LED_C_OFF(); } // Tries to read block. // retries 10times. -static bool iClass_ReadBlock(uint8_t blockno, uint8_t *data) { +static bool iclass_readblock(uint8_t blockno, uint8_t *data) { uint8_t resp[10]; uint8_t c[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00}; AddCrc(c + 1, 1); uint32_t eof_time = 0; - bool isOK = iclass_send_cmd_with_retries(c, sizeof(c), resp, sizeof(resp), 10, 10, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + bool isOK = iclass_send_cmd_with_retries(c, sizeof(c), resp, sizeof(resp), 10, 10, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); memcpy(data, resp, 8); return isOK; } @@ -1283,24 +1259,25 @@ void iClass_ReadBlk(uint8_t blockno) { } PACKED result; LED_A_ON(); - result.isOK = iClass_ReadBlock(blockno, result.blockdata); + result.isOK = iclass_readblock(blockno, result.blockdata); switch_off(); reply_ng(CMD_HF_ICLASS_READBL, PM3_SUCCESS, (uint8_t *)&result, sizeof(result)); } - + // Dump command seems to dump a block related portion of card memory. // I suppose it will need to do an authentatication to AA1, read its blocks by calling this. // then authenticate AA2, and read those blocks by calling this. // By the looks at it only 2K cards is supported, or first page dumps on larger cards. -// turn off afterwards +// turn off afterwards void iClass_Dump(uint8_t start_blockno, uint8_t numblks) { - BigBuf_free(); + BigBuf_free(); - uint8_t *dataout = BigBuf_malloc(0xFF * 8); + uint8_t *dataout = BigBuf_malloc(0xFF * 8); if (dataout == NULL) { DbpString("fail to allocate memory"); - OnError(1); + reply_ng(CMD_HF_ICLASS_DUMP, PM3_EMALLOC, NULL, 0); + switch_off(); return; } memset(dataout, 0xFF, 0xFF * 8); @@ -1308,24 +1285,29 @@ void iClass_Dump(uint8_t start_blockno, uint8_t numblks) { bool isOK; uint8_t blkcnt = 0; for (; blkcnt < numblks; blkcnt++) { - isOK = iClass_ReadBlock(start_blockno + blkcnt, dataout + (8 * blkcnt)); - - if (!isOK) { - isOK = iClass_ReadBlock(start_blockno + blkcnt, dataout + (8 * blkcnt)); - if (!isOK) { - Dbprintf("failed to read block %02X", start_blockno + blkcnt); - break; - } + isOK = iclass_readblock(start_blockno + blkcnt, dataout + (8 * blkcnt)); + if (isOK == false) { + Dbprintf("failed to read block %02X", start_blockno + blkcnt); + break; } } switch_off(); - // return pointer to dump memory in arg3 - // iceman: why not return | dataout - getbigbuf ? Should give exact location. - Dbprintf("ICE:: dataout, %u max trace %u, bb start %u, data-bb %u ", dataout, BigBuf_max_traceLen(), BigBuf_get_addr(), dataout - BigBuf_get_addr() ); - Dbprintf("ICE:: bb size %u, malloced %u (255*8)", BigBuf_get_size(), BigBuf_get_size() - (dataout - BigBuf_get_addr()) ); - reply_mix(CMD_ACK, isOK, blkcnt, BigBuf_max_traceLen(), 0, 0); + // return pointer to dump memory in arg3 + // iceman: why not return | dataout - getbigbuf ? Should give exact location. +// Dbprintf("ICE:: dataout, %u max trace %u, bb start %u, data-bb %u ", dataout, BigBuf_max_traceLen(), BigBuf_get_addr(), dataout - BigBuf_get_addr() ); +// Dbprintf("ICE:: bb size %u, malloced %u (255*8)", BigBuf_get_size(), BigBuf_get_size() - (dataout - BigBuf_get_addr()) ); +// reply_mix(CMD_ACK, isOK, blkcnt, BigBuf_max_traceLen(), 0, 0); + struct p { + bool isOK; + uint8_t block_cnt; + uint32_t bb_offset; + } PACKED payload; + payload.isOK = isOK; + payload.block_cnt = blkcnt; + payload.bb_offset = BigBuf_max_traceLen(); + reply_ng(CMD_HF_ICLASS_DUMP, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload)); BigBuf_free(); } @@ -1337,35 +1319,35 @@ static bool iClass_WriteBlock_ext(uint8_t blockno, uint8_t *data) { uint8_t resp[10] = {0}; uint32_t eof_time = 0; - bool isOK = iclass_send_cmd_with_retries(write, sizeof(write), resp, sizeof(resp), 10, 3, 0, ICLASS_READER_TIMEOUT_UPDATE, &eof_time); - if (isOK == false) { - return false; - } + bool isOK = iclass_send_cmd_with_retries(write, sizeof(write), resp, sizeof(resp), 10, 3, 0, ICLASS_READER_TIMEOUT_UPDATE, &eof_time); + if (isOK == false) { + return false; + } - uint8_t all_ff[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - if (blockno == 2) { + uint8_t all_ff[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + if (blockno == 2) { // check response. e-purse update swaps first and second half - if (memcmp(data + 4, resp, 4) || memcmp(data, resp + 4, 4)) { - return false; - } - } else if (blockno == 3 || blockno == 4) { + if (memcmp(data + 4, resp, 4) || memcmp(data, resp + 4, 4)) { + return false; + } + } else if (blockno == 3 || blockno == 4) { // check response. Key updates always return 0xffffffffffffffff - if (memcmp(all_ff, resp, 8)) { - return false; - } - } else { + if (memcmp(all_ff, resp, 8)) { + return false; + } + } else { // check response. All other updates return unchanged data - if (memcmp(data, resp, 8)) { - return false; - } - } + if (memcmp(data, resp, 8)) { + return false; + } + } return true; } // turn off afterwards void iClass_WriteBlock(uint8_t blockno, uint8_t *data) { - LED_A_ON(); + LED_A_ON(); uint8_t isOK = iClass_WriteBlock_ext(blockno, data); switch_off(); reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); From e055858fb8c865e36c7da691e9b8cff87235c540 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 12:25:44 +0200 Subject: [PATCH 048/139] clean up --- client/src/cmdtrace.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index 81840dfc1..7f854d303 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -200,6 +200,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr data_len = hdr->data_len; if (tracepos + TRACELOG_HDR_LEN + data_len + TRACELOG_PARITY_LEN(hdr) > traceLen) { + PrintAndLogEx(DEBUG, "trace pos offset %d larger than reported tracelen %d", tracepos + TRACELOG_HDR_LEN + data_len + TRACELOG_PARITY_LEN(hdr), traceLen); return traceLen; } @@ -390,19 +391,23 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr } } - if (DecodeMifareData(frame, data_len, parityBytes, hdr->isResponse, mfData, &mfDataLen)) { - memset(explanation, 0x00, sizeof(explanation)); - if (!hdr->isResponse) { - annotateIso14443a(explanation, sizeof(explanation), mfData, mfDataLen); + if (protocol == PROTO_MIFARE) { + if (DecodeMifareData(frame, data_len, parityBytes, hdr->isResponse, mfData, &mfDataLen)) { + memset(explanation, 0x00, sizeof(explanation)); + if (!hdr->isResponse) { + annotateIso14443a(explanation, sizeof(explanation), mfData, mfDataLen); + } + uint8_t crcc = iso14443A_CRC_check(hdr->isResponse, mfData, mfDataLen); + PrintAndLogEx(NORMAL, " | | * |%-72s | %-4s| %s", + sprint_hex_inrow_spaces(mfData, mfDataLen, 2), + (crcc == 0 ? "!crc" : (crcc == 1 ? " ok " : " ")), + explanation); } - uint8_t crcc = iso14443A_CRC_check(hdr->isResponse, mfData, mfDataLen); - PrintAndLogEx(NORMAL, " | | * |%-72s | %-4s| %s", - sprint_hex_inrow_spaces(mfData, mfDataLen, 2), - (crcc == 0 ? "!crc" : (crcc == 1 ? " ok " : " ")), - explanation); } - if (is_last_record(tracepos, traceLen)) return traceLen; + if (is_last_record(tracepos, traceLen)) { + return traceLen; + } if (showWaitCycles && !hdr->isResponse && next_record_is_response(tracepos, trace)) { @@ -420,7 +425,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr static int download_trace(void) { - if (!IfPm3Present()) { + if (IfPm3Present() == false) { PrintAndLogEx(FAILED, "You requested a trace upload in offline mode, consider using parameter '1' for working from Tracebuffer"); return PM3_EINVARG; } @@ -437,7 +442,7 @@ static int download_trace(void) { return PM3_EMALLOC; } - PrintAndLogEx(INFO, "downloading tracelog from device"); + PrintAndLogEx(INFO, "downloading tracelog data from device"); // Query for the size of the trace, downloading PM3_CMD_DATA_SIZE PacketResponseNG response; @@ -533,11 +538,6 @@ int CmdTraceList(const char *Cmd) { bool errors = false; uint8_t protocol = 0; char type[10] = {0}; - - //int tlen = param_getstr(Cmd,0,type); - //char param1 = param_getchar(Cmd, 1); - //char param2 = param_getchar(Cmd, 2); - char cmdp = 0; while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { @@ -651,7 +651,10 @@ int CmdTraceList(const char *Cmd) { PrintAndLogEx(NORMAL, " Start | End | Src | Data (! denotes parity error) | CRC | Annotation"); PrintAndLogEx(NORMAL, "------------+------------+-----+-------------------------------------------------------------------------+-----+--------------------"); - ClearAuthData(); + // clean authentication data used with the mifare classic decrypt fct + if (protocol == ISO_14443A || protocol == PROTO_MIFARE) + ClearAuthData(); + while (tracepos < g_traceLen) { tracepos = printTraceLine(tracepos, g_traceLen, g_trace, protocol, showWaitCycles, markCRCBytes); From e7127514db514ef5fd29d565057f71fa2ece7b2b Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 12:26:26 +0200 Subject: [PATCH 049/139] change debug --- armsrc/BigBuf.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 24fe45e57..8756e8171 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -28,7 +28,7 @@ Pointer to highest available memory: s_bigbuf_hi low 0x00 */ -static uint32_t s_bigbuf_size = 0; +static uint32_t s_bigbuf_size = 0; // High memory mark static uint32_t s_bigbuf_hi = 0; @@ -164,7 +164,6 @@ uint32_t BigBuf_get_traceLen(void) { **/ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag) { if (tracing == false) { - Dbprintf("trace is turned off"); return false; } @@ -187,7 +186,7 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ } if (duration > 0x7FFF) { - if (DBGLEVEL >= DBG_ERROR) { //DBG_DEBUG + if (DBGLEVEL >= DBG_ERROR) { Dbprintf("Error in LogTrace: duration too long for 15 bits encoding: 0x%08x start: 0x%08x end: 0x%08x", duration, timestamp_start, timestamp_end); } duration /= 32; From a87ec6bd521335cb9df1b275ad22ba8fe08c1d45 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 12:28:01 +0200 Subject: [PATCH 050/139] style --- armsrc/iso15693.c | 50 ++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 39a64a8c4..7c018554c 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -95,18 +95,12 @@ #define ISO15693_MAX_RESPONSE_LENGTH 36 // allows read single block with the maximum block size of 256bits. Read multiple blocks not supported yet #define ISO15693_MAX_COMMAND_LENGTH 45 // allows write single block with the maximum block size of 256bits. Write multiple blocks not supported yet - // 32 + 2 crc + 1 #define ISO15_MAX_FRAME 35 #define CMD_ID_RESP 5 #define CMD_READ_RESP 13 #define CMD_INV_RESP 12 -#define FrameSOF Iso15693FrameSOF -#define Logic0 Iso15693Logic0 -#define Logic1 Iso15693Logic1 -#define FrameEOF Iso15693FrameEOF - //#define Crc(data, len) Crc(CRC_15693, (data), (len)) #define CheckCrc15(data, len) check_crc(CRC_15693, (data), (len)) #define AddCrc15(data, len) compute_crc(CRC_15693, (data), (len), (data)+(len), (data)+(len)+1) @@ -132,7 +126,7 @@ void CodeIso15693AsReader(uint8_t *cmd, int n) { // data for (int i = 0; i < n; i++) { for (int j = 0; j < 8; j += 2) { - int these = (cmd[i] >> j) & 0x03; + uint8_t these = (cmd[i] >> j) & 0x03; switch(these) { case 0: ToSend[++ToSendMax] = 0x40; //01000000 @@ -152,7 +146,6 @@ void CodeIso15693AsReader(uint8_t *cmd, int n) { // EOF ToSend[++ToSendMax] = 0x20; //0010 + 0000 padding - ToSendMax++; } @@ -189,11 +182,15 @@ static void CodeIso15693AsReader256(uint8_t *cmd, int n) { // EOF ToSend[++ToSendMax] = 0x20; //0010 + 0000 padding - ToSendMax++; } -static const uint8_t encode_4bits[16] = { 0xaa, 0x6a, 0x9a, 0x5a, 0xa6, 0x66, 0x96, 0x56, 0xa9, 0x69, 0x99, 0x59, 0xa5, 0x65, 0x95, 0x55 }; +static const uint8_t encode_4bits[16] = { + 0xaa, 0x6a, 0x9a, 0x5a, + 0xa6, 0x66, 0x96, 0x56, + 0xa9, 0x69, 0x99, 0x59, + 0xa5, 0x65, 0x95, 0x55 +}; void CodeIso15693AsTag(uint8_t *cmd, size_t len) { /* @@ -260,8 +257,8 @@ void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time) { LED_B_ON(); for (int c = 0; c < len; c++) { - uint8_t data = cmd[c]; - for (int i = 0; i < 8; i++) { + volatile uint8_t data = cmd[c]; + for (uint8_t i = 0; i < 8; i++) { uint16_t send_word = (data & 0x80) ? 0xffff : 0x0000; while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; AT91C_BASE_SSC->SSC_THR = send_word; @@ -307,7 +304,7 @@ void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, for (size_t c = 0; c < len; c++) { for (int i = (c==0?4:7); i >= 0; i--) { uint8_t cmd_bits = ((cmd[c] >> i) & 0x01) ? 0xff : 0x00; - for (int j = 0; j < (slow?4:1); ) { + for (int j = 0; j < (slow ? 4 : 1); ) { if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { bits_to_send = bits_to_shift << (8 - shift_delay) | cmd_bits >> shift_delay; AT91C_BASE_SSC->SSC_THR = bits_to_send; @@ -652,9 +649,9 @@ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeo dma_start_time = GetCountSspClk() & 0xfffffff0; } - uint16_t tagdata = *upTo++; + volatile uint16_t tagdata = *upTo++; - if(upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + if (upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. upTo = dmaBuf; // start reading the circular buffer from the beginning if (behindBy > (9 * ISO15693_DMA_BUFFER_SIZE / 10)) { Dbprintf("About to blow circular buffer - aborted! behindBy=%d", behindBy); @@ -705,12 +702,11 @@ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeo Dbprintf("timing: sof_time = %d, eof_time = %d", (sof_time * 4), (*eof_time * 4)); } - if (LogTrace(DecodeTag.output, DecodeTag.len, (sof_time * 4), (*eof_time * 4), NULL, false) == false) - DbpString("GetIso15693AnswerFromTag: failed logtrace"); - if (ret < 0) { return ret; } + + LogTrace(DecodeTag.output, DecodeTag.len, (sof_time * 4), (*eof_time * 4), NULL, false); return DecodeTag.len; } @@ -1017,7 +1013,6 @@ static RAMFUNC int Handle15693SampleFromReader(bool bit, DecodeReader_t *DecodeR int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time) { int samples = 0; bool gotFrame = false; - uint8_t b; uint8_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; @@ -1043,15 +1038,15 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo uint8_t *upTo = dmaBuf; for (;;) { - uint16_t behindBy = ((uint8_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (ISO15693_DMA_BUFFER_SIZE-1); + uint16_t behindBy = ((uint8_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (ISO15693_DMA_BUFFER_SIZE - 1); if (behindBy == 0) continue; - b = *upTo++; + volatile uint8_t b = *upTo++; if (upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. upTo = dmaBuf; // start reading the circular buffer from the beginning if (behindBy > (9*ISO15693_DMA_BUFFER_SIZE/10)) { - Dbprintf("About to blow circular buffer - aborted! behindBy=%d", behindBy); + Dbprintf("About to blow circular buffer - aborted! behindBy %d", behindBy); break; } } @@ -1088,11 +1083,10 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo if (DecodeReader.byteCount > 0) { uint32_t sof_time = *eof_time - - DecodeReader.byteCount * (DecodeReader.Coding==CODING_1_OUT_OF_4?128:2048) // time for byte transfers + - DecodeReader.byteCount * (DecodeReader.Coding == CODING_1_OUT_OF_4 ? 128 : 2048) // time for byte transfers - 32 // time for SOF transfer - 16; // time for EOF transfer - if (LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time*32, *eof_time*32, NULL, true) == false) - DbpString("GetIso15693CommandFromReader: failed logtrace"); + LogTrace(DecodeReader.output, DecodeReader.byteCount, (sof_time * 32), (*eof_time * 32), NULL, true); } return DecodeReader.byteCount; @@ -1317,7 +1311,7 @@ void Iso15693InitReader(void) { // Start from off (no field generated) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); - SpinDelay(10); + SpinDelay(50); // switch field on FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); @@ -1328,7 +1322,7 @@ void Iso15693InitReader(void) { SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // give tags some time to energize - SpinDelay(250); + SpinDelay(200); set_tracing(true); @@ -1572,6 +1566,8 @@ void Iso15693InitTag(void) { FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + set_tracing(true); + // turn on clock StartCountSspClk(); } From 7aedd9c7fe120b60442e5b7d447f3488b3156d37 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 12:28:30 +0200 Subject: [PATCH 051/139] textual --- include/pm3_cmd.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index ac5d7ce00..b85d996d6 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -122,6 +122,7 @@ typedef struct { bool verbose; } PACKED sample_config; +// Tracelog Header struct typedef struct { uint32_t timestamp; uint16_t duration; From 52e9d7a75048adad307acb5137f41f6b713c58f6 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 12:29:31 +0200 Subject: [PATCH 052/139] rework iclass dump --- client/src/cmdhficlass.c | 174 ++++++++++++++++++++------------------- client/src/cmdhficlass.h | 13 +++ 2 files changed, 102 insertions(+), 85 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 62ba0f70a..5b0b11492 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -1112,7 +1112,7 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) { return PM3_SUCCESS; } -static void Calc_wb_mac(uint8_t blockno, uint8_t *data, uint8_t *div_key, uint8_t MAC[4]) { +static void calc_wb_mac(uint8_t blockno, uint8_t *data, uint8_t *div_key, uint8_t *MAC) { uint8_t wb[9]; wb[0] = blockno; memcpy(wb + 1, data, 8); @@ -1120,24 +1120,25 @@ static void Calc_wb_mac(uint8_t blockno, uint8_t *data, uint8_t *div_key, uint8_ } static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool use_credit_key, bool verbose) { - PacketResponseNG resp; - uint8_t flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE); + uint8_t flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE); if (use_credit_key) flags |= FLAG_ICLASS_READER_CEDITKEY; clearCommandBuffer(); + PacketResponseNG resp; SendCommandMIX(CMD_HF_ICLASS_READER, flags, 0, 0, NULL, 0); if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { PrintAndLogEx(WARNING, "command execute timeout"); return false; } - uint8_t isOK = resp.oldarg[0] & 0xff; + uint8_t isok = resp.oldarg[0] & 0xff; - if (isOK == 0xFF) { + // no tag found or button pressed + if ((isok == 0) || isok == 0xFF) { if (verbose) { - PrintAndLogEx(FAILED, "failed tag-select, aborting... (%d)", isOK); + PrintAndLogEx(FAILED, "failed tag-select, aborting... (%d)", isok); } return false; } @@ -1159,17 +1160,12 @@ static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool use_credit_key, bool v static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool use_credit_key, bool elite, bool rawkey, bool verbose) { - struct { - uint8_t key[8]; - bool use_raw; - bool use_elite; - bool use_credit_key; - } PACKED payload; - + iclass_auth_req_t payload = { + .use_raw = rawkey, + .use_elite = elite, + .use_credit_key = use_credit_key + }; memcpy(payload.key, KEY, 8); - payload.use_raw = rawkey; - payload.use_elite = elite; - payload.use_credit_key = use_credit_key; SendCommandNG(CMD_HF_ICLASS_AUTH, (uint8_t*)&payload, sizeof(payload)); PacketResponseNG resp; @@ -1184,13 +1180,8 @@ static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool u return false; } - struct p { - bool isOK; - uint8_t div_key[8]; - uint8_t mac[4]; - } PACKED; - struct p *packet = (struct p *)resp.data.asBytes; - + iclass_auth_resp_t *packet = (iclass_auth_resp_t *)resp.data.asBytes; + if (packet->isOK == 0) { if (verbose) PrintAndLogEx(FAILED, "authentication error"); return false; @@ -1206,7 +1197,7 @@ static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool u return true; } -static int CmdHFiClassReader_Dump(const char *Cmd) { +static int CmdHFiClassDump(const char *Cmd) { uint8_t MAC[4] = {0x00, 0x00, 0x00, 0x00}; uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -1245,6 +1236,7 @@ static int CmdHFiClassReader_Dump(const char *Cmd) { keyNbr = param_get8(Cmd, cmdp + 1); if (keyNbr < ICLASS_KEYS_MAX) { memcpy(CreditKEY, iClass_Key_Table[keyNbr], 8); + PrintAndLogEx(INFO, "AA2 (credit) index %u", keyNbr); } else { PrintAndLogEx(WARNING, "\nERROR: Credit KeyNbr is invalid\n"); errors = true; @@ -1277,6 +1269,7 @@ static int CmdHFiClassReader_Dump(const char *Cmd) { keyNbr = param_get8(Cmd, cmdp + 1); if (keyNbr < ICLASS_KEYS_MAX) { memcpy(KEY, iClass_Key_Table[keyNbr], 8); + PrintAndLogEx(INFO, "AA1 (debit) index %u", keyNbr); } else { PrintAndLogEx(WARNING, "\nERROR: Credit KeyNbr is invalid\n"); errors = true; @@ -1307,12 +1300,7 @@ static int CmdHFiClassReader_Dump(const char *Cmd) { // if no debit key given try credit key on AA1 (not for iclass but for some picopass this will work) if (!have_debit_key && have_credit_key) use_credit_key = true; - uint32_t flags = ( - FLAG_ICLASS_READER_INIT | - FLAG_ICLASS_READER_CLEARTRACE | - FLAG_ICLASS_READER_ONLY_ONCE - ); - + uint32_t flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE); //get config and first 3 blocks PacketResponseNG resp; @@ -1320,7 +1308,7 @@ static int CmdHFiClassReader_Dump(const char *Cmd) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ICLASS_READER, flags, 0, 0, NULL, 0); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) { + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { PrintAndLogEx(WARNING, "command execute timeout"); DropField(); return PM3_ESOFT; @@ -1346,21 +1334,22 @@ static int CmdHFiClassReader_Dump(const char *Cmd) { } // authenticate debit key and get div_key - later store in dump block 3 - int numberAuthRetries = ICLASS_AUTH_RETRY; - do { - if (select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, verbose)) - break; - } while (numberAuthRetries--); - - if (numberAuthRetries <= 0) { + if (select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, verbose) == false) { PrintAndLogEx(WARNING, "failed authenticating with debit key"); DropField(); return PM3_ESOFT; } - // begin dump + struct { + uint8_t start_blockno; + uint8_t numblks; + } PACKED payload; + payload.start_blockno = blockno; + payload.numblks = numblks - blockno + 1; + clearCommandBuffer(); - SendCommandMIX(CMD_HF_ICLASS_DUMP, blockno, numblks - blockno + 1, 0, NULL, 0); + SendCommandNG(CMD_HF_ICLASS_DUMP, (uint8_t*)&payload, sizeof(payload)); + while (true) { printf("."); fflush(stdout); @@ -1371,47 +1360,53 @@ static int CmdHFiClassReader_Dump(const char *Cmd) { return PM3_EOPABORTED; } - if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) + if (WaitForResponseTimeout(CMD_HF_ICLASS_DUMP, &resp, 2000)) break; } + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(ERR, "failed to communicate with card"); + return resp.status; + } + // dump cmd switch off at device when finised. - uint32_t blocksRead = resp.oldarg[1]; - uint8_t isOK = resp.oldarg[0] & 0xff; - if (!isOK && !blocksRead) { + struct p_resp { + bool isOK; + uint8_t block_cnt; + uint32_t bb_offset; + } PACKED; + struct p_resp *packet = (struct p_resp *)resp.data.asBytes; + + uint32_t blocks_read = packet->block_cnt; + + if (packet->isOK == false) { PrintAndLogEx(WARNING, "read block failed"); return PM3_ESOFT; } - uint32_t startindex = resp.oldarg[2]; - if (blocksRead * 8 > sizeof(tag_data) - (blockno * 8)) { + uint32_t startindex = packet->bb_offset; + + if (blocks_read * 8 > sizeof(tag_data) - (blockno * 8)) { PrintAndLogEx(FAILED, "data exceeded buffer size!"); - blocksRead = (sizeof(tag_data) / 8) - blockno; + blocks_read = (sizeof(tag_data) / 8) - blockno; } // response ok - now get bigbuf content of the dump - if (!GetFromDevice(BIG_BUF, tag_data + (blockno * 8), blocksRead * 8, startindex, NULL, 0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, tag_data + (blockno * 8), blocks_read * 8, startindex, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } - size_t gotBytes = blocksRead * 8 + blockno * 8; + size_t gotBytes = blocks_read * 8 + blockno * 8; - // try AA2 + // try AA2 Kc, Credit if (have_credit_key) { - //turn off hf field before authenticating with different key - DropField(); memset(MAC, 0, 4); // AA2 authenticate credit key and git c_div_key - later store in dump block 4 - numberAuthRetries = ICLASS_AUTH_RETRY; - do { - if (select_and_auth(CreditKEY, MAC, c_div_key, true, elite, rawkey, verbose)) - break; - } while (numberAuthRetries--); - - if (numberAuthRetries <= 0) { + if (select_and_auth(CreditKEY, MAC, c_div_key, true, elite, rawkey, verbose) == false) { PrintAndLogEx(WARNING, "failed authenticating with credit key"); DropField(); return PM3_ESOFT; @@ -1419,32 +1414,43 @@ static int CmdHFiClassReader_Dump(const char *Cmd) { // do we still need to read more block? (aa2 enabled?) if (maxBlk > blockno + numblks + 1) { - // setup dump and start + + payload.start_blockno = blockno + blocks_read; + payload.numblks = maxBlk - (blockno + blocks_read); + clearCommandBuffer(); - SendCommandMIX(CMD_HF_ICLASS_DUMP, blockno + blocksRead, maxBlk - (blockno + blocksRead), 0, NULL, 0); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) { + SendCommandNG(CMD_HF_ICLASS_DUMP, (uint8_t*)&payload, sizeof(payload)); + + if (!WaitForResponseTimeout(CMD_HF_ICLASS_DUMP, &resp, 2000)) { PrintAndLogEx(WARNING, "command execute timeout 2"); return PM3_ETIMEOUT; } - isOK = resp.oldarg[0] & 0xff; - blocksRead = resp.oldarg[1]; - if (!isOK && !blocksRead) { - PrintAndLogEx(WARNING, "read block failed 2"); + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(ERR, "failed to communicate with card"); + return resp.status; + } + + packet = (struct p_resp *)resp.data.asBytes; + if (packet->isOK == false) { + PrintAndLogEx(WARNING, "read block failed using credit key"); return PM3_ESOFT; } - startindex = resp.oldarg[2]; - if (blocksRead * 8 > sizeof(tag_data) - gotBytes) { + blocks_read = packet->block_cnt; + startindex = packet->bb_offset; + + if (blocks_read * 8 > sizeof(tag_data) - gotBytes) { PrintAndLogEx(FAILED, "data exceeded buffer size!"); - blocksRead = (sizeof(tag_data) - gotBytes) / 8; + blocks_read = (sizeof(tag_data) - gotBytes) / 8; } // get dumped data from bigbuf - if (!GetFromDevice(BIG_BUF, tag_data + gotBytes, blocksRead * 8, startindex, NULL, 0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, tag_data + gotBytes, blocks_read * 8, startindex, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } - gotBytes += blocksRead * 8; + gotBytes += blocks_read * 8; } } @@ -1485,13 +1491,13 @@ static int WriteBlock(uint8_t blockno, uint8_t *bldata, uint8_t *KEY, bool use_c uint8_t MAC[4] = {0x00, 0x00, 0x00, 0x00}; uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - if (!select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, verbose)) { + if (select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, verbose) == false) { numberAuthRetries--; DropField(); continue; } - Calc_wb_mac(blockno, bldata, div_key, MAC); + calc_wb_mac(blockno, bldata, div_key, MAC); struct p { uint8_t blockno; @@ -1506,7 +1512,7 @@ static int WriteBlock(uint8_t blockno, uint8_t *bldata, uint8_t *KEY, bool use_c SendCommandNG(CMD_HF_ICLASS_WRITEBL, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_ICLASS_WRITEBL, &resp, 4000) == 0) { + if (WaitForResponseTimeout(CMD_HF_ICLASS_WRITEBL, &resp, 2000) == 0) { if (verbose) PrintAndLogEx(WARNING, "Command execute timeout"); DropField(); return PM3_ETIMEOUT; @@ -1767,7 +1773,7 @@ static int CmdHFiClassCloneTag(const char *Cmd) { // calculate all mac for every the block we will write for (i = startblock; i <= endblock; i++) { - Calc_wb_mac(i, tag_data[i - startblock].d, div_key, MAC); + calc_wb_mac(i, tag_data[i - startblock].d, div_key, MAC); // usb command d start pointer = d + (i - 6) * 12 // memcpy(pointer,tag_data[i - 6],8) 8 bytes // memcpy(pointer + 8,mac,sizoof(mac) 4 bytes; @@ -1806,7 +1812,7 @@ static int CmdHFiClassCloneTag(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_HF_ICLASS_CLONE, (uint8_t *)&payload, total_bytes + 2); - if (WaitForResponseTimeout(CMD_HF_ICLASS_CLONE, &resp, 4500) == 0) { + if (WaitForResponseTimeout(CMD_HF_ICLASS_CLONE, &resp, 2000) == 0) { PrintAndLogEx(WARNING, "command execute timeout"); DropField(); return PM3_ETIMEOUT; @@ -1829,7 +1835,6 @@ static int ReadBlock(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool isOK; uint8_t blockdata[8]; } PACKED; - struct p *result = NULL; do { @@ -1837,7 +1842,7 @@ static int ReadBlock(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, if (auth || blockno >= 2) { uint8_t MAC[4] = {0x00, 0x00, 0x00, 0x00}; uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - if (!select_and_auth(KEY, MAC, div_key, (keyType == 0x18), elite, rawkey, verbose)) { + if (select_and_auth(KEY, MAC, div_key, (keyType == 0x18), elite, rawkey, verbose) == false) { numberAuthRetries--; DropField(); continue; @@ -1845,7 +1850,7 @@ static int ReadBlock(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, } else { uint8_t CSN[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - if (!select_only(CSN, CCNR, (keyType == 0x18), verbose)) { + if (select_only(CSN, CCNR, (keyType == 0x18), verbose) == false) { numberAuthRetries--; DropField(); continue; @@ -2156,6 +2161,7 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) { bool elite = false; bool errors = false; uint8_t cmdp = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { case 'h': @@ -2217,9 +2223,9 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) { } if (errors || cmdp < 4) return usage_hf_iclass_calc_newkey(); - if (!givenCSN) { + if (givenCSN == false) { uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - if (!select_only(CSN, CCNR, false, true)) { + if (select_only(CSN, CCNR, false, true) == false) { DropField(); return PM3_ESOFT; } @@ -2512,7 +2518,7 @@ static int CmdHFiClassCheckKeys(const char *Cmd) { SendCommandOLD(CMD_HF_ICLASS_CHKKEYS, flags, keys, 0, pre + key_offset, 4 * keys); PacketResponseNG resp; - while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + while (!WaitForResponseTimeout(CMD_HF_ICLASS_CHKKEYS, &resp, 2000)) { timeout++; printf("."); fflush(stdout); @@ -2927,7 +2933,7 @@ static command_t CommandTable[] = { {"chk", CmdHFiClassCheckKeys, AlwaysAvailable, "[options..] Check keys"}, {"clone", CmdHFiClassCloneTag, IfPm3Iclass, "[options..] Restore a dump file onto a iClass tag"}, {"decrypt", CmdHFiClassDecrypt, AlwaysAvailable, "[options..] Decrypt given block data or tag dump file" }, - {"dump", CmdHFiClassReader_Dump, IfPm3Iclass, "[options..] Dump iClass tag to file"}, + {"dump", CmdHFiClassDump, IfPm3Iclass, "[options..] Dump iClass tag to file"}, {"eload", CmdHFiClassELoad, IfPm3Iclass, "[f ] Load iClass dump file into emulator memory"}, {"encrypt", CmdHFiClassEncryptBlk, AlwaysAvailable, "[options..] Encrypt given block data"}, {"info", CmdHFiClassInfo, AlwaysAvailable, " Tag information"}, @@ -2979,8 +2985,6 @@ int readIclass(bool loop, bool verbose) { uint8_t readStatus = resp.oldarg[0] & 0xff; -// PrintAndLogEx(NORMAL, "ICE: %x", readStatus); - // no tag found or button pressed if ((readStatus == 0 && !loop) || readStatus == 0xFF) { DropField(); diff --git a/client/src/cmdhficlass.h b/client/src/cmdhficlass.h index 53880d647..19b994d0c 100644 --- a/client/src/cmdhficlass.h +++ b/client/src/cmdhficlass.h @@ -27,6 +27,19 @@ typedef struct iclass_prekey { uint8_t key[8]; } iclass_prekey_t; +typedef struct { + uint8_t key[8]; + bool use_raw; + bool use_elite; + bool use_credit_key; +} PACKED iclass_auth_req_t; + +typedef struct { + bool isOK; + uint8_t div_key[8]; + uint8_t mac[4]; +} PACKED iclass_auth_resp_t; + int CmdHFiClass(const char *Cmd); int readIclass(bool loop, bool verbose); From aaa6f25ab6e012f4ebf9997fc0b7cbc6f9dc53ba Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 13:55:19 +0200 Subject: [PATCH 053/139] chg: 'trace list' - now support relative times, us/fc (@piwi) --- client/src/cmdtrace.c | 174 +++++++++++++++++++++++++++++++++--------- 1 file changed, 136 insertions(+), 38 deletions(-) diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index 7f854d303..f2b20ab68 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -31,6 +31,8 @@ static int usage_trace_list(void) { PrintAndLogEx(NORMAL, "Usage: trace list [f][c| <0|1>"); PrintAndLogEx(NORMAL, " f - show frame delay times as well"); PrintAndLogEx(NORMAL, " c - mark CRC bytes"); + PrintAndLogEx(NORMAL, " r - show relative times (gap and duration)"); + PrintAndLogEx(NORMAL, " u - display times in microseconds instead of clock cycles"); PrintAndLogEx(NORMAL, " x - show hexdump to convert to pcap(ng) or to import into Wireshark using encapsulation type \"ISO 14443\""); PrintAndLogEx(NORMAL, " syntax to use: `text2pcap -t \"%%S.\" -l 264 -n `"); PrintAndLogEx(NORMAL, " <0|1> - use data from Tracebuffer, if not set, try to collect a trace from Proxmark3 device."); @@ -179,16 +181,16 @@ static uint16_t printHexLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trac return ret; } -static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, uint8_t protocol, bool showWaitCycles, bool markCRCBytes) { +static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, uint8_t protocol, bool showWaitCycles, bool markCRCBytes, uint32_t *prev_eot, bool use_us) { // sanity check if (is_last_record(tracepos, traceLen)) { PrintAndLogEx(DEBUG, "last record triggered. t-pos: %u t-len %u", tracepos, traceLen); return traceLen; } + uint32_t end_of_transmission_timestamp = 0; uint32_t duration; uint16_t data_len; - uint32_t EndOfTransmissionTimestamp; uint8_t topaz_reader_command[9]; char explanation[40] = {0}; uint8_t mfData[32] = {0}; @@ -203,6 +205,12 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr PrintAndLogEx(DEBUG, "trace pos offset %d larger than reported tracelen %d", tracepos + TRACELOG_HDR_LEN + data_len + TRACELOG_PARITY_LEN(hdr), traceLen); return traceLen; } + + // adjust for different time scales + if (protocol == ICLASS || protocol == ISO_15693) { + duration *= 32; + } + uint8_t *frame = hdr->frame; uint8_t *parityBytes = hdr->frame + data_len; @@ -266,7 +274,13 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr char line[18][120] = {{0}}; if (data_len == 0) { - sprintf(line[0], ""); + if (protocol == ICLASS && duration == 2048) { + sprintf(line[0], " "); + } else if (protocol == ISO_15693 && duration == 512) { + sprintf(line[0], " "); + } else { + sprintf(line[0], ""); + } return tracepos; } @@ -318,7 +332,20 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr // Draw the CRC column const char *crc = (crcStatus == 0 ? "!crc" : (crcStatus == 1 ? " ok " : " ")); - EndOfTransmissionTimestamp = hdr->timestamp + duration; + + uint32_t previous_end_of_transmission_timestamp = 0; + if (prev_eot) { + if (*prev_eot) { + previous_end_of_transmission_timestamp = *prev_eot; + } else { + previous_end_of_transmission_timestamp = hdr->timestamp; + } + } + + end_of_transmission_timestamp = hdr->timestamp + duration; + + if (prev_eot) + *prev_eot = end_of_transmission_timestamp; // Always annotate LEGIC read/tag if (protocol == LEGIC) @@ -376,18 +403,40 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr int num_lines = MIN((data_len - 1) / 18 + 1, 18); for (int j = 0; j < num_lines ; j++) { if (j == 0) { - PrintAndLogEx(NORMAL, " %10u | %10u | %s |%-72s | %s| %s", - (hdr->timestamp - first_hdr->timestamp), - (EndOfTransmissionTimestamp - first_hdr->timestamp), - (hdr->isResponse ? "Tag" : "Rdr"), - line[j], - (j == num_lines - 1) ? crc : " ", - (j == num_lines - 1) ? explanation : ""); + + uint32_t time1 = hdr->timestamp - first_hdr->timestamp; + uint32_t time2 = end_of_transmission_timestamp - first_hdr->timestamp; + if (prev_eot) { + time1 = hdr->timestamp - previous_end_of_transmission_timestamp; + time2 = duration; + } + + if (use_us) { + PrintAndLogEx(NORMAL, " %10.1f | %10.1f | %s |%-72s | %s| %s", + (float)time1/13.56, + (float)time2/13.56, + (hdr->isResponse ? "Tag" : "Rdr"), + line[j], + (j == num_lines - 1) ? crc : " ", + (j == num_lines - 1) ? explanation : "" + ); + } else { + PrintAndLogEx(NORMAL, " %10u | %10u | %s |%-72s | %s| %s", + (hdr->timestamp - first_hdr->timestamp), + (end_of_transmission_timestamp - first_hdr->timestamp), + (hdr->isResponse ? "Tag" : "Rdr"), + line[j], + (j == num_lines - 1) ? crc : " ", + (j == num_lines - 1) ? explanation : "" + ); + } + } else { PrintAndLogEx(NORMAL, " | | |%-72s | %s| %s", - line[j], - (j == num_lines - 1) ? crc : " ", - (j == num_lines - 1) ? explanation : ""); + line[j], + (j == num_lines - 1) ? crc : " ", + (j == num_lines - 1) ? explanation : "" + ); } } @@ -414,10 +463,10 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr tracelog_hdr_t *next_hdr = (tracelog_hdr_t *)(trace + tracepos); PrintAndLogEx(NORMAL, " %10u | %10u | %s |fdt (Frame Delay Time): %d", - (EndOfTransmissionTimestamp - first_hdr->timestamp), + (end_of_transmission_timestamp - first_hdr->timestamp), (next_hdr->timestamp - first_hdr->timestamp), " ", - (next_hdr->timestamp - EndOfTransmissionTimestamp)); + (next_hdr->timestamp - end_of_transmission_timestamp)); } return tracepos; @@ -531,10 +580,9 @@ int CmdTraceList(const char *Cmd) { clearCommandBuffer(); - bool showWaitCycles = false; - bool markCRCBytes = false; - bool showHex = false; - bool isOnline = true; + bool showWaitCycles = false, markCRCBytes = false; + bool showHex = false, isOnline = true; + bool use_us = false, use_relative = false; bool errors = false; uint8_t protocol = 0; char type[10] = {0}; @@ -567,6 +615,14 @@ int CmdTraceList(const char *Cmd) { isOnline = false; cmdp++; break; + case 'r': + use_relative = true; + cmdp++; + break; + case 'u': + use_us = true; + cmdp++; + break; default: PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; @@ -626,37 +682,79 @@ int CmdTraceList(const char *Cmd) { tracepos = printHexLine(tracepos, g_traceLen, g_trace, protocol); } } else { - PrintAndLogEx(INFO, _YELLOW_("Start") " = Start of Start Bit, " _YELLOW_("End") " = End of last modulation. " _YELLOW_("Src") " = Source of Transfer"); - if (protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == MFDES || protocol == TOPAZ || protocol == LTO) - PrintAndLogEx(INFO, "ISO14443A - All times are in carrier periods (1/13.56MHz)"); - if (protocol == THINFILM) - PrintAndLogEx(INFO, "Thinfilm - All times are in carrier periods (1/13.56MHz)"); - if (protocol == ICLASS) - PrintAndLogEx(INFO, "iClass - Timings are not as accurate"); + + if (use_relative) { + PrintAndLogEx(INFO, _YELLOW_("gap") " = time between transfers. " _YELLOW_("duration") " = duration of data transfer. " _YELLOW_("src") " = source of transfer"); + } else { + PrintAndLogEx(INFO, _YELLOW_("start") " = start of start frame " _YELLOW_("end") " = end of frame. " _YELLOW_("src") " = source of transfer"); + } + + if (protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == MFDES || protocol == TOPAZ || protocol == LTO) { + if (use_us) + PrintAndLogEx(INFO, _YELLOW_("ISO14443A") " - all times are in microseconds"); + else + PrintAndLogEx(INFO, _YELLOW_("ISO14443A") " - all times are in carrier periods (1/13.56MHz)"); + } + + if (protocol == THINFILM) { + if (use_us) + PrintAndLogEx(INFO, _YELLOW_("Thinfilm") " - all times are in microseconds"); + else + PrintAndLogEx(INFO, _YELLOW_("Thinfilm") " - all times are in carrier periods (1/13.56MHz)"); + } + + if (protocol == ICLASS || protocol == ISO_15693) { + if (use_us) + PrintAndLogEx(INFO, _YELLOW_("ISO15693 / iCLASS") " - all times are in microseconds"); + else + PrintAndLogEx(INFO, _YELLOW_("ISO15693 / iCLASS") " - all times are in carrier periods (1/13.56MHz)"); + } + if (protocol == LEGIC) - PrintAndLogEx(INFO, "LEGIC - Reader Mode: Timings are in ticks (1us == 1.5ticks)\n" + PrintAndLogEx(INFO, _YELLOW_("LEGIC") " - Reader Mode: Timings are in ticks (1us == 1.5ticks)\n" " Tag Mode: Timings are in sub carrier periods (1/212 kHz == 4.7us)"); - if (protocol == ISO_14443B) - PrintAndLogEx(INFO, "ISO14443B"); // Timings ? - if (protocol == ISO_15693) - PrintAndLogEx(INFO, "ISO15693 - Timings are not as accurate"); + + if (protocol == ISO_14443B) { + if (use_us) + PrintAndLogEx(INFO, _YELLOW_("ISO14443B") " - all times are in microseconds"); + else + PrintAndLogEx(INFO, _YELLOW_("ISO14443B") " - all times are in carrier periods (1/13.56MHz)"); + } + if (protocol == ISO_7816_4) - PrintAndLogEx(INFO, "ISO7816-4 / Smartcard - Timings N/A yet"); + PrintAndLogEx(INFO, _YELLOW_("ISO7816-4 / Smartcard") " - Timings N/A"); + if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS) - PrintAndLogEx(INFO, "Hitag1 / Hitag2 / HitagS - Timings in ETU (8us)"); - if (protocol == FELICA) - PrintAndLogEx(INFO, "ISO18092 / FeliCa - Timings are not as accurate"); + PrintAndLogEx(INFO, _YELLOW_("Hitag1 / Hitag2 / HitagS") " - Timings in ETU (8us)"); + + if (protocol == FELICA) { + if (use_us) + PrintAndLogEx(INFO, _YELLOW_("ISO18092 / FeliCa") " - all times are in microseconds"); + else + PrintAndLogEx(INFO, _YELLOW_("ISO18092 / FeliCa") " - all times are in carrier periods (1/13.56MHz)"); + } + PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, " Start | End | Src | Data (! denotes parity error) | CRC | Annotation"); + if (use_relative) { + PrintAndLogEx(NORMAL, " Gap | Duration | Src | Data (! denotes parity error, ' denotes short bytes) | CRC | Annotation"); + } else { + PrintAndLogEx(NORMAL, " Start | End | Src | Data (! denotes parity error) | CRC | Annotation"); + } PrintAndLogEx(NORMAL, "------------+------------+-----+-------------------------------------------------------------------------+-----+--------------------"); // clean authentication data used with the mifare classic decrypt fct if (protocol == ISO_14443A || protocol == PROTO_MIFARE) ClearAuthData(); + + uint32_t previous_EOT = 0; + uint32_t *prev_EOT = NULL; + if (use_relative) { + prev_EOT = &previous_EOT; + } while (tracepos < g_traceLen) { - tracepos = printTraceLine(tracepos, g_traceLen, g_trace, protocol, showWaitCycles, markCRCBytes); + tracepos = printTraceLine(tracepos, g_traceLen, g_trace, protocol, showWaitCycles, markCRCBytes, prev_EOT, use_us); if (kbd_enter_pressed()) break; From a6c240fe3be87e4494683b6528e498263c60c23f Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 17:56:19 +0200 Subject: [PATCH 054/139] tosend into bigbuff --- armsrc/BigBuf.c | 44 +++++++++++++++++++++ armsrc/BigBuf.h | 14 +++++++ armsrc/appmain.c | 83 +++++++++++++-------------------------- armsrc/hfsnoop.c | 9 +++-- armsrc/iclass.c | 98 +++++++++++++++++++++++------------------------ armsrc/iso15693.c | 88 +++++++++++++++++++++++------------------- armsrc/thinfilm.c | 16 +++++--- 7 files changed, 195 insertions(+), 157 deletions(-) diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 8756e8171..9b3733d6f 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -227,3 +227,47 @@ uint8_t emlSet(uint8_t *data, uint32_t offset, uint32_t length) { Dbprintf("Error, trying to set memory outside of bounds! %d > %d", (offset + length), CARD_MEMORY_SIZE); return 1; } + + +//============================================================================= +// The ToSend buffer. +// A buffer where we can queue things up to be sent through the FPGA, for +// any purpose (fake tag, as reader, whatever). We go MSB first, since that +// is the order in which they go out on the wire. +//============================================================================= +static tosend_t toSend = { + .max = -1, + .bit = 8, + .buf = NULL +}; + +// get the address of the ToSend buffer. Allocate part of Bigbuf for it, if not yet done +tosend_t *get_tosend(void) { + + if (toSend.buf == NULL) + toSend.buf = BigBuf_malloc(TOSEND_BUFFER_SIZE); + + return &toSend; +} + +void tosend_reset(void) { + toSend.max = -1; + toSend.bit = 8; +} + +void tosend_stuffbit(int b) { + if (toSend.bit >= 8) { + toSend.max++; + toSend.buf[toSend.max] = 0; + toSend.bit = 0; + } + + if (b) + toSend.buf[ toSend.max] |= (1 << (7 - toSend.bit)); + + toSend.bit++; + + if (toSend.max >= TOSEND_BUFFER_SIZE) { + toSend.bit = 0; + } +} diff --git a/armsrc/BigBuf.h b/armsrc/BigBuf.h index b89b94242..33c454226 100644 --- a/armsrc/BigBuf.h +++ b/armsrc/BigBuf.h @@ -21,6 +21,9 @@ #define CARD_MEMORY_SIZE 4096 #define DMA_BUFFER_SIZE 128 +// 8 data bits and 1 parity bit per payload byte, 1 correction bit, 1 SOC bit, 2 EOC bits +#define TOSEND_BUFFER_SIZE (9 * MAX_FRAME_SIZE + 1 + 1 + 2) + uint8_t *BigBuf_get_addr(void); uint32_t BigBuf_get_size(void); uint8_t *BigBuf_get_EM_addr(void); @@ -42,4 +45,15 @@ bool get_tracing(void); bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag); uint8_t emlSet(uint8_t *data, uint32_t offset, uint32_t length); + +typedef struct { + int max; + int bit; + uint8_t *buf; +} tosend_t; + +tosend_t *get_tosend(void); +void tosend_reset(void); +void tosend_stuffbit(int b); + #endif /* __BIGBUF_H */ diff --git a/armsrc/appmain.c b/armsrc/appmain.c index e35dc8ae2..094982a3c 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -62,17 +62,6 @@ #include "spiffs.h" #endif -//============================================================================= -// A buffer where we can queue things up to be sent through the FPGA, for -// any purpose (fake tag, as reader, whatever). We go MSB first, since that -// is the order in which they go out on the wire. -//============================================================================= - -#define TOSEND_BUFFER_SIZE (9*MAX_FRAME_SIZE + 1 + 1 + 2) // 8 data bits and 1 parity bit per payload byte, 1 correction bit, 1 SOC bit, 2 EOC bits -uint8_t ToSend[TOSEND_BUFFER_SIZE] = {0}; -int ToSendMax = -1; -static int ToSendBit; - extern uint32_t _stack_start, _stack_end; struct common_area common_area __attribute__((section(".commonarea"))); static int button_status = BUTTON_NO_CLICK; @@ -84,29 +73,6 @@ inline void send_wtx(uint16_t wtx) { } } -void ToSendReset(void) { - ToSendMax = -1; - ToSendBit = 8; -} - -void ToSendStuffBit(int b) { - if (ToSendBit >= 8) { - ToSendMax++; - ToSend[ToSendMax] = 0; - ToSendBit = 0; - } - - if (b) - ToSend[ToSendMax] |= (1 << (7 - ToSendBit)); - - ToSendBit++; - - if (ToSendMax >= sizeof(ToSend)) { - ToSendBit = 0; - DbpString("ToSendStuffBit overflowed!"); - } -} - //----------------------------------------------------------------------------- // Read an ADC channel and block till it completes, then return the result // in ADC units (0 to 1023). Also a routine to sum up a number of samples and @@ -326,6 +292,28 @@ static void TimingIntervalAcquisition(void) { StartTickCount(); } +static void print_debug_level(void) { + char dbglvlstr[20] = {0}; + switch(DBGLEVEL) { + case DBG_NONE: + sprintf(dbglvlstr, "NONE"); + break; + case DBG_ERROR: + sprintf(dbglvlstr, "ERROR"); + break; + case DBG_INFO: + sprintf(dbglvlstr, "INFO"); + break; + case DBG_DEBUG: + sprintf(dbglvlstr, "DEBUG"); + break; + case DBG_EXTENDED: + sprintf(dbglvlstr, "EXTENDED"); + break; + } + Dbprintf(" DBGLEVEL................%d ( " _YELLOW_("%s")" )", DBGLEVEL, dbglvlstr); +} + // measure the Connection Speed by sending SpeedTestBufferSize bytes to client and measuring the elapsed time. // Note: this mimics GetFromBigbuf(), i.e. we have the overhead of the PacketCommandNG structure included. static void printConnSpeed(void) { @@ -372,29 +360,10 @@ static void SendStatus(void) { DbpString(_CYAN_("Various")); print_stack_usage(); - - char dbglvlstr[20] = {0}; - switch(DBGLEVEL) { - case DBG_NONE: - sprintf(dbglvlstr, "NONE"); - break; - case DBG_ERROR: - sprintf(dbglvlstr, "ERROR"); - break; - case DBG_INFO: - sprintf(dbglvlstr, "INFO"); - break; - case DBG_DEBUG: - sprintf(dbglvlstr, "DEBUG"); - break; - case DBG_EXTENDED: - sprintf(dbglvlstr, "EXTENDED"); - break; - } - Dbprintf(" DBGLEVEL................%d ( " _YELLOW_("%s")" )", DBGLEVEL, dbglvlstr); + print_debug_level(); - Dbprintf(" ToSendMax...............%d", ToSendMax); - Dbprintf(" ToSendBit...............%d", ToSendBit); + tosend_t *ts = get_tosend(); + Dbprintf(" ToSendMax...............%d", ts->max ); Dbprintf(" ToSend BUFFERSIZE.......%d", TOSEND_BUFFER_SIZE); while ((AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINRDY) == 0); // Wait for MAINF value to become available... uint16_t mainf = AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINF; // Get # main clocks within 16 slow clocks @@ -752,7 +721,7 @@ static void PacketReceived(PacketCommandNG *packet) { // emulator case CMD_SET_DBGMODE: { DBGLEVEL = packet->data.asBytes[0]; - Dbprintf("Debug level: %d", DBGLEVEL); + print_debug_level(); reply_ng(CMD_SET_DBGMODE, PM3_SUCCESS, NULL, 0); break; } diff --git a/armsrc/hfsnoop.c b/armsrc/hfsnoop.c index 1b87d857a..ab2517187 100644 --- a/armsrc/hfsnoop.c +++ b/armsrc/hfsnoop.c @@ -114,8 +114,9 @@ int HfSniff(uint32_t samplesToSkip, uint32_t triggersToSkip, uint16_t *len) { } void HfPlotDownload(void) { - uint8_t *buf = ToSend; - uint8_t *this_buf = buf; + + tosend_t *ts = get_tosend(); + uint8_t *this_buf = ts->buf; FpgaDownloadAndGo(FPGA_BITSTREAM_HF); @@ -124,7 +125,7 @@ void HfPlotDownload(void) { AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; // Disable DMA Transfer AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) this_buf; // start transfer to this memory address AT91C_BASE_PDC_SSC->PDC_RCR = PM3_CMD_DATA_SIZE; // transfer this many samples - buf[0] = (uint8_t)AT91C_BASE_SSC->SSC_RHR; // clear receive register + ts->buf[0] = (uint8_t)AT91C_BASE_SSC->SSC_RHR; // clear receive register AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; // Start DMA transfer FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_GET_TRACE); // let FPGA transfer its internal Block-RAM @@ -132,7 +133,7 @@ void HfPlotDownload(void) { LED_B_ON(); for (size_t i = 0; i < FPGA_TRACE_SIZE; i += PM3_CMD_DATA_SIZE) { // prepare next DMA transfer: - uint8_t *next_buf = buf + ((i + PM3_CMD_DATA_SIZE) % (2 * PM3_CMD_DATA_SIZE)); + uint8_t *next_buf = ts->buf + ((i + PM3_CMD_DATA_SIZE) % (2 * PM3_CMD_DATA_SIZE)); AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t)next_buf; AT91C_BASE_PDC_SSC->PDC_RNCR = PM3_CMD_DATA_SIZE; diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 58cf36a0d..d180aa6e0 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -90,9 +90,10 @@ static void rotateCSN(uint8_t *original_csn, uint8_t *rotated_csn) { // Encode SOF only static void CodeIClassTagSOF(void) { - ToSendReset(); - ToSend[++ToSendMax] = 0x1D; - ToSendMax++; + tosend_reset(); + tosend_t *ts = get_tosend(); + ts->buf[++ts->max] = 0x1D; + ts->max++; } @@ -393,42 +394,43 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); // Prepare card messages - ToSendMax = 0; + tosend_t *ts = get_tosend(); + ts->max = 0; // First card answer: SOF CodeIClassTagSOF(); - memcpy(resp_sof, ToSend, ToSendMax); - resp_sof_len = ToSendMax; + memcpy(resp_sof, ts->buf, ts->max); + resp_sof_len = ts->max; // Anticollision CSN CodeIso15693AsTag(anticoll_data, sizeof(anticoll_data)); - memcpy(resp_anticoll, ToSend, ToSendMax); - resp_anticoll_len = ToSendMax; + memcpy(resp_anticoll, ts->buf, ts->max); + resp_anticoll_len = ts->max; // CSN (block 0) CodeIso15693AsTag(csn_data, sizeof(csn_data)); - memcpy(resp_csn, ToSend, ToSendMax); - resp_csn_len = ToSendMax; + memcpy(resp_csn, ts->buf, ts->max); + resp_csn_len = ts->max; // Configuration (block 1) CodeIso15693AsTag(conf_block, sizeof(conf_block)); - memcpy(resp_conf, ToSend, ToSendMax); - resp_conf_len = ToSendMax; + memcpy(resp_conf, ts->buf, ts->max); + resp_conf_len = ts->max; // e-Purse (block 2) CodeIso15693AsTag(card_challenge_data, sizeof(card_challenge_data)); - memcpy(resp_cc, ToSend, ToSendMax); - resp_cc_len = ToSendMax; + memcpy(resp_cc, ts->buf, ts->max); + resp_cc_len = ts->max; // Kd, Kc (blocks 3 and 4) CodeIso15693AsTag(ff_data, sizeof(ff_data)); - memcpy(resp_ff, ToSend, ToSendMax); - resp_ff_len = ToSendMax; + memcpy(resp_ff, ts->buf, ts->max); + resp_ff_len = ts->max; // Application Issuer Area (block 5) CodeIso15693AsTag(aia_data, sizeof(aia_data)); - memcpy(resp_aia, ToSend, ToSendMax); - resp_aia_len = ToSendMax; + memcpy(resp_aia, ts->buf, ts->max); + resp_aia_len = ts->max; //This is used for responding to READ-block commands or other data which is dynamically generated //First the 'trace'-data, not encoded for FPGA @@ -542,8 +544,8 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { trace_data = data_generic_trace; trace_data_size = 10; CodeIso15693AsTag(trace_data, trace_data_size); - memcpy(modulated_response, ToSend, ToSendMax); - modulated_response_size = ToSendMax; + memcpy(modulated_response, ts->buf, ts->max); + modulated_response_size = ts->max; goto send; } break; @@ -586,9 +588,9 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { trace_data = data_generic_trace; trace_data_size = 4; CodeIso15693AsTag(trace_data, trace_data_size); - memcpy(data_response, ToSend, ToSendMax); + memcpy(data_response, ts->buf, ts->max); modulated_response = data_response; - modulated_response_size = ToSendMax; + modulated_response_size = ts->max; } else { // Not fullsim, we don't respond // We do not know what to answer, so lets keep quiet @@ -636,8 +638,8 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { trace_data = data_generic_trace; trace_data_size = 34; CodeIso15693AsTag(trace_data, trace_data_size); - memcpy(modulated_response, ToSend, ToSendMax); - modulated_response_size = ToSendMax; + memcpy(modulated_response, ts->buf, ts->max); + modulated_response_size = ts->max; goto send; } @@ -650,8 +652,8 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { if (block == 2) { // update e-purse memcpy(card_challenge_data, receivedCmd + 2, 8); CodeIso15693AsTag(card_challenge_data, sizeof(card_challenge_data)); - memcpy(resp_cc, ToSend, ToSendMax); - resp_cc_len = ToSendMax; + memcpy(resp_cc, ts->buf, ts->max); + resp_cc_len = ts->max; cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd); cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kc); @@ -684,9 +686,9 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { trace_data = data_generic_trace; trace_data_size = 10; CodeIso15693AsTag(trace_data, trace_data_size); - memcpy(data_response, ToSend, ToSendMax); + memcpy(data_response, ts->buf, ts->max); modulated_response = data_response; - modulated_response_size = ToSendMax; + modulated_response_size = ts->max; } goto send; @@ -714,9 +716,9 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { trace_data_size = 10; CodeIso15693AsTag(trace_data, trace_data_size); - memcpy(data_response, ToSend, ToSendMax); + memcpy(data_response, ts->buf, ts->max); modulated_response = data_response; - modulated_response_size = ToSendMax; + modulated_response_size = ts->max; } } @@ -752,8 +754,9 @@ send: // THE READER CODE static void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time) { CodeIso15693AsReader(frame, len); - TransmitTo15693Tag(ToSend, ToSendMax, start_time); - uint32_t end_time = *start_time + (32 * ((8 * ToSendMax) - 4)); // substract the 4 padding bits after EOF + tosend_t *ts = get_tosend(); + TransmitTo15693Tag(ts->buf, ts->max, start_time); + uint32_t end_time = *start_time + (32 * ((8 * ts->max) - 4)); // substract the 4 padding bits after EOF LogTrace(frame, len, (*start_time * 4), (end_time * 4), NULL, true); } @@ -770,7 +773,7 @@ static bool iclass_send_cmd_with_retries(uint8_t* cmd, size_t cmdsize, uint8_t* if (expected_size == GetIso15693AnswerFromTag(resp, max_resp_size, timeout, eof_time)) { return true; } - start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; +// start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; } return false; } @@ -796,24 +799,19 @@ static bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t if (use_credit_key) read_check_cc[0] = 0x10 | ICLASS_CMD_READCHECK; - - set_tracing(true); - int len; - uint32_t start_time; - uint8_t tries = 10; - do { - // wakeup - start_time = GetCountSspClk(); - iclass_send_as_reader(act_all, 1, &start_time); - len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_ACTALL, eof_time); - if (len >= 0) { - break; - } else if (len == -2) { - return false; - } - } while (tries-- > 0); + // wakeup + uint32_t start_time = GetCountSspClk(); + iclass_send_as_reader(act_all, 1, &start_time); + int len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_ACTALL, eof_time); + if (len < 0) + return false; +/* + bool ok = iclass_send_cmd_with_retries(act_all, 1, resp, sizeof(resp), 1, 5, start_time, ICLASS_READER_TIMEOUT_ACTALL, eof_time); + if (ok == false) + return false; +*/ // send Identify start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; iclass_send_as_reader(identify, 1, &start_time); @@ -881,8 +879,6 @@ void ReaderIClass(uint8_t flags) { Iso15693InitReader(); } - set_tracing(true); - if ((flags & FLAG_ICLASS_READER_CLEARTRACE) == FLAG_ICLASS_READER_CLEARTRACE) { clear_trace(); } diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 7c018554c..82640d0cb 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -118,10 +118,11 @@ static void BuildInventoryResponse(uint8_t *uid); // n ... length of data void CodeIso15693AsReader(uint8_t *cmd, int n) { - ToSendReset(); + tosend_reset(); + tosend_t *ts = get_tosend(); // SOF for 1of4 - ToSend[++ToSendMax] = 0x84; //10000100 + ts->buf[++ts->max] = 0x84; //10000100 // data for (int i = 0; i < n; i++) { @@ -129,31 +130,32 @@ void CodeIso15693AsReader(uint8_t *cmd, int n) { uint8_t these = (cmd[i] >> j) & 0x03; switch(these) { case 0: - ToSend[++ToSendMax] = 0x40; //01000000 + ts->buf[++ts->max] = 0x40; //01000000 break; case 1: - ToSend[++ToSendMax] = 0x10; //00010000 + ts->buf[++ts->max] = 0x10; //00010000 break; case 2: - ToSend[++ToSendMax] = 0x04; //00000100 + ts->buf[++ts->max] = 0x04; //00000100 break; case 3: - ToSend[++ToSendMax] = 0x01; //00000001 + ts->buf[++ts->max] = 0x01; //00000001 break; } } } // EOF - ToSend[++ToSendMax] = 0x20; //0010 + 0000 padding - ToSendMax++; + ts->buf[++ts->max] = 0x20; //0010 + 0000 padding + ts->max++; } // Encode EOF only static void CodeIso15693AsReaderEOF(void) { - ToSendReset(); - ToSend[++ToSendMax] = 0x20; - ToSendMax++; + tosend_reset(); + tosend_t *ts = get_tosend(); + ts->buf[++ts->max] = 0x20; + ts->max++; } @@ -162,27 +164,28 @@ static void CodeIso15693AsReaderEOF(void) { // is designed for more robust communication over longer distances static void CodeIso15693AsReader256(uint8_t *cmd, int n) { - ToSendReset(); - + tosend_reset(); + tosend_t *ts = get_tosend(); + // SOF for 1of256 - ToSend[++ToSendMax] = 0x81; //10000001 + ts->buf[++ts->max] = 0x81; //10000001 // data for(int i = 0; i < n; i++) { for (int j = 0; j <= 255; j++) { if (cmd[i] == j) { - ToSendStuffBit(0); - ToSendStuffBit(1); + tosend_stuffbit(0); + tosend_stuffbit(1); } else { - ToSendStuffBit(0); - ToSendStuffBit(0); + tosend_stuffbit(0); + tosend_stuffbit(0); } } } // EOF - ToSend[++ToSendMax] = 0x20; //0010 + 0000 padding - ToSendMax++; + ts->buf[++ts->max] = 0x20; //0010 + 0000 padding + ts->max++; } static const uint8_t encode_4bits[16] = { @@ -219,22 +222,22 @@ void CodeIso15693AsTag(uint8_t *cmd, size_t len) { * A logic 0 is 10 * * */ - - ToSendReset(); + tosend_reset(); + tosend_t *ts = get_tosend(); // SOF - ToSend[++ToSendMax] = 0x1D; // 00011101 + ts->buf[++ts->max] = 0x1D; // 00011101 // data for (int i = 0; i < len; i++) { - ToSend[++ToSendMax] = encode_4bits[cmd[i] & 0xF]; - ToSend[++ToSendMax] = encode_4bits[cmd[i] >> 4]; + ts->buf[++ts->max] = encode_4bits[cmd[i] & 0xF]; + ts->buf[++ts->max] = encode_4bits[cmd[i] >> 4]; } // EOF - ToSend[++ToSendMax] = 0xB8; // 10111000 + ts->buf[++ts->max] = 0xB8; // 10111000 - ToSendMax++; + ts->max++; } // Transmit the command (to the tag) that was placed in cmd[]. @@ -617,9 +620,7 @@ static void DecodeTagReset(DecodeTag_t *DecodeTag) { */ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeout, uint32_t *eof_time) { - int samples = 0; - int ret = 0; - + int samples = 0, ret = 0; uint16_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; // the Decoder data structure @@ -641,7 +642,8 @@ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeo for(;;) { uint16_t behindBy = ((uint16_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (ISO15693_DMA_BUFFER_SIZE-1); - if (behindBy == 0) continue; + if (behindBy == 0) + continue; samples++; if (samples == 1) { @@ -702,7 +704,7 @@ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeo Dbprintf("timing: sof_time = %d, eof_time = %d", (sof_time * 4), (*eof_time * 4)); } - if (ret < 0) { + if (ret == -1) { return ret; } @@ -1115,8 +1117,10 @@ void AcquireRawAdcSamplesIso15693(void) { SpinDelay(100); // Now send the command + tosend_t *ts = get_tosend(); + uint32_t start_time = 0; - TransmitTo15693Tag(ToSend, ToSendMax, &start_time); + TransmitTo15693Tag(ts->buf, ts->max, &start_time); // wait for last transfer to complete while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) ; @@ -1321,11 +1325,11 @@ void Iso15693InitReader(void) { FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + set_tracing(true); + // give tags some time to energize SpinDelay(200); - set_tracing(true); - StartCountSspClk(); } @@ -1396,8 +1400,9 @@ int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t CodeIso15693AsReader256(send, sendlen); } - TransmitTo15693Tag(ToSend, ToSendMax, &start_time); - uint32_t end_time = start_time + 32 * (8 * ToSendMax -4); // substract the 4 padding bits after EOF + tosend_t *ts = get_tosend(); + TransmitTo15693Tag(ts->buf, ts->max, &start_time); + uint32_t end_time = start_time + 32 * (8 * ts->max - 4); // substract the 4 padding bits after EOF LogTrace(send, sendlen, (start_time * 4), (end_time * 4), NULL, true); int res = 0; @@ -1410,8 +1415,9 @@ int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time) { CodeIso15693AsReaderEOF(); - TransmitTo15693Tag(ToSend, ToSendMax, &start_time); - uint32_t end_time = start_time + 32 * (8 * ToSendMax - 4); // substract the 4 padding bits after EOF + tosend_t *ts = get_tosend(); + TransmitTo15693Tag(ts->buf, ts->max, &start_time); + uint32_t end_time = start_time + 32 * (8 * ts->max - 4); // substract the 4 padding bits after EOF LogTrace(NULL, 0, (start_time * 4), (end_time * 4), NULL, true); int res = 0; @@ -1591,6 +1597,8 @@ void SimTagIso15693(uint8_t *uid) { // not so obvious, but in the call to BuildInventoryResponse, the command is copied to the global ToSend buffer used below. BuildInventoryResponse(uid); + tosend_t *ts = get_tosend(); + while (!BUTTON_PRESS()) { WDT_HIT(); @@ -1602,7 +1610,7 @@ void SimTagIso15693(uint8_t *uid) { if ((cmd_len >= 5) && (cmd[0] & ISO15_REQ_INVENTORY) && (cmd[1] == ISO15_CMD_INVENTORY)) { // TODO: check more flags bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH); start_time = eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM; - TransmitTo15693Reader(ToSend, ToSendMax, &start_time, 0, slow); + TransmitTo15693Reader(ts->buf, ts->max, &start_time, 0, slow); } if (DBGLEVEL >= DBG_EXTENDED) { diff --git a/armsrc/thinfilm.c b/armsrc/thinfilm.c index 16c91c8bf..dcc2ff36f 100644 --- a/armsrc/thinfilm.c +++ b/armsrc/thinfilm.c @@ -30,7 +30,6 @@ void ReadThinFilm(void) { clear_trace(); - set_tracing(true); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); @@ -60,15 +59,19 @@ static uint16_t ReadReaderField(void) { } static void CodeThinfilmAsTag(const uint8_t *cmd, uint16_t len) { - ToSendReset(); + + tosend_reset(); + + tosend_t *ts = get_tosend(); + for (uint16_t i = 0; i < len; i++) { uint8_t b = cmd[i]; for (uint8_t j = 0; j < 8; j++) { - ToSend[++ToSendMax] = (b & 0x80) ? SEC_D : SEC_E; + ts->buf[++ts->max] = (b & 0x80) ? SEC_D : SEC_E; b <<= 1; } } - ToSendMax++; + ts->max++; } static int EmSendCmdThinfilmRaw(uint8_t *resp, uint16_t respLen) { @@ -132,6 +135,8 @@ void SimulateThinFilm(uint8_t *data, size_t len) { uint16_t hf_baseline = ReadReaderField(); + tosend_t *ts = get_tosend(); + // Start the timer StartCountSspClk(); @@ -147,7 +152,8 @@ void SimulateThinFilm(uint8_t *data, size_t len) { if (hf_av < hf_baseline) hf_baseline = hf_av; if (hf_av > hf_baseline + 10) { - EmSendCmdThinfilmRaw(ToSend, ToSendMax); + + EmSendCmdThinfilmRaw(ts->buf, ts->max); if (!reader_detected) { LED_B_ON(); //Dbprintf("Reader detected, start beaming data"); From d6f65f8578c22717437ed3ec5039998d9d90cbee Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 18:14:34 +0200 Subject: [PATCH 055/139] tosend into bigbuff --- armsrc/iso14443a.c | 156 ++++++++++++++++++++++++--------------------- armsrc/iso14443b.c | 79 +++++++++++++---------- 2 files changed, 129 insertions(+), 106 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 0dae3c872..bac3008f9 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -729,62 +729,61 @@ void RAMFUNC SniffIso14443a(uint8_t param) { //----------------------------------------------------------------------------- static void CodeIso14443aAsTagPar(const uint8_t *cmd, uint16_t len, uint8_t *par, bool collision) { - //uint8_t localCol = 0; - ToSendReset(); + tosend_reset(); + + tosend_t *ts = get_tosend(); // Correction bit, might be removed when not needed - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(1); // <----- - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); + tosend_stuffbit(0); + tosend_stuffbit(0); + tosend_stuffbit(0); + tosend_stuffbit(0); + tosend_stuffbit(1); // <----- + tosend_stuffbit(0); + tosend_stuffbit(0); + tosend_stuffbit(0); // Send startbit - ToSend[++ToSendMax] = SEC_D; - LastProxToAirDuration = 8 * ToSendMax - 4; + ts->buf[++ts->max] = SEC_D; + LastProxToAirDuration = 8 * ts->max - 4; for (uint16_t i = 0; i < len; i++) { uint8_t b = cmd[i]; // Data bits for (uint16_t j = 0; j < 8; j++) { - //if (collision && (localCol >= colpos)){ if (collision) { - ToSend[++ToSendMax] = SEC_COLL; - //localCol++; + ts->buf[++ts->max] = SEC_COLL; } else { if (b & 1) { - ToSend[++ToSendMax] = SEC_D; + ts->buf[++ts->max] = SEC_D; } else { - ToSend[++ToSendMax] = SEC_E; + ts->buf[++ts->max] = SEC_E; } b >>= 1; } } if (collision) { - ToSend[++ToSendMax] = SEC_COLL; - LastProxToAirDuration = 8 * ToSendMax; + ts->buf[++ts->max] = SEC_COLL; + LastProxToAirDuration = 8 * ts->max; } else { // Get the parity bit if (par[i >> 3] & (0x80 >> (i & 0x0007))) { - ToSend[++ToSendMax] = SEC_D; - LastProxToAirDuration = 8 * ToSendMax - 4; + ts->buf[++ts->max] = SEC_D; + LastProxToAirDuration = 8 * ts->max - 4; } else { - ToSend[++ToSendMax] = SEC_E; - LastProxToAirDuration = 8 * ToSendMax; + ts->buf[++ts->max] = SEC_E; + LastProxToAirDuration = 8 * ts->max; } } } // Send stopbit - ToSend[++ToSendMax] = SEC_F; + ts->buf[++ts->max] = SEC_F; // Convert from last byte pos to length - ToSendMax++; + ts->max++; } static void CodeIso14443aAsTagEx(const uint8_t *cmd, uint16_t len, bool collision) { @@ -799,37 +798,39 @@ static void CodeIso14443aAsTag(const uint8_t *cmd, uint16_t len) { static void Code4bitAnswerAsTag(uint8_t cmd) { uint8_t b = cmd; - ToSendReset(); + tosend_reset(); + + tosend_t *ts = get_tosend(); // Correction bit, might be removed when not needed - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(1); // 1 - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); + tosend_stuffbit(0); + tosend_stuffbit(0); + tosend_stuffbit(0); + tosend_stuffbit(0); + tosend_stuffbit(1); // 1 + tosend_stuffbit(0); + tosend_stuffbit(0); + tosend_stuffbit(0); // Send startbit - ToSend[++ToSendMax] = SEC_D; + ts->buf[++ts->max] = SEC_D; for (uint8_t i = 0; i < 4; i++) { if (b & 1) { - ToSend[++ToSendMax] = SEC_D; - LastProxToAirDuration = 8 * ToSendMax - 4; + ts->buf[++ts->max] = SEC_D; + LastProxToAirDuration = 8 * ts->max - 4; } else { - ToSend[++ToSendMax] = SEC_E; - LastProxToAirDuration = 8 * ToSendMax; + ts->buf[++ts->max] = SEC_E; + LastProxToAirDuration = 8 * ts->max; } b >>= 1; } // Send stopbit - ToSend[++ToSendMax] = SEC_F; + ts->buf[++ts->max] = SEC_F; // Convert from last byte pos to length - ToSendMax++; + ts->max++; } //----------------------------------------------------------------------------- @@ -887,32 +888,36 @@ bool prepare_tag_modulation(tag_response_info_t *response_info, size_t max_buffe // Prepare the tag modulation bits from the message CodeIso14443aAsTag(response_info->response, response_info->response_n); + tosend_t *ts = get_tosend(); + // Make sure we do not exceed the free buffer space - if (ToSendMax > max_buffer_size) { + if (ts->max > max_buffer_size) { Dbprintf("ToSend buffer, Out-of-bound, when modulating bits for tag answer:"); Dbhexdump(response_info->response_n, response_info->response, false); return false; } // Copy the byte array, used for this modulation to the buffer position - memcpy(response_info->modulation, ToSend, ToSendMax); + memcpy(response_info->modulation, ts->buf, ts->max); // Store the number of bytes that were used for encoding/modulation and the time needed to transfer them - response_info->modulation_n = ToSendMax; + response_info->modulation_n = ts->max; response_info->ProxToAirDuration = LastProxToAirDuration; return true; } bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_t **buffer, size_t *max_buffer_size) { + tosend_t *ts = get_tosend(); + // Retrieve and store the current buffer index response_info->modulation = *buffer; // Forward the prepare tag modulation function to the inner function if (prepare_tag_modulation(response_info, *max_buffer_size)) { // Update the free buffer offset and the remaining buffer size - *buffer += ToSendMax; - *max_buffer_size -= ToSendMax; + *buffer += ts->max; + *max_buffer_size -= ts->max; return true; } else { return false; @@ -1638,12 +1643,14 @@ static void PrepareDelayedTransfer(uint16_t delay) { for (uint16_t i = 0; i < delay; i++) bitmask |= (0x01 << i); - ToSend[ToSendMax++] = 0x00; + tosend_t *ts = get_tosend(); - for (uint16_t i = 0; i < ToSendMax; i++) { - uint8_t bits_to_shift = ToSend[i] & bitmask; - ToSend[i] = ToSend[i] >> delay; - ToSend[i] = ToSend[i] | (bits_shifted << (8 - delay)); + ts->buf[ts->max++] = 0x00; + + for (uint16_t i = 0; i < ts->max; i++) { + uint8_t bits_to_shift = ts->buf[i] & bitmask; + ts->buf[i] = ts->buf[i] >> delay; + ts->buf[i] = ts->buf[i] | (bits_shifted << (8 - delay)); bits_shifted = bits_to_shift; } } @@ -1701,11 +1708,12 @@ static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing static void CodeIso14443aBitsAsReaderPar(const uint8_t *cmd, uint16_t bits, const uint8_t *par) { int last = 0; - ToSendReset(); + tosend_reset(); + tosend_t *ts = get_tosend(); // Start of Communication (Seq. Z) - ToSend[++ToSendMax] = SEC_Z; - LastProxToAirDuration = 8 * (ToSendMax + 1) - 6; + ts->buf[++ts->max] = SEC_Z; + LastProxToAirDuration = 8 * (ts->max + 1) - 6; size_t bytecount = nbytes(bits); // Generate send structure for the data bits @@ -1717,17 +1725,17 @@ static void CodeIso14443aBitsAsReaderPar(const uint8_t *cmd, uint16_t bits, cons for (j = 0; j < bitsleft; j++) { if (b & 1) { // Sequence X - ToSend[++ToSendMax] = SEC_X; - LastProxToAirDuration = 8 * (ToSendMax + 1) - 2; + ts->buf[++ts->max] = SEC_X; + LastProxToAirDuration = 8 * (ts->max + 1) - 2; last = 1; } else { if (last == 0) { // Sequence Z - ToSend[++ToSendMax] = SEC_Z; - LastProxToAirDuration = 8 * (ToSendMax + 1) - 6; + ts->buf[++ts->max] = SEC_Z; + LastProxToAirDuration = 8 * (ts->max + 1) - 6; } else { // Sequence Y - ToSend[++ToSendMax] = SEC_Y; + ts->buf[++ts->max] = SEC_Y; last = 0; } } @@ -1739,17 +1747,17 @@ static void CodeIso14443aBitsAsReaderPar(const uint8_t *cmd, uint16_t bits, cons // Get the parity bit if (par[i >> 3] & (0x80 >> (i & 0x0007))) { // Sequence X - ToSend[++ToSendMax] = SEC_X; - LastProxToAirDuration = 8 * (ToSendMax + 1) - 2; + ts->buf[++ts->max] = SEC_X; + LastProxToAirDuration = 8 * (ts->max + 1) - 2; last = 1; } else { if (last == 0) { // Sequence Z - ToSend[++ToSendMax] = SEC_Z; - LastProxToAirDuration = 8 * (ToSendMax + 1) - 6; + ts->buf[++ts->max] = SEC_Z; + LastProxToAirDuration = 8 * (ts->max + 1) - 6; } else { // Sequence Y - ToSend[++ToSendMax] = SEC_Y; + ts->buf[++ts->max] = SEC_Y; last = 0; } } @@ -1759,16 +1767,16 @@ static void CodeIso14443aBitsAsReaderPar(const uint8_t *cmd, uint16_t bits, cons // End of Communication: Logic 0 followed by Sequence Y if (last == 0) { // Sequence Z - ToSend[++ToSendMax] = SEC_Z; - LastProxToAirDuration = 8 * (ToSendMax + 1) - 6; + ts->buf[++ts->max] = SEC_Z; + LastProxToAirDuration = 8 * (ts->max + 1) - 6; } else { // Sequence Y - ToSend[++ToSendMax] = SEC_Y; + ts->buf[++ts->max] = SEC_Y; } - ToSend[++ToSendMax] = SEC_Y; + ts->buf[++ts->max] = SEC_Y; // Convert to length of command: - ToSendMax++; + ts->max++; } //----------------------------------------------------------------------------- @@ -1970,7 +1978,8 @@ int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen) { int EmSend4bit(uint8_t resp) { Code4bitAnswerAsTag(resp); - int res = EmSendCmd14443aRaw(ToSend, ToSendMax); + tosend_t *ts = get_tosend(); + int res = EmSendCmd14443aRaw(ts->buf, ts->max); // do the tracing for the previous reader request and this tag answer: uint8_t par[1] = {0x00}; GetParity(&resp, 1, par); @@ -1991,7 +2000,9 @@ int EmSendCmdPar(uint8_t *resp, uint16_t respLen, uint8_t *par) { } int EmSendCmdParEx(uint8_t *resp, uint16_t respLen, uint8_t *par, bool collision) { CodeIso14443aAsTagPar(resp, respLen, par, collision); - int res = EmSendCmd14443aRaw(ToSend, ToSendMax); + tosend_t *ts = get_tosend(); + int res = EmSendCmd14443aRaw(ts->buf, ts->max); + // do the tracing for the previous reader request and this tag answer: EmLogTrace(Uart.output, Uart.len, @@ -2152,7 +2163,8 @@ void ReaderTransmitBitsPar(uint8_t *frame, uint16_t bits, uint8_t *par, uint32_t CodeIso14443aBitsAsReaderPar(frame, bits, par); // Send command to tag - TransmitFor14443a(ToSend, ToSendMax, timing); + tosend_t *ts = get_tosend(); + TransmitFor14443a(ts->buf, ts->max, timing); if (g_trigger) LED_A_ON(); LogTrace(frame, nbytes(bits), (LastTimeProxToAirStart << 4) + DELAY_ARM2AIR_AS_READER, ((LastTimeProxToAirStart + LastProxToAirDuration) << 4) + DELAY_ARM2AIR_AS_READER, par, true); diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 4448d3570..b06dbcb44 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -50,7 +50,7 @@ #endif // 4sample -#define SEND4STUFFBIT(x) ToSendStuffBit(x);ToSendStuffBit(x);ToSendStuffBit(x);ToSendStuffBit(x); +#define SEND4STUFFBIT(x) tosend_stuffbit(x);tosend_stuffbit(x);tosend_stuffbit(x);tosend_stuffbit(x); static void iso14b_set_timeout(uint32_t timeout); static void iso14b_set_maxframesize(uint16_t size); @@ -140,7 +140,7 @@ static uint32_t iso14b_timeout = TR0; static void CodeIso14443bAsTag(const uint8_t *cmd, int len) { int i; - ToSendReset(); + tosend_reset(); // Transmit a burst of ones, as the initial thing that lets the // reader get phase sync. @@ -199,8 +199,9 @@ static void CodeIso14443bAsTag(const uint8_t *cmd, int len) { SEND4STUFFBIT(1); } + tosend_t *ts = get_tosend(); // Convert from last byte pos to length - ToSendMax++; + ts->max++; } //----------------------------------------------------------------------------- @@ -543,24 +544,32 @@ void SimulateIso14443bTag(uint32_t pupi) { uint16_t len, cmdsReceived = 0; int cardSTATE = SIM_NOFIELD; int vHf = 0; // in mV + + tosend_t *ts = get_tosend(); + uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); // prepare "ATQB" tag answer (encoded): CodeIso14443bAsTag(respATQB, sizeof(respATQB)); - uint8_t *encodedATQB = BigBuf_malloc(ToSendMax); - uint16_t encodedATQBLen = ToSendMax; - memcpy(encodedATQB, ToSend, ToSendMax); + uint8_t *encodedATQB = BigBuf_malloc(ts->max); + uint16_t encodedATQBLen = ts->max; + memcpy(encodedATQB, ts->buf, ts->max); // prepare "OK" tag answer (encoded): CodeIso14443bAsTag(respOK, sizeof(respOK)); - uint8_t *encodedOK = BigBuf_malloc(ToSendMax); - uint16_t encodedOKLen = ToSendMax; - memcpy(encodedOK, ToSend, ToSendMax); + uint8_t *encodedOK = BigBuf_malloc(ts->max); + uint16_t encodedOKLen = ts->max; + memcpy(encodedOK, ts->buf, ts->max); // Simulation loop - while (!BUTTON_PRESS() && !data_available()) { + while (BUTTON_PRESS() == false) { WDT_HIT(); + + //iceman: limit with 2000 times.. + if (data_available()) { + break; + } // find reader field if (cardSTATE == SIM_NOFIELD) { @@ -669,7 +678,7 @@ void SimulateIso14443bTag(uint32_t pupi) { if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Emulator stopped. Trace length: %d ", BigBuf_get_traceLen()); - switch_off(); //simulate + switch_off(); //simulate } //============================================================================= @@ -967,9 +976,10 @@ static void TransmitFor14443b_AsReader(void) { FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); LED_B_ON(); - - for (int c = 0; c < ToSendMax; c++) { - uint8_t data = ToSend[c]; + tosend_t *ts = get_tosend(); + + for (int c = 0; c < ts->max; c++) { + uint8_t data = ts->buf[c]; for (int i = 0; i < 8; i++) { uint16_t send_word = (data & 0x80) ? 0x0000 : 0xffff; @@ -1006,48 +1016,48 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) { * 1 "stuffbit" = 1ETU (9us) */ - ToSendReset(); + tosend_reset(); // Send SOF // 10-11 ETUs of ZERO for (int i = 0; i < 10; i++) - ToSendStuffBit(0); + tosend_stuffbit(0); // 2-3 ETUs of ONE - ToSendStuffBit(1); - ToSendStuffBit(1); + tosend_stuffbit(1); + tosend_stuffbit(1); // Sending cmd, LSB // from here we add BITS for (int i = 0; i < len; i++) { // Start bit - ToSendStuffBit(0); + tosend_stuffbit(0); // Data bits uint8_t b = cmd[i]; - ToSendStuffBit(b & 1); - ToSendStuffBit((b >> 1) & 1); - ToSendStuffBit((b >> 2) & 1); - ToSendStuffBit((b >> 3) & 1); - ToSendStuffBit((b >> 4) & 1); - ToSendStuffBit((b >> 5) & 1); - ToSendStuffBit((b >> 6) & 1); - ToSendStuffBit((b >> 7) & 1); + tosend_stuffbit(b & 1); + tosend_stuffbit((b >> 1) & 1); + tosend_stuffbit((b >> 2) & 1); + tosend_stuffbit((b >> 3) & 1); + tosend_stuffbit((b >> 4) & 1); + tosend_stuffbit((b >> 5) & 1); + tosend_stuffbit((b >> 6) & 1); + tosend_stuffbit((b >> 7) & 1); // Stop bit - ToSendStuffBit(1); + tosend_stuffbit(1); // EGT extra guard time // For PCD it ranges 0-57us (1etu = 9us) -// ToSendStuffBit(1); -// ToSendStuffBit(1); -// ToSendStuffBit(1); +// tosend_stuffbit(1); +// tosend_stuffbit(1); +// tosend_stuffbit(1); } // Send EOF // 10-11 ETUs of ZERO for (int i = 0; i < 10; i++) - ToSendStuffBit(0); + tosend_stuffbit(0); // Transition time. TR0 - guard time // 8ETUS minum? @@ -1056,11 +1066,12 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) { // ensure that last byte is filled up for (int i = 0; i < 8 ; ++i) - ToSendStuffBit(1); + tosend_stuffbit(1); // TR1 - Synchronization time // Convert from last character reference to length - ToSendMax++; + tosend_t *ts = get_tosend(); + ts->max++; } /* From 88c14b9c08e80e1ab3fc3ccac9a0d5ff9236acc5 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 18:20:43 +0200 Subject: [PATCH 056/139] iclass doesnt need 4500 timout anymore, can put earlier in hf search. --- client/src/cmdhf.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index 4fc138a98..e1cf99305 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -129,6 +129,15 @@ int CmdHFSearch(const char *Cmd) { } } + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, " Searching for iClass / PicoPass tag..."); + if (IfPm3Iclass()) { + if (readIclass(false, false) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("iClass tag / PicoPass tag") " found\n"); + res = PM3_SUCCESS; + } + } + PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, " Searching for LEGIC tag..."); if (IfPm3Legicrf()) { @@ -156,7 +165,6 @@ int CmdHFSearch(const char *Cmd) { } } /* - // 14b and iclass is the longest test (put last) PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, " Searching for CryptoRF tag..."); if (IfPm3Iso14443b()) { @@ -167,7 +175,7 @@ int CmdHFSearch(const char *Cmd) { } */ - // 14b and iclass is the longest test (put last) + // 14b is the longest test (put last) PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, " Searching for ISO14443-B tag..."); if (IfPm3Iso14443b()) { @@ -177,14 +185,6 @@ int CmdHFSearch(const char *Cmd) { } } - PROMPT_CLEARLINE; - PrintAndLogEx(INPLACE, " Searching for iClass / PicoPass tag..."); - if (IfPm3Iclass()) { - if (readIclass(false, false) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("iClass tag / PicoPass tag") " found\n"); - res = PM3_SUCCESS; - } - } PROMPT_CLEARLINE; if (res != PM3_SUCCESS) { From b62c060727dc292e3f673b4970d9b11f4b0fe502 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 20:13:25 +0200 Subject: [PATCH 057/139] chg: hf iclass rdbl, wrbl - got some love --- client/src/cmdhficlass.c | 146 +++++++++++++++++++++++---------------- 1 file changed, 88 insertions(+), 58 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 5b0b11492..8bdde6592 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -35,6 +35,7 @@ #define ICLASS_KEYS_MAX 8 #define ICLASS_AUTH_RETRY 10 #define ICLASS_DECRYPTION_BIN "iclass_decryptionkey.bin" +static uint8_t empty[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; static int CmdHelp(const char *Cmd); @@ -954,7 +955,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { uint8_t max_blk = 31; getMemConfig(mem, chip, &max_blk, &app_areas, &kb); - uint8_t empty[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + BLOCK79ENCRYPTION aa1_encryption = (decrypted[(6 * 8) + 7] & 0x03); @@ -1484,61 +1485,40 @@ static int CmdHFiClassDump(const char *Cmd) { return PM3_SUCCESS; } -static int WriteBlock(uint8_t blockno, uint8_t *bldata, uint8_t *KEY, bool use_credit_key, bool elite, bool rawkey, bool verbose) { +static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *KEY, bool use_credit_key, bool elite, bool rawkey, bool verbose) { - int numberAuthRetries = ICLASS_AUTH_RETRY; - do { - - uint8_t MAC[4] = {0x00, 0x00, 0x00, 0x00}; - uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - if (select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, verbose) == false) { - numberAuthRetries--; - DropField(); - continue; - } - - calc_wb_mac(blockno, bldata, div_key, MAC); - - struct p { - uint8_t blockno; - uint8_t data[12]; - } PACKED payload; - payload.blockno = blockno; - - memcpy(payload.data, bldata, 8); - memcpy(payload.data + 8, MAC, 4); - - clearCommandBuffer(); - SendCommandNG(CMD_HF_ICLASS_WRITEBL, (uint8_t *)&payload, sizeof(payload)); - PacketResponseNG resp; - - if (WaitForResponseTimeout(CMD_HF_ICLASS_WRITEBL, &resp, 2000) == 0) { - if (verbose) PrintAndLogEx(WARNING, "Command execute timeout"); - DropField(); - return PM3_ETIMEOUT; - } - - if (resp.status != PM3_SUCCESS) { - if (verbose) PrintAndLogEx(ERR, "failed to communicate with card"); - DropField(); - return PM3_EWRONGANSWER; - } - - if (resp.data.asBytes[0] == 1) - break; - - } while (numberAuthRetries); - - DropField(); - - if (numberAuthRetries > 0) { - PrintAndLogEx(SUCCESS, "Write block %02X successful\n", blockno); - } else { - PrintAndLogEx(ERR, "failed to authenticate and write block"); + uint8_t MAC[4] = {0x00, 0x00, 0x00, 0x00}; + uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + if (select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, verbose) == false) { return PM3_ESOFT; } - return PM3_SUCCESS; + calc_wb_mac(blockno, bldata, div_key, MAC); + + struct p { + uint8_t blockno; + uint8_t data[12]; + } PACKED payload; + payload.blockno = blockno; + + memcpy(payload.data, bldata, 8); + memcpy(payload.data + 8, MAC, 4); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_ICLASS_WRITEBL, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + + if (WaitForResponseTimeout(CMD_HF_ICLASS_WRITEBL, &resp, 2000) == 0) { + if (verbose) PrintAndLogEx(WARNING, "Command execute timeout"); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + if (verbose) PrintAndLogEx(ERR, "failed to communicate with card"); + return PM3_EWRONGANSWER; + } + + return (resp.data.asBytes[0] == 1) ? PM3_SUCCESS : PM3_ESOFT; } static int CmdHFiClass_WriteBlock(const char *Cmd) { @@ -1620,7 +1600,12 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { if (errors || cmdp < 6) return usage_hf_iclass_writeblock(); - return WriteBlock(blockno, bldata, KEY, use_credit_key, elite, rawkey, verbose); + int isok = iclass_write_block(blockno, bldata, KEY, use_credit_key, elite, rawkey, verbose); + if (isok == PM3_SUCCESS) + PrintAndLogEx(SUCCESS, "Wrote block %02X successful", blockno); + else + PrintAndLogEx(FAILED, "Writing failed"); + return isok; } static int CmdHFiClassCloneTag(const char *Cmd) { @@ -1827,7 +1812,7 @@ static int CmdHFiClassCloneTag(const char *Cmd) { return resp.status; } -static int ReadBlock(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool verbose, bool auth) { +static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool verbose, bool auth) { int numberAuthRetries = ICLASS_AUTH_RETRY; // return data. @@ -1886,12 +1871,57 @@ static int ReadBlock(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "block %02X: %s\n", blockno, sprint_hex(result->blockdata, sizeof(result->blockdata))); + PrintAndLogEx(SUCCESS, "block %02X: " _GREEN_("%s"), blockno, sprint_hex(result->blockdata, sizeof(result->blockdata))); - if (blockno == 6) { - if (IsCryptoHelperPresent()) { + if (memcmp(result->blockdata, empty, 8) == 0) + return PM3_SUCCESS; + + bool use_sc = IsCryptoHelperPresent(); + if (use_sc == false) + return PM3_SUCCESS; + + // crypto helper available. + switch (blockno) { + case 6: { DecodeBlock6(result->blockdata); + break; } + case 7: { +// case 8: +// case 9: { + PrintAndLogEx(INFO, "Trying to decrypt..."); + + uint8_t dec_data[8]; + Decrypt(result->blockdata, dec_data); + + if (memcmp(dec_data, empty, 8) != 0) { + + //todo: remove preamble/sentinal + + uint32_t top = 0, mid, bot; + mid = bytes_to_num(dec_data, 4); + bot = bytes_to_num(dec_data + 4, 4); + + PrintAndLogEx(INFO, "Block 7 binary"); + + char hexstr[8 + 1] = {0}; + hex_to_buffer((uint8_t *)hexstr, dec_data, 8, sizeof(hexstr) - 1, 0, 0, true); + + char binstr[8 * 8 + 1] = {0}; + hextobinstring(binstr, hexstr); + uint8_t i = 0; + while (i < strlen(binstr) && binstr[i++] == '0'); + + PrintAndLogEx(SUCCESS, "%s", binstr + i); + + PrintAndLogEx(INFO, "Wiegand decode"); + wiegand_message_t packed = initialize_message_object(top, mid, bot); + HIDTryUnpack(&packed, true); + PrintAndLogEx(INFO, "-----------------------------------------------------------------"); + } else { + PrintAndLogEx(INFO, "No credential found."); + } + } } return PM3_SUCCESS; @@ -1973,7 +2003,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { if (!auth) PrintAndLogEx(FAILED, "warning: no authentication used with read, only a few specific blocks can be read accurately without authentication."); - return ReadBlock(KEY, blockno, keyType, elite, rawkey, verbose, auth); + return iclass_read_block(KEY, blockno, keyType, elite, rawkey, verbose, auth); } static int CmdHFiClass_loclass(const char *Cmd) { From e476e7f504f1f73b83d34798c83b4b10c9977411 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 20:13:56 +0200 Subject: [PATCH 058/139] coding style --- armsrc/iclass.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index d180aa6e0..84f854c9c 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1307,7 +1307,7 @@ void iClass_Dump(uint8_t start_blockno, uint8_t numblks) { BigBuf_free(); } -static bool iClass_WriteBlock_ext(uint8_t blockno, uint8_t *data) { +static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data) { uint8_t write[16] = { 0x80 | ICLASS_CMD_UPDATE, blockno }; memcpy(write + 2, data, 12); // data + mac @@ -1344,7 +1344,7 @@ static bool iClass_WriteBlock_ext(uint8_t blockno, uint8_t *data) { // turn off afterwards void iClass_WriteBlock(uint8_t blockno, uint8_t *data) { LED_A_ON(); - uint8_t isOK = iClass_WriteBlock_ext(blockno, data); + uint8_t isOK = iclass_writeblock_ext(blockno, data); switch_off(); reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); } @@ -1356,7 +1356,7 @@ void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data) { uint16_t total_blocks = (endblock - startblock) + 1; for (uint8_t b = startblock; b < total_blocks; b++) { - if (iClass_WriteBlock_ext(b, data + ((b - startblock) * 12))) { + if (iclass_writeblock_ext(b, data + ((b - startblock) * 12))) { Dbprintf("Write block [%02x] successful", b); written++; } else { From 4493153dbb7a674c3ab504b8c9da47ab067a1948 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 20:58:24 +0200 Subject: [PATCH 059/139] verbose.. --- client/src/cmdhficlass.c | 85 +++++++++++++++++----------------------- common/cardhelper.c | 11 +++--- common/cardhelper.h | 2 +- 3 files changed, 42 insertions(+), 56 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 8bdde6592..b4fc7dcb2 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -918,7 +918,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { if (errors || cmdp < 1) return usage_hf_iclass_decrypt(); - bool use_sc = IsCryptoHelperPresent(); + bool use_sc = IsCryptoHelperPresent(true); if (have_key == false && use_sc == false) { int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen); @@ -1092,7 +1092,7 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) { if (errors || cmdp < 1) return usage_hf_iclass_encrypt(); - bool use_sc = IsCryptoHelperPresent(); + bool use_sc = IsCryptoHelperPresent(true); if (have_key == false && use_sc == false) { size_t keylen = 0; @@ -1814,7 +1814,6 @@ static int CmdHFiClassCloneTag(const char *Cmd) { static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool verbose, bool auth) { - int numberAuthRetries = ICLASS_AUTH_RETRY; // return data. struct p { bool isOK; @@ -1822,61 +1821,51 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo } PACKED; struct p *result = NULL; - do { - // block 0,1 should always be able to read, and block 5 on some cards. - if (auth || blockno >= 2) { - uint8_t MAC[4] = {0x00, 0x00, 0x00, 0x00}; - uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - if (select_and_auth(KEY, MAC, div_key, (keyType == 0x18), elite, rawkey, verbose) == false) { - numberAuthRetries--; - DropField(); - continue; - } - } else { - uint8_t CSN[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - if (select_only(CSN, CCNR, (keyType == 0x18), verbose) == false) { - numberAuthRetries--; - DropField(); - continue; - } + // block 0,1 should always be able to read, and block 5 on some cards. + if (auth || blockno >= 2) { + uint8_t MAC[4] = {0x00, 0x00, 0x00, 0x00}; + uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + if (select_and_auth(KEY, MAC, div_key, (keyType == 0x18), elite, rawkey, verbose) == false) { + return PM3_ESOFT; } - - PacketResponseNG resp; - clearCommandBuffer(); - SendCommandNG(CMD_HF_ICLASS_READBL, (uint8_t *)&blockno, sizeof(uint8_t)); - - if (WaitForResponseTimeout(CMD_HF_ICLASS_READBL, &resp, 2000) == 0) { - if (verbose) PrintAndLogEx(WARNING, "Command execute timeout"); - DropField(); - return PM3_ETIMEOUT; + } else { + uint8_t CSN[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + if (select_only(CSN, CCNR, (keyType == 0x18), verbose) == false) { + return PM3_ESOFT; } + } - if (resp.status != PM3_SUCCESS) { - if (verbose) PrintAndLogEx(ERR, "failed to communicate with card"); - DropField(); - return PM3_EWRONGANSWER; - } + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ICLASS_READBL, (uint8_t *)&blockno, sizeof(uint8_t)); - result = (struct p *)resp.data.asBytes; - if (result->isOK) - break; + if (WaitForResponseTimeout(CMD_HF_ICLASS_READBL, &resp, 2000) == 0) { + if (verbose) PrintAndLogEx(WARNING, "Command execute timeout"); + DropField(); + return PM3_ETIMEOUT; + } - } while (numberAuthRetries); + if (resp.status != PM3_SUCCESS) { + if (verbose) PrintAndLogEx(ERR, "failed to communicate with card"); + return PM3_EWRONGANSWER; + } + + result = (struct p *)resp.data.asBytes; + if (result->isOK == false) + return PM3_ESOFT; DropField(); - if (numberAuthRetries == 0) { - PrintAndLogEx(ERR, "failed to authenticate and read block"); - return PM3_ESOFT; - } - PrintAndLogEx(SUCCESS, "block %02X: " _GREEN_("%s"), blockno, sprint_hex(result->blockdata, sizeof(result->blockdata))); if (memcmp(result->blockdata, empty, 8) == 0) return PM3_SUCCESS; - bool use_sc = IsCryptoHelperPresent(); + if (blockno < 6 || blockno > 7) + return PM3_SUCCESS; + + bool use_sc = IsCryptoHelperPresent(verbose); if (use_sc == false) return PM3_SUCCESS; @@ -1887,8 +1876,6 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo break; } case 7: { -// case 8: -// case 9: { PrintAndLogEx(INFO, "Trying to decrypt..."); uint8_t dec_data[8]; @@ -1902,7 +1889,7 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo mid = bytes_to_num(dec_data, 4); bot = bytes_to_num(dec_data + 4, 4); - PrintAndLogEx(INFO, "Block 7 binary"); + PrintAndLogEx(INFO, "Binary"); char hexstr[8 + 1] = {0}; hex_to_buffer((uint8_t *)hexstr, dec_data, 8, sizeof(hexstr) - 1, 0, 0, true); @@ -1914,7 +1901,7 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo PrintAndLogEx(SUCCESS, "%s", binstr + i); - PrintAndLogEx(INFO, "Wiegand decode"); + PrintAndLogEx(INFO, "-----------------------------------------------------------------"); wiegand_message_t packed = initialize_message_object(top, mid, bot); HIDTryUnpack(&packed, true); PrintAndLogEx(INFO, "-----------------------------------------------------------------"); diff --git a/common/cardhelper.c b/common/cardhelper.c index 03af2d799..fbdaf2002 100644 --- a/common/cardhelper.c +++ b/common/cardhelper.c @@ -22,7 +22,7 @@ static uint8_t cmd[] = {0x96, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // look for CryptoHelper -bool IsCryptoHelperPresent(void) { +bool IsCryptoHelperPresent(bool verbose) { if (IfPm3Smartcard()) { int resp_len = 0; @@ -31,14 +31,13 @@ bool IsCryptoHelperPresent(void) { ExchangeAPDUSC(true, version, sizeof(version), true, true, resp, sizeof(resp), &resp_len); if (strstr("CryptoHelper", (char *)resp) == 0) { - PrintAndLogEx(INFO, "Found smart card helper"); + if (verbose) { + PrintAndLogEx(INFO, "Found smart card helper"); + } return true; - } else { - return false; } - } else { - return false; } + return false; } static bool executeCrypto(uint8_t ins, uint8_t *src, uint8_t *dest) { diff --git a/common/cardhelper.h b/common/cardhelper.h index fcdd73cfe..eaf7ff38f 100644 --- a/common/cardhelper.h +++ b/common/cardhelper.h @@ -14,7 +14,7 @@ #include #include "common.h" -bool IsCryptoHelperPresent(void); +bool IsCryptoHelperPresent(bool verbose); bool Encrypt(uint8_t *src, uint8_t *dest); bool Decrypt(uint8_t *src, uint8_t *dest); void DecodeBlock6(uint8_t *src); From 830ecd6e135d85df5f907a1cb4902e39cda05b43 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 22:05:11 +0200 Subject: [PATCH 060/139] debugs --- armsrc/BigBuf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 9b3733d6f..f5a64bfa2 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -186,11 +186,12 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ } if (duration > 0x7FFF) { - if (DBGLEVEL >= DBG_ERROR) { + /* + if (DBGLEVEL >= DBG_DEBUG) { Dbprintf("Error in LogTrace: duration too long for 15 bits encoding: 0x%08x start: 0x%08x end: 0x%08x", duration, timestamp_start, timestamp_end); } + */ duration /= 32; -// duration = 0; } hdr->timestamp = timestamp_start; From f00c0eb5cf91d9efb6db450d943f7799c1acc940 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 22:05:38 +0200 Subject: [PATCH 061/139] prep for timings --- armsrc/iclass.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 84f854c9c..c5fc534f4 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -752,12 +752,12 @@ send: // THE READER CODE -static void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time) { +static void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time, uint32_t *end_time) { CodeIso15693AsReader(frame, len); tosend_t *ts = get_tosend(); TransmitTo15693Tag(ts->buf, ts->max, start_time); - uint32_t end_time = *start_time + (32 * ((8 * ts->max) - 4)); // substract the 4 padding bits after EOF - LogTrace(frame, len, (*start_time * 4), (end_time * 4), NULL, true); + *end_time = *start_time + (32 * ((8 * ts->max) - 4)); // substract the 4 padding bits after EOF + LogTrace(frame, len, (*start_time * 4), (*end_time * 4), NULL, true); } static bool iclass_send_cmd_with_retries(uint8_t* cmd, size_t cmdsize, uint8_t* resp, size_t max_resp_size, @@ -765,7 +765,7 @@ static bool iclass_send_cmd_with_retries(uint8_t* cmd, size_t cmdsize, uint8_t* uint16_t timeout, uint32_t *eof_time) { while (tries-- > 0) { - iclass_send_as_reader(cmd, cmdsize, &start_time); + iclass_send_as_reader(cmd, cmdsize, &start_time, eof_time); if (resp == NULL) return true; @@ -773,7 +773,7 @@ static bool iclass_send_cmd_with_retries(uint8_t* cmd, size_t cmdsize, uint8_t* if (expected_size == GetIso15693AnswerFromTag(resp, max_resp_size, timeout, eof_time)) { return true; } -// start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; } return false; } @@ -802,19 +802,15 @@ static bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t // wakeup uint32_t start_time = GetCountSspClk(); - iclass_send_as_reader(act_all, 1, &start_time); + iclass_send_as_reader(act_all, 1, &start_time, eof_time); int len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_ACTALL, eof_time); if (len < 0) return false; -/* - bool ok = iclass_send_cmd_with_retries(act_all, 1, resp, sizeof(resp), 1, 5, start_time, ICLASS_READER_TIMEOUT_ACTALL, eof_time); - if (ok == false) - return false; -*/ + // send Identify start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(identify, 1, &start_time); + iclass_send_as_reader(identify, 1, &start_time, eof_time); // expect a 10-byte response here, 8 byte anticollision-CSN and 2 byte CRC len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); @@ -826,7 +822,7 @@ static bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t // select the card start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(select, sizeof(select), &start_time); + iclass_send_as_reader(select, sizeof(select), &start_time, eof_time); // expect a 10-byte response here, 8 byte CSN and 2 byte CRC len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); @@ -838,7 +834,7 @@ static bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t // card selected, now read config (block1) (only 8 bytes no CRC) start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(read_conf, sizeof(read_conf), &start_time); + iclass_send_as_reader(read_conf, sizeof(read_conf), &start_time, eof_time); // expect a 8-byte response here len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); @@ -850,7 +846,7 @@ static bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t // card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time); + iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, eof_time); // expect a 8-byte response here len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); @@ -1215,7 +1211,7 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; // Auth Sequence MUST begin with reading e-purse. (block2) // Card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) - iclass_send_as_reader(readcheck_cc, sizeof(readcheck_cc), &start_time); + iclass_send_as_reader(readcheck_cc, sizeof(readcheck_cc), &start_time, &eof_time); LED_B_OFF(); } From dd7a3c208ce3720fa83fc41823d532ced30d1819 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 22:06:15 +0200 Subject: [PATCH 062/139] init arrays... --- armsrc/iso15693.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 82640d0cb..c12e2837b 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -621,7 +621,7 @@ static void DecodeTagReset(DecodeTag_t *DecodeTag) { int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeout, uint32_t *eof_time) { int samples = 0, ret = 0; - uint16_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; + uint16_t dmaBuf[ISO15693_DMA_BUFFER_SIZE] = {0}; // the Decoder data structure DecodeTag_t DecodeTag = { 0 }; @@ -678,7 +678,7 @@ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeo } if (samples > timeout && DecodeTag.state < STATE_TAG_RECEIVING_DATA) { - ret = -1; // timeout + ret = -3; // timeout break; } @@ -704,7 +704,7 @@ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeo Dbprintf("timing: sof_time = %d, eof_time = %d", (sof_time * 4), (*eof_time * 4)); } - if (ret == -1) { + if (ret == -1) { return ret; } @@ -1016,7 +1016,7 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo int samples = 0; bool gotFrame = false; - uint8_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; + uint8_t dmaBuf[ISO15693_DMA_BUFFER_SIZE] = {0}; // the decoder data structure DecodeReader_t DecodeReader = {0}; @@ -1148,7 +1148,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { set_tracing(true); // The DMA buffer, used to stream samples from the FPGA - uint16_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; + uint16_t dmaBuf[ISO15693_DMA_BUFFER_SIZE] = {0}; // Count of samples received so far, so that we can include timing // information in the trace buffer. From 2fa9bd585fd3e8305ae0ce86380775dc074f6a61 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 22:06:50 +0200 Subject: [PATCH 063/139] style --- client/src/comms.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/comms.c b/client/src/comms.c index 5976cfcc1..209fd47cd 100644 --- a/client/src/comms.c +++ b/client/src/comms.c @@ -303,7 +303,7 @@ static void PacketResponseReceived(PacketResponseNG *packet) { break; } case CMD_DEBUG_PRINT_INTEGERS: { - if (! packet->ng) + if (packet->ng == false) PrintAndLogEx(NORMAL, "[" _MAGENTA_("pm3") "] ["_BLUE_("#")"] " "%" PRIx64 ", %" PRIx64 ", %" PRIx64 "", packet->oldarg[0], packet->oldarg[1], packet->oldarg[2]); break; } From 4b14f21c77717d21509906b2fe7a53e71b92545e Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 13 Jul 2020 23:27:16 +0200 Subject: [PATCH 064/139] fix iclass simulation --- armsrc/iclass.c | 393 ++++++++++++++++++++++++++---------------------- 1 file changed, 213 insertions(+), 180 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index c5fc534f4..d7c57ee01 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -148,8 +148,6 @@ static void CodeIClassTagSOF(void) { // turn off afterwards void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) { - if (DBGLEVEL > 3) Dbprintf("[+] iClass_simulate Enter"); - LEDsoff(); Iso15693InitTag(); @@ -176,7 +174,7 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain doIClassSimulation(ICLASS_SIM_MODE_CSN, NULL); } else if (simType == ICLASS_SIM_MODE_READER_ATTACK) { - Dbprintf("[+] going into attack mode, %d CSNS sent", numberOfCSNS); + Dbprintf("going into attack mode, %d CSNS sent", numberOfCSNS); // In this mode, a number of csns are within datain. We'll simulate each one, one at a time // in order to collect MAC's from the reader. This can later be used in an offlne-attack // in order to obtain the keys, as in the "dismantling iclass"-paper. @@ -204,7 +202,7 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain // the collected data (mac_response) is doubled out since we are trying to collect both keys in the keyroll process. // Keyroll iceman 9 csns * 8 * 2 = 144 // keyroll CARL55 15csns * 8 * 2 = 15 * 8 * 2 = 240 - Dbprintf("[+] going into attack keyroll mode, %d CSNS sent", numberOfCSNS); + Dbprintf("going into attack keyroll mode, %d CSNS sent", numberOfCSNS); // In this mode, a number of csns are within datain. We'll simulate each one, one at a time // in order to collect MAC's from the reader. This can later be used in an offlne-attack // in order to obtain the keys, as in the "dismantling iclass"-paper. @@ -218,14 +216,14 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain memcpy(emulator, datain + (i * 8), 8); // keyroll 1 - if (doIClassSimulation(MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE)) { + if (doIClassSimulation(ICLASS_SIM_MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE)) { reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2); // Button pressed goto out; } // keyroll 2 - if (doIClassSimulation(MODE_EXIT_AFTER_MAC, mac_responses + (i + numberOfCSNS) * EPURSE_MAC_SIZE)) { + if (doIClassSimulation(ICLASS_SIM_MODE_EXIT_AFTER_MAC, mac_responses + (i + numberOfCSNS) * EPURSE_MAC_SIZE)) { reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2); // Button pressed goto out; @@ -237,7 +235,7 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain } else { // We may want a mode here where we hardcode the csns to use (from proxclone). // That will speed things up a little, but not required just yet. - DbpString("[-] the mode is not implemented, reserved for future use"); + DbpString("the mode is not implemented, reserved for future use"); } out: @@ -471,116 +469,139 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { block = receivedCmd[1]; if (cmd == ICLASS_CMD_ACTALL && len == 1) { // 0x0A - // Reader in anticollission phase + // Reader in anti collision phase if (chip_state != HALTED) { modulated_response = resp_sof; modulated_response_size = resp_sof_len; chip_state = ACTIVATED; + } + goto send; + + } else if (cmd == ICLASS_CMD_READ_OR_IDENTIFY && len == 1) { // 0x0C + // Reader asks for anti collision CSN + if (chip_state == SELECTED || chip_state == ACTIVATED) { + modulated_response = resp_anticoll; + modulated_response_size = resp_anticoll_len; + trace_data = anticoll_data; + trace_data_size = sizeof(anticoll_data); + } + goto send; + + } else if (cmd == ICLASS_CMD_READ_OR_IDENTIFY && len == 4) { // 0x0C + + if (chip_state != SELECTED) { goto send; } - - } else if (cmd == ICLASS_CMD_READ_OR_IDENTIFY) { // 0x0C - if (len == 1) { - // Reader asks for anticollission CSN - if (chip_state == SELECTED || chip_state == ACTIVATED) { - modulated_response = resp_anticoll; - modulated_response_size = resp_anticoll_len; //order = 2; - trace_data = anticoll_data; - trace_data_size = sizeof(anticoll_data); + // block0,1,2,5 is always readable. + switch (block) { + case 0: { // csn (0c 00) + modulated_response = resp_csn; + modulated_response_size = resp_csn_len; + trace_data = csn_data; + trace_data_size = sizeof(csn_data); goto send; } - } + case 1: { // configuration (0c 01) + modulated_response = resp_conf; + modulated_response_size = resp_conf_len; + trace_data = conf_block; + trace_data_size = sizeof(conf_block); + goto send; + } + case 2: {// e-purse (0c 02) + modulated_response = resp_cc; + modulated_response_size = resp_cc_len; + trace_data = card_challenge_data; + trace_data_size = sizeof(card_challenge_data); + // set epurse of sim2,4 attack + if (reader_mac_buf != NULL) { + memcpy(reader_mac_buf, card_challenge_data, 8); + } + goto send; + } + case 3: + case 4: { // Kd, Kc, always respond with 0xff bytes + modulated_response = resp_ff; + modulated_response_size = resp_ff_len; + trace_data = ff_data; + trace_data_size = sizeof(ff_data); + goto send; + } + case 5: { // Application Issuer Area (0c 05) + modulated_response = resp_aia; + modulated_response_size = resp_aia_len; + trace_data = aia_data; + trace_data_size = sizeof(aia_data); + goto send; + } + default : { + if (simulationMode == ICLASS_SIM_MODE_FULL) { // 0x0C + //Read block + //Take the data... + memcpy(data_generic_trace, emulator + (block << 3), 8); + AddCrc(data_generic_trace, 8); + trace_data = data_generic_trace; + trace_data_size = 10; + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(modulated_response, ts->buf, ts->max); + modulated_response_size = ts->max; + } + goto send; + } + } // swith - if (len == 4) { - if (chip_state == SELECTED) { - // block0,1,2,5 is always readable. - switch (block) { - case 0: { // csn (0c 00) - modulated_response = resp_csn; - modulated_response_size = resp_csn_len; - trace_data = csn_data; - trace_data_size = sizeof(csn_data); - goto send; - } - case 1: { // configuration (0c 01) - modulated_response = resp_conf; - modulated_response_size = resp_conf_len; - trace_data = conf_block; - trace_data_size = sizeof(conf_block); - goto send; - } - case 2: {// e-purse (0c 02) - modulated_response = resp_cc; - modulated_response_size = resp_cc_len; - trace_data = card_challenge_data; - trace_data_size = sizeof(card_challenge_data); - // set epurse of sim2,4 attack - if (reader_mac_buf != NULL) { - memcpy(reader_mac_buf, card_challenge_data, 8); - } - goto send; - } - case 3: - case 4: { // Kd, Kc, always respond with 0xff bytes - modulated_response = resp_ff; - modulated_response_size = resp_ff_len; - trace_data = ff_data; - trace_data_size = sizeof(ff_data); - goto send; - } - case 5: { // Application Issuer Area (0c 05) - modulated_response = resp_aia; - modulated_response_size = resp_aia_len; - trace_data = aia_data; - trace_data_size = sizeof(aia_data); - goto send; - } - default : { - if (simulationMode == ICLASS_SIM_MODE_FULL) { // 0x0C - //Read block - //Take the data... - memcpy(data_generic_trace, emulator + (block << 3), 8); - AddCrc(data_generic_trace, 8); - trace_data = data_generic_trace; - trace_data_size = 10; - CodeIso15693AsTag(trace_data, trace_data_size); - memcpy(modulated_response, ts->buf, ts->max); - modulated_response_size = ts->max; - goto send; - } - break; - } - } // swith - } // selected - } // if 4 - } else if (cmd == ICLASS_CMD_SELECT) { // 0x81 + } else if (cmd == ICLASS_CMD_SELECT && len == 9) { // 0x81 // Reader selects anticollission CSN. // Tag sends the corresponding real CSN - modulated_response = resp_csn; - modulated_response_size = resp_csn_len; //order = 3; - trace_data = csn_data; - trace_data_size = sizeof(csn_data); + if (chip_state == ACTIVATED || chip_state == SELECTED) { + if (!memcmp(receivedCmd + 1, anticoll_data, 8)) { + modulated_response = resp_csn; + modulated_response_size = resp_csn_len; + trace_data = csn_data; + trace_data_size = sizeof(csn_data); + chip_state = SELECTED; + } else { + chip_state = IDLE; + } + } else if (chip_state == HALTED) { + // RESELECT with CSN + if (!memcmp(receivedCmd + 1, csn_data, 8)) { + modulated_response = resp_csn; + modulated_response_size = resp_csn_len; + trace_data = csn_data; + trace_data_size = sizeof(csn_data); + chip_state = SELECTED; + } + } goto send; } else if (cmd == ICLASS_CMD_READCHECK) { // 0x88 // Read e-purse KD (88 02) KC (18 02) - if (chip_state == SELECTED) { - if ( ICLASS_DEBIT(cmd) ){ - cipher_state = &cipher_state_KD[current_page]; - diversified_key = diversified_kd; - } else { - cipher_state = &cipher_state_KC[current_page]; - diversified_key = diversified_kc; - } - modulated_response = resp_cc; - modulated_response_size = resp_cc_len; - trace_data = card_challenge_data; - trace_data_size = sizeof(card_challenge_data); + if (chip_state != SELECTED) { goto send; } - } else if (cmd == ICLASS_CMD_CHECK) { // 0x05 + if ( ICLASS_DEBIT(cmd) ){ + cipher_state = &cipher_state_KD[current_page]; + diversified_key = diversified_kd; + } else { + cipher_state = &cipher_state_KC[current_page]; + diversified_key = diversified_kc; + } + + modulated_response = resp_cc; + modulated_response_size = resp_cc_len; + trace_data = card_challenge_data; + trace_data_size = sizeof(card_challenge_data); + goto send; + + } else if (cmd == ICLASS_CMD_CHECK && len == 9) { // 0x05 + // Reader random and reader MAC!!! + if (chip_state != SELECTED) { + goto send; + } + if (simulationMode == ICLASS_SIM_MODE_FULL) { // NR, from reader, is in receivedCmd +1 opt_doTagMAC_2(*cipher_state, receivedCmd + 1, data_generic_trace, diversified_key); @@ -598,17 +619,18 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { modulated_response_size = 0; trace_data = NULL; trace_data_size = 0; + chip_state = HALTED; - if (simulationMode == MODE_EXIT_AFTER_MAC) { + if (simulationMode == ICLASS_SIM_MODE_EXIT_AFTER_MAC) { if (DBGLEVEL == DBG_EXTENDED) { - Dbprintf("[+] CSN: %02x %02x %02x %02x %02x %02x %02x %02x", csn[0], csn[1], csn[2], csn[3], csn[4], csn[5], csn[6], csn[7]); - Dbprintf("[+] RDR: (len=%02d): %02x %02x %02x %02x %02x %02x %02x %02x %02x", len, + Dbprintf("CSN: %02x %02x %02x %02x %02x %02x %02x %02x", csn[0], csn[1], csn[2], csn[3], csn[4], csn[5], csn[6], csn[7]); + Dbprintf("RDR: (len=%02d): %02x %02x %02x %02x %02x %02x %02x %02x %02x", len, receivedCmd[0], receivedCmd[1], receivedCmd[2], receivedCmd[3], receivedCmd[4], receivedCmd[5], receivedCmd[6], receivedCmd[7], receivedCmd[8]); } else { - Dbprintf("[+] CSN: %02x .... %02x OK", csn[0], csn[7]); + Dbprintf("CSN: %02x .... %02x OK", csn[0], csn[7]); } if (reader_mac_buf != NULL) { // save NR and MAC for sim 2,4 @@ -616,119 +638,130 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } exit_loop = true; } + } goto send; } else if (cmd == ICLASS_CMD_HALT && options == 0 && len == 1) { - if (chip_state == SELECTED) { - // Reader ends the session - modulated_response = resp_sof; - modulated_response_size = resp_sof_len; - chip_state = HALTED; + if (chip_state != SELECTED) { goto send; } + // Reader ends the session + modulated_response = resp_sof; + modulated_response_size = resp_sof_len; + chip_state = HALTED; + goto send; } else if (simulationMode == ICLASS_SIM_MODE_FULL && cmd == ICLASS_CMD_READ4 && len == 4) { // 0x06 - if (chip_state == SELECTED) { - //Read block - memcpy(data_generic_trace, emulator + (current_page * page_size) + (block * 8), 8 * 4); - AddCrc(data_generic_trace, 8 * 4); - trace_data = data_generic_trace; - trace_data_size = 34; - CodeIso15693AsTag(trace_data, trace_data_size); - memcpy(modulated_response, ts->buf, ts->max); - modulated_response_size = ts->max; + if (chip_state != SELECTED) { goto send; } + //Read block + memcpy(data_generic_trace, emulator + (current_page * page_size) + (block * 8), 8 * 4); + AddCrc(data_generic_trace, 8 * 4); + trace_data = data_generic_trace; + trace_data_size = 34; + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(modulated_response, ts->buf, ts->max); + modulated_response_size = ts->max; + goto send; } else if (simulationMode == ICLASS_SIM_MODE_FULL && cmd == ICLASS_CMD_UPDATE && (len == 12 || len == 14)) { // We're expected to respond with the data+crc, exactly what's already in the receivedCmd // receivedCmd is now UPDATE 1b | ADDRESS 1b | DATA 8b | Signature 4b or CRC 2b - if (chip_state == SELECTED) { + if (chip_state != SELECTED) { + goto send; + } - if (block == 2) { // update e-purse - memcpy(card_challenge_data, receivedCmd + 2, 8); - CodeIso15693AsTag(card_challenge_data, sizeof(card_challenge_data)); - memcpy(resp_cc, ts->buf, ts->max); - resp_cc_len = ts->max; - cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd); - cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kc); - - } else if (block == 3) { // update Kd - for (int i = 0; i < 8; i++) { - if (personalization_mode) { - diversified_kd[i] = receivedCmd[2 + i]; - } else { - diversified_kd[i] ^= receivedCmd[2 + i]; - } + if (block == 2) { // update e-purse + memcpy(card_challenge_data, receivedCmd + 2, 8); + CodeIso15693AsTag(card_challenge_data, sizeof(card_challenge_data)); + memcpy(resp_cc, ts->buf, ts->max); + resp_cc_len = ts->max; + cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd); + cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kc); + + } else if (block == 3) { // update Kd + for (int i = 0; i < 8; i++) { + if (personalization_mode) { + diversified_kd[i] = receivedCmd[2 + i]; + } else { + diversified_kd[i] ^= receivedCmd[2 + i]; } - cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd); - - } else if (block == 4) { // update Kc - for (int i = 0; i < 8; i++) { - if (personalization_mode) { - diversified_kc[i] = receivedCmd[2 + i]; - } else { - diversified_kc[i] ^= receivedCmd[2 + i]; - } - } - cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kc); } - - // update emulator + cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd); + if (simulationMode == ICLASS_SIM_MODE_FULL) { + memcpy(emulator + (current_page * page_size) + (8 * 3), diversified_kd, 8); + } + } else if (block == 4) { // update Kc + for (int i = 0; i < 8; i++) { + if (personalization_mode) { + diversified_kc[i] = receivedCmd[2 + i]; + } else { + diversified_kc[i] ^= receivedCmd[2 + i]; + } + } + cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kc); + if (simulationMode == ICLASS_SIM_MODE_FULL) { + memcpy(emulator + (current_page * page_size) + (8 * 4), diversified_kc, 8); + } + } else if (simulationMode == ICLASS_SIM_MODE_FULL) { + // update emulator memory memcpy(emulator + (current_page * page_size) + (8 * block), receivedCmd + 2, 8); + } - memcpy(data_generic_trace, receivedCmd + 2, 8); + memcpy(data_generic_trace, receivedCmd + 2, 8); + AddCrc(data_generic_trace, 8); + trace_data = data_generic_trace; + trace_data_size = 10; + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ts->buf, ts->max); + modulated_response = data_response; + modulated_response_size = ts->max; + goto send; + + } else if (cmd == ICLASS_CMD_PAGESEL && len == 4) { // 0x84 + // Pagesel, + // - enables to select a page in the selected chip memory and return its configuration block + // Chips with a single page will not answer to this command + // Otherwise, we should answer 8bytes (conf block 1) + 2bytes CRC + if (chip_state != SELECTED) { + goto send; + } + + if (simulationMode == ICLASS_SIM_MODE_FULL && max_page > 0) { + + current_page = receivedCmd[1]; + + memcpy(data_generic_trace, emulator + (current_page * page_size) + (8 * 1), 8); + memcpy(diversified_kd, emulator + (current_page * page_size) + (8 * 3), 8); + memcpy(diversified_kc, emulator + (current_page * page_size) + (8 * 4), 8); + + cipher_state = &cipher_state_KD[current_page]; + + personalization_mode = data_generic_trace[7] & 0x80; AddCrc(data_generic_trace, 8); + trace_data = data_generic_trace; trace_data_size = 10; + CodeIso15693AsTag(trace_data, trace_data_size); memcpy(data_response, ts->buf, ts->max); modulated_response = data_response; modulated_response_size = ts->max; } goto send; - - } else if (receivedCmd[0] == ICLASS_CMD_PAGESEL && len == 4) { // 0x84 - // Pagesel, - // - enables to select a page in the selected chip memory and return its configuration block - // Chips with a single page will not answer to this command - // Otherwise, we should answer 8bytes (conf block 1) + 2bytes CRC - if (chip_state == SELECTED) { - - if (simulationMode == ICLASS_SIM_MODE_FULL && max_page > 0) { - - current_page = receivedCmd[1]; - - memcpy(data_generic_trace, emulator + (current_page * page_size) + (8 * 1), 8); - memcpy(diversified_kd, emulator + (current_page * page_size) + (8 * 3), 8); - memcpy(diversified_kc, emulator + (current_page * page_size) + (8 * 4), 8); - - cipher_state = &cipher_state_KD[current_page]; - - personalization_mode = data_generic_trace[7] & 0x80; - AddCrc(data_generic_trace, 8); - - trace_data = data_generic_trace; - trace_data_size = 10; - - CodeIso15693AsTag(trace_data, trace_data_size); - memcpy(data_response, ts->buf, ts->max); - modulated_response = data_response; - modulated_response_size = ts->max; - } - } -// } else if(receivedCmd[0] == ICLASS_CMD_DETECT) { // 0x0F - } else if (receivedCmd[0] == 0x26 && len == 5) { +// } else if(cmd == ICLASS_CMD_DETECT) { // 0x0F + } else if (cmd == 0x26 && len == 5) { // standard ISO15693 INVENTORY command. Ignore. } else { // Never seen this command before if (DBGLEVEL >= DBG_EXTENDED) - print_result("[-] Unhandled command received ", receivedCmd, len); + print_result("Unhandled command received ", receivedCmd, len); } send: @@ -745,7 +778,7 @@ send: LEDsoff(); if (button_pressed) - DbpString("[+] button pressed"); + DbpString("button pressed"); return button_pressed; } From 0f507c24e1722f868bebb598c03f4cce9c9e6eaa Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 14 Jul 2020 00:16:25 +0200 Subject: [PATCH 065/139] chg: readblock assuming better --- client/src/cmdhficlass.c | 45 ++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index b4fc7dcb2..4d21ca3d1 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -371,6 +371,15 @@ typedef enum { TRIPLEDES } BLOCK79ENCRYPTION; +static inline uint32_t leadingzeros(uint64_t a) { +#if defined __GNUC__ + return __builtin_clzll(a); +#else + return 0; +#endif +} + + static uint8_t isset(uint8_t val, uint8_t mask) { return (val & mask); } @@ -1857,14 +1866,14 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo DropField(); - PrintAndLogEx(SUCCESS, "block %02X: " _GREEN_("%s"), blockno, sprint_hex(result->blockdata, sizeof(result->blockdata))); + PrintAndLogEx(SUCCESS, " block %02X : " _GREEN_("%s"), blockno, sprint_hex(result->blockdata, sizeof(result->blockdata))); if (memcmp(result->blockdata, empty, 8) == 0) return PM3_SUCCESS; if (blockno < 6 || blockno > 7) return PM3_SUCCESS; - + bool use_sc = IsCryptoHelperPresent(verbose); if (use_sc == false) return PM3_SUCCESS; @@ -1876,10 +1885,16 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo break; } case 7: { - PrintAndLogEx(INFO, "Trying to decrypt..."); - + PrintAndLogEx(INFO, "-----------------------------------------------------------------"); uint8_t dec_data[8]; - Decrypt(result->blockdata, dec_data); + uint64_t a = bytes_to_num(result->blockdata, 8); + if (leadingzeros(a) < 16) { + PrintAndLogEx(INFO, "data looks encrypted, false positive is possible"); + Decrypt(result->blockdata, dec_data); + PrintAndLogEx(SUCCESS, "decrypted : " _GREEN_("%s"), sprint_hex(dec_data, sizeof(dec_data))); + } else { + PrintAndLogEx(INFO, "data looks unencrypted, trying to decode"); + } if (memcmp(dec_data, empty, 8) != 0) { @@ -1889,28 +1904,26 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo mid = bytes_to_num(dec_data, 4); bot = bytes_to_num(dec_data + 4, 4); - PrintAndLogEx(INFO, "Binary"); - - char hexstr[8 + 1] = {0}; + char hexstr[16 + 1] = {0}; hex_to_buffer((uint8_t *)hexstr, dec_data, 8, sizeof(hexstr) - 1, 0, 0, true); - - char binstr[8 * 8 + 1] = {0}; + char binstr[64 + 1] = {0}; hextobinstring(binstr, hexstr); uint8_t i = 0; while (i < strlen(binstr) && binstr[i++] == '0'); - PrintAndLogEx(SUCCESS, "%s", binstr + i); - + i &= 0x3C; + PrintAndLogEx(SUCCESS, " bin : %s", binstr + i); PrintAndLogEx(INFO, "-----------------------------------------------------------------"); wiegand_message_t packed = initialize_message_object(top, mid, bot); HIDTryUnpack(&packed, true); - PrintAndLogEx(INFO, "-----------------------------------------------------------------"); } else { - PrintAndLogEx(INFO, "No credential found."); + PrintAndLogEx(INFO, "no credential found"); } - } - } + PrintAndLogEx(INFO, "-----------------------------------------------------------------"); + break; + } + } return PM3_SUCCESS; } From 889d1c93cb0ad63f02315e49174876fbba6bc360 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 14 Jul 2020 15:25:56 +0200 Subject: [PATCH 066/139] fix: coverity mem leak --- client/src/cmdhficlass.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 4d21ca3d1..4f51042e9 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -536,9 +536,11 @@ static int CmdHFiClassSniff(const char *Cmd) { arg_lit0("j", "jam", "Jam (prevent) e-purse updates"), arg_param_end }; - CLIExecWithReturn(ctx, Cmd, argtable, true); - + + CLIExecWithReturn(ctx, Cmd, argtable, true); bool jam_epurse_update = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + const uint8_t update_epurse_sequence[2] = {0x87, 0x02}; struct { @@ -1888,7 +1890,7 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo PrintAndLogEx(INFO, "-----------------------------------------------------------------"); uint8_t dec_data[8]; uint64_t a = bytes_to_num(result->blockdata, 8); - if (leadingzeros(a) < 16) { + if (leadingzeros(a) < 12) { PrintAndLogEx(INFO, "data looks encrypted, false positive is possible"); Decrypt(result->blockdata, dec_data); PrintAndLogEx(SUCCESS, "decrypted : " _GREEN_("%s"), sprint_hex(dec_data, sizeof(dec_data))); From bac126530c68afa798fd4574e19cb7804886b170 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 14 Jul 2020 15:43:54 +0200 Subject: [PATCH 067/139] prep for stand mode --- armsrc/iclass.c | 75 ++++++++++++++++++++++++++++++++++++------------- armsrc/iclass.h | 4 ++- 2 files changed, 58 insertions(+), 21 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index d7c57ee01..078da13e5 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -147,6 +147,10 @@ static void CodeIClassTagSOF(void) { */ // turn off afterwards void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) { + iclass_simulate(arg0, arg1, arg2, datain, NULL, NULL); +} + +void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_t *datain, uint8_t *dataout, uint16_t *dataoutlen) { LEDsoff(); @@ -155,54 +159,63 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain clear_trace(); set_tracing(true); - uint32_t simType = arg0; - uint32_t numberOfCSNS = arg1; - //Use the emulator memory for SIM uint8_t *emulator = BigBuf_get_EM_addr(); uint8_t mac_responses[PM3_CMD_DATA_SIZE] = { 0 }; - if (simType == ICLASS_SIM_MODE_CSN) { + if (sim_type == ICLASS_SIM_MODE_CSN) { // Use the CSN from commandline memcpy(emulator, datain, 8); doIClassSimulation(ICLASS_SIM_MODE_CSN, NULL); - } else if (simType == ICLASS_SIM_MODE_CSN_DEFAULT) { + + } else if (sim_type == ICLASS_SIM_MODE_CSN_DEFAULT) { //Default CSN uint8_t csn[] = { 0x03, 0x1f, 0xec, 0x8a, 0xf7, 0xff, 0x12, 0xe0 }; // Use the CSN from commandline memcpy(emulator, csn, 8); doIClassSimulation(ICLASS_SIM_MODE_CSN, NULL); - } else if (simType == ICLASS_SIM_MODE_READER_ATTACK) { - Dbprintf("going into attack mode, %d CSNS sent", numberOfCSNS); + } else if (sim_type == ICLASS_SIM_MODE_READER_ATTACK) { + + Dbprintf("going into attack mode, %d CSNS sent", num_csns); // In this mode, a number of csns are within datain. We'll simulate each one, one at a time // in order to collect MAC's from the reader. This can later be used in an offlne-attack // in order to obtain the keys, as in the "dismantling iclass"-paper. #define EPURSE_MAC_SIZE 16 int i = 0; - for (; i < numberOfCSNS && i * EPURSE_MAC_SIZE + 8 < PM3_CMD_DATA_SIZE; i++) { + for (; i < num_csns && i * EPURSE_MAC_SIZE + 8 < PM3_CMD_DATA_SIZE; i++) { memcpy(emulator, datain + (i * 8), 8); if (doIClassSimulation(ICLASS_SIM_MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE)) { + + if (dataoutlen) + *dataoutlen = i * EPURSE_MAC_SIZE; + // Button pressed - reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i, 0, mac_responses, i * EPURSE_MAC_SIZE); + if (send_reply) + reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i, 0, mac_responses, i * EPURSE_MAC_SIZE); goto out; } } - reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i, 0, mac_responses, i * EPURSE_MAC_SIZE); + if (dataoutlen) + *dataoutlen = i * EPURSE_MAC_SIZE; - } else if (simType == ICLASS_SIM_MODE_FULL) { + if (send_reply) + reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i, 0, mac_responses, i * EPURSE_MAC_SIZE); + + } else if (sim_type == ICLASS_SIM_MODE_FULL) { //This is 'full sim' mode, where we use the emulator storage for data. //ie: BigBuf_get_EM_addr should be previously filled with data from the "eload" command doIClassSimulation(ICLASS_SIM_MODE_FULL, NULL); - } else if (simType == ICLASS_SIM_MODE_READER_ATTACK_KEYROLL) { + + } else if (sim_type == ICLASS_SIM_MODE_READER_ATTACK_KEYROLL) { // This is the KEYROLL version of sim 2. // the collected data (mac_response) is doubled out since we are trying to collect both keys in the keyroll process. // Keyroll iceman 9 csns * 8 * 2 = 144 // keyroll CARL55 15csns * 8 * 2 = 15 * 8 * 2 = 240 - Dbprintf("going into attack keyroll mode, %d CSNS sent", numberOfCSNS); + Dbprintf("going into attack keyroll mode, %d CSNS sent", num_csns); // In this mode, a number of csns are within datain. We'll simulate each one, one at a time // in order to collect MAC's from the reader. This can later be used in an offlne-attack // in order to obtain the keys, as in the "dismantling iclass"-paper. @@ -211,27 +224,44 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain // attack below is same as SIM 2, but we run the CSN twice to collected the mac for both keys. int i = 0; // The usb data is 512 bytes, fitting 65 8-byte CSNs in there. iceman fork uses 9 CSNS - for (; i < numberOfCSNS && i * EPURSE_MAC_SIZE + 8 < PM3_CMD_DATA_SIZE; i++) { + for (; i < num_csns && i * EPURSE_MAC_SIZE + 8 < PM3_CMD_DATA_SIZE; i++) { memcpy(emulator, datain + (i * 8), 8); // keyroll 1 if (doIClassSimulation(ICLASS_SIM_MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE)) { - reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2); + + if (dataoutlen) + *dataoutlen = i * EPURSE_MAC_SIZE * 2; + + if (send_reply) + reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2); + // Button pressed goto out; } // keyroll 2 - if (doIClassSimulation(ICLASS_SIM_MODE_EXIT_AFTER_MAC, mac_responses + (i + numberOfCSNS) * EPURSE_MAC_SIZE)) { - reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2); + if (doIClassSimulation(ICLASS_SIM_MODE_EXIT_AFTER_MAC, mac_responses + (i + num_csns) * EPURSE_MAC_SIZE)) { + + if (dataoutlen) + *dataoutlen = i * EPURSE_MAC_SIZE * 2; + + if (send_reply) + reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2); + // Button pressed goto out; } } - // double the amount of collected data. - reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2); + if (dataoutlen) + *dataoutlen = i * EPURSE_MAC_SIZE * 2; + + // double the amount of collected data. + if (send_reply) + reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2); + } else { // We may want a mode here where we hardcode the csns to use (from proxclone). // That will speed things up a little, but not required just yet. @@ -239,6 +269,9 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain } out: + if (dataout && dataoutlen) + memcpy(dataout, mac_responses, *dataoutlen); + switch_off(); BigBuf_free_keep_EM(); } @@ -683,7 +716,9 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { resp_cc_len = ts->max; cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd); cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kc); - + if (simulationMode == ICLASS_SIM_MODE_FULL) { + memcpy(emulator + (current_page * page_size) + (8 * 2), card_challenge_data, 8); + } } else if (block == 3) { // update Kd for (int i = 0; i < 8; i++) { if (personalization_mode) { diff --git a/armsrc/iclass.h b/armsrc/iclass.h index 20a862535..3b4de40f0 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -14,7 +14,6 @@ #include "common.h" void SniffIClass(uint8_t jam_search_len, uint8_t *jam_search_string); -void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void ReaderIClass(uint8_t arg0); void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac); void iClass_Authentication(uint8_t *mac); @@ -27,4 +26,7 @@ void iClass_ReadCheck(uint8_t blockno, uint8_t keytype); int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf); + +void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); +void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_t *datain, uint8_t *dataout, uint16_t *dataoutlen); #endif From 4dabccf9dbe46853855e454b82ea5cc41ee824f9 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 14 Jul 2020 15:44:23 +0200 Subject: [PATCH 068/139] style --- armsrc/spiffs.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/armsrc/spiffs.c b/armsrc/spiffs.c index 025146b55..10c923c14 100644 --- a/armsrc/spiffs.c +++ b/armsrc/spiffs.c @@ -496,10 +496,10 @@ int rdv40_spiffs_read_as_symlink(char *filename, uint8_t *dst, uint32_t size, RD // rdv40_spiffs_read_as_symlink((uint8_t *)"world",(uint8_t *) buffer, orig_file_size, RDV40_SPIFFS_SAFETY_SAFE); // TODO : FORBID creating a symlink with a basename (before.lnk) which already exists as a file ! int rdv40_spiffs_make_symlink(char *linkdest, char *filename, RDV40SpiFFSSafetyLevel level) { - RDV40_SPIFFS_SAFE_FUNCTION( // - char linkfilename[SPIFFS_OBJ_NAME_LEN]; // + RDV40_SPIFFS_SAFE_FUNCTION( + char linkfilename[SPIFFS_OBJ_NAME_LEN]; sprintf(linkfilename, "%s.lnk", filename); - write_to_spiffs((char *)linkfilename, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN); // + write_to_spiffs((char *)linkfilename, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN); ) } @@ -510,20 +510,20 @@ int rdv40_spiffs_make_symlink(char *linkdest, char *filename, RDV40SpiFFSSafetyL // preexistance, avoiding a link being created if filename exists, or avoiding a file being created if // symlink exists with same name int rdv40_spiffs_read_as_filetype(char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level) { - RDV40_SPIFFS_SAFE_FUNCTION( // - RDV40SpiFFSFileType filetype = filetype_in_spiffs((char *)filename); // - switch (filetype) { - case RDV40_SPIFFS_FILETYPE_REAL: - rdv40_spiffs_read((char *)filename, (uint8_t *)dst, size, level); - break; - case RDV40_SPIFFS_FILETYPE_SYMLINK: - rdv40_spiffs_read_as_symlink((char *)filename, (uint8_t *)dst, size, level); - break; - case RDV40_SPIFFS_FILETYPE_BOTH: - case RDV40_SPIFFS_FILETYPE_UNKNOWN: - default: + RDV40_SPIFFS_SAFE_FUNCTION( + RDV40SpiFFSFileType filetype = filetype_in_spiffs((char *)filename); + switch (filetype) { + case RDV40_SPIFFS_FILETYPE_REAL: + rdv40_spiffs_read((char *)filename, (uint8_t *)dst, size, level); + break; + case RDV40_SPIFFS_FILETYPE_SYMLINK: + rdv40_spiffs_read_as_symlink((char *)filename, (uint8_t *)dst, size, level); + break; + case RDV40_SPIFFS_FILETYPE_BOTH: + case RDV40_SPIFFS_FILETYPE_UNKNOWN: + default: ; - } // + } ) } From d2a66cecba6ea66f0e0d7d85b74fb7fe47f27563 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 14 Jul 2020 15:44:38 +0200 Subject: [PATCH 069/139] style --- armsrc/BigBuf.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index f5a64bfa2..7cb440d82 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -114,8 +114,6 @@ void BigBuf_free_keep_EM(void) { s_bigbuf_hi = emulator_memory - (uint8_t *)BigBuf; else s_bigbuf_hi = s_bigbuf_size; - - // shouldn't this empty BigBuf also? } void BigBuf_print_status(void) { From ce981d91247bdfc45a4bd568cfc4f2698afd1139 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 14 Jul 2020 15:46:05 +0200 Subject: [PATCH 070/139] fpga 14b merge p.N --- armsrc/iso14443b.c | 322 ++++++++++++++++++++++----------------------- 1 file changed, 159 insertions(+), 163 deletions(-) diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index b06dbcb44..d9d08a966 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -138,9 +138,9 @@ static uint32_t iso14b_timeout = TR0; // them yet, just leaves them ready to send in ToSend[]. //----------------------------------------------------------------------------- static void CodeIso14443bAsTag(const uint8_t *cmd, int len) { - int i; + int i; - tosend_reset(); + tosend_reset(); // Transmit a burst of ones, as the initial thing that lets the // reader get phase sync. @@ -152,56 +152,56 @@ static void CodeIso14443bAsTag(const uint8_t *cmd, int len) { // Send TR1. // 10-11 ETU * 4times samples ONES - for (i = 0; i < 20; i++) { - SEND4STUFFBIT(1); - } + for (i = 0; i < 20; i++) { + SEND4STUFFBIT(1); + } // Send SOF. // 10-11 ETU * 4times samples ZEROS - for (i = 0; i < 10; i++) { - SEND4STUFFBIT(0); - } + for (i = 0; i < 10; i++) { + SEND4STUFFBIT(0); + } // 2-3 ETU * 4times samples ONES - for (i = 0; i < 2; i++) { - SEND4STUFFBIT(1); - } + for (i = 0; i < 2; i++) { + SEND4STUFFBIT(1); + } // data - for (i = 0; i < len; i++) { + for (i = 0; i < len; i++) { // Start bit - SEND4STUFFBIT(0); + SEND4STUFFBIT(0); - // Data bits - uint8_t b = cmd[i]; - for (int j = 0; j < 8; j++) { + // Data bits + uint8_t b = cmd[i]; + for (int j = 0; j < 8; j++) { SEND4STUFFBIT(b & 1); - b >>= 1; - } + b >>= 1; + } - // Stop bit - SEND4STUFFBIT(1); + // Stop bit + SEND4STUFFBIT(1); // Extra Guard bit // For PICC it ranges 0-18us (1etu = 9us) //SEND4STUFFBIT(1); - } + } // Send EOF. // 10-11 ETU * 4 sample rate = ZEROS - for(i = 0; i < 10; i++) { - SEND4STUFFBIT(0); - } + for(i = 0; i < 10; i++) { + SEND4STUFFBIT(0); + } // why this? - for(i = 0; i < 2; i++) { - SEND4STUFFBIT(1); - } + for(i = 0; i < 2; i++) { + SEND4STUFFBIT(1); + } tosend_t *ts = get_tosend(); - // Convert from last byte pos to length - ts->max++; + // Convert from last byte pos to length + ts->max++; } //----------------------------------------------------------------------------- @@ -451,10 +451,10 @@ static int GetIso14443bCommandFromReader(uint8_t *received, uint16_t *len) { while (BUTTON_PRESS() == false) { WDT_HIT(); - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - for (uint8_t mask = 0x80; mask != 0x00; mask >>= 1) { - if (Handle14443bReaderUartBit(b & mask)) { + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + for (uint8_t mask = 0x80; mask != 0x00; mask >>= 1) { + if (Handle14443bReaderUartBit(b & mask)) { *len = Uart.byteCnt; return true; } @@ -497,13 +497,13 @@ static void TransmitFor14443b_AsTag(uint8_t *response, uint16_t len) { void SimulateIso14443bTag(uint32_t pupi) { LED_A_ON(); - // the only commands we understand is WUPB, AFI=0, Select All, N=1: + // the only commands we understand is WUPB, AFI=0, Select All, N=1: // static const uint8_t cmdWUPB[] = { ISO14443B_REQB, 0x00, 0x08, 0x39, 0x73 }; // WUPB - // ... and REQB, AFI=0, Normal Request, N=1: + // ... and REQB, AFI=0, Normal Request, N=1: // static const uint8_t cmdREQB[] = { ISO14443B_REQB, 0x00, 0x00, 0x71, 0xFF }; // REQB - // ... and HLTB -// static const uint8_t cmdHLTB[] = { 0x50, 0xff, 0xff, 0xff, 0xff }; // HLTB - // ... and ATTRIB + // ... and HLTB +// static const uint8_t cmdHLTB[] = { 0x50, 0xff, 0xff, 0xff, 0xff }; // HLTB + // ... and ATTRIB // static const uint8_t cmdATTRIB[] = { ISO14443B_ATTRIB, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; // ATTRIB // ... if not PUPI/UID is supplied we always respond with ATQB, PUPI = 820de174, Application Data = 0x20381922, @@ -532,7 +532,7 @@ void SimulateIso14443bTag(uint32_t pupi) { // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - // Set up the synchronous serial port + // Set up the synchronous serial port FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); // allocate command receive buffer @@ -720,10 +720,7 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { } \ } -// Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by abs(ci) + abs(cq) -// Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by max(abs(ci),abs(cq)) + 1/2*min(abs(ci),abs(cq))) - -#define SUBCARRIER_DETECT_THRESHOLD 8 +#define SUBCARRIER_DETECT_THRESHOLD 8 //note: couldn't we just use MAX(ABS(ci),ABS(cq)) + (MIN(ABS(ci),ABS(cq))/2) from common.h - marshmellow #define CHECK_FOR_SUBCARRIER(void) { v = MAX(myI, myQ) + (MIN(myI, myQ) >> 1); } @@ -773,8 +770,8 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { } else { // maximum length of TR1 = 200 1/fs if (Demod.posCount > 200 / 4){ - Demod.state = DEMOD_UNSYNCD; - } + Demod.state = DEMOD_UNSYNCD; + } } Demod.posCount++; break; @@ -792,7 +789,7 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { LED_C_ON(); // Got SOF Demod.state = DEMOD_AWAITING_START_BIT; Demod.posCount = 0; - Demod.bitCount = 0; + Demod.bitCount = 0; Demod.len = 0; } } else { @@ -811,12 +808,12 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { if (v > 0) { if (Demod.posCount > 6 * 2) { // max 19us between characters = 16 1/fs, max 3 etu after low phase of SOF = 24 1/fs - LED_C_OFF(); - if (Demod.bitCount == 0 && Demod.len == 0) { // received SOF only, this is valid for iClass/Picopass - return true; - } else { - Demod.state = DEMOD_UNSYNCD; - } + LED_C_OFF(); + if (Demod.bitCount == 0 && Demod.len == 0) { // received SOF only, this is valid for iClass/Picopass + return true; + } else { + Demod.state = DEMOD_UNSYNCD; + } } } else { // start bit detected Demod.bitCount = 0; @@ -856,8 +853,8 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { // left shift to drop the startbit uint8_t b = (s >> 1); Demod.output[Demod.len] = b; - Demod.len++; - Demod.bitCount = 0; + Demod.len++; + Demod.bitCount = 0; Demod.state = DEMOD_AWAITING_START_BIT; } else { // this one is a bit hard, either its a correc byte or its unsynced. @@ -883,27 +880,26 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { /* * Demodulate the samples we received from the tag, also log to tracebuffer - * quiet: set to 'TRUE' to disable debug output */ static int GetTagSamplesFor14443bDemod(int timeout) { - int ret = 0; - int maxBehindBy = 0; - int lastRxCounter, samples = 0; - int8_t ci, cq; + int ret = 0; + int maxBehindBy = 0; + int lastRxCounter, samples = 0; + int8_t ci, cq; uint32_t time_0 = 0, time_stop = 0; BigBuf_free(); - // The response (tag -> reader) that we're receiving. - uint8_t *receivedResponse = BigBuf_malloc(MAX_FRAME_SIZE); + // The response (tag -> reader) that we're receiving. + uint8_t *receivedResponse = BigBuf_malloc(MAX_FRAME_SIZE); - // The DMA buffer, used to stream samples from the FPGA - uint16_t *dmaBuf = (uint16_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE * sizeof(uint16_t)); + // The DMA buffer, used to stream samples from the FPGA + uint16_t *dmaBuf = (uint16_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE * sizeof(uint16_t)); // Set up the demodulator for tag -> reader responses. Demod14bInit(receivedResponse); - // wait for last transfer to complete - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) // Setup and start DMA. FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); @@ -912,60 +908,60 @@ static int GetTagSamplesFor14443bDemod(int timeout) { return -1; } - uint16_t *upTo = dmaBuf; - lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; + uint16_t *upTo = dmaBuf; + lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; - // Signal field is ON with the appropriate LED: - LED_D_ON(); + // Signal field is ON with the appropriate LED: + LED_D_ON(); // And put the FPGA in the appropriate mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_848_KHZ | FPGA_HF_READER_MODE_RECEIVE_IQ); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_848_KHZ | FPGA_HF_READER_MODE_RECEIVE_IQ); - for(;;) { - int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1); - if (behindBy > maxBehindBy) { - maxBehindBy = behindBy; - } + for(;;) { + int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1); + if (behindBy > maxBehindBy) { + maxBehindBy = behindBy; + } - if (behindBy < 1) continue; + if (behindBy < 1) continue; - ci = *upTo >> 8; - cq = *upTo; - upTo++; - lastRxCounter--; - if (upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. - upTo = dmaBuf; // start reading the circular buffer from the beginning - lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; - } + ci = *upTo >> 8; + cq = *upTo; + upTo++; + lastRxCounter--; + if (upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning + lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; + } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; // DMA Next Counter registers - } - samples++; + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; // DMA Next Counter registers + } + samples++; - if (Handle14443bTagSamplesDemod(ci, cq)) { - ret = Demod.len; - break; - } + if (Handle14443bTagSamplesDemod(ci, cq)) { + ret = Demod.len; + break; + } - if(samples > timeout && Demod.state < DEMOD_PHASE_REF_TRAINING) { - ret = -1; - LED_C_OFF(); - break; - } + if(samples > timeout && Demod.state < DEMOD_PHASE_REF_TRAINING) { + ret = -1; + LED_C_OFF(); + break; + } } FpgaDisableSscDma(); - if (ret < 0) { - return ret; - } - - if (Demod.len > 0) { - LogTrace(Demod.output, Demod.len, time_0, time_stop, NULL, false); + if (ret < 0) { + return ret; } - return ret; + if (Demod.len > 0) { + LogTrace(Demod.output, Demod.len, time_0, time_stop, NULL, false); + } + + return ret; } //----------------------------------------------------------------------------- @@ -973,27 +969,27 @@ static int GetTagSamplesFor14443bDemod(int timeout) { //----------------------------------------------------------------------------- static void TransmitFor14443b_AsReader(void) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); - LED_B_ON(); + LED_B_ON(); tosend_t *ts = get_tosend(); - for (int c = 0; c < ts->max; c++) { - uint8_t data = ts->buf[c]; - for (int i = 0; i < 8; i++) { - uint16_t send_word = (data & 0x80) ? 0x0000 : 0xffff; - - while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; - - AT91C_BASE_SSC->SSC_THR = send_word; - while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; - AT91C_BASE_SSC->SSC_THR = send_word; + for (int c = 0; c < ts->max; c++) { + uint8_t data = ts->buf[c]; + for (int i = 0; i < 8; i++) { + uint16_t send_word = (data & 0x80) ? 0x0000 : 0xffff; + + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + + AT91C_BASE_SSC->SSC_THR = send_word; + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + AT91C_BASE_SSC->SSC_THR = send_word; - data <<= 1; - } + data <<= 1; + } WDT_HIT(); } - LED_B_OFF(); + LED_B_OFF(); } //----------------------------------------------------------------------------- @@ -1021,7 +1017,7 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) { // Send SOF // 10-11 ETUs of ZERO for (int i = 0; i < 10; i++) - tosend_stuffbit(0); + tosend_stuffbit(0); // 2-3 ETUs of ONE @@ -1057,16 +1053,16 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) { // Send EOF // 10-11 ETUs of ZERO for (int i = 0; i < 10; i++) - tosend_stuffbit(0); + tosend_stuffbit(0); // Transition time. TR0 - guard time // 8ETUS minum? // Per specification, Subcarrier must be stopped no later than 2 ETUs after EOF. // I'm guessing this is for the FPGA to be able to send all bits before we switch to listening mode - - // ensure that last byte is filled up + + // ensure that last byte is filled up for (int i = 0; i < 8 ; ++i) - tosend_stuffbit(1); + tosend_stuffbit(1); // TR1 - Synchronization time // Convert from last character reference to length @@ -1090,7 +1086,7 @@ static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len) { * TODO: check CRC and preamble */ uint8_t iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *response) { - LED_A_ON(); + LED_A_ON(); uint8_t message_frame[message_length + 4]; // PCB message_frame[0] = 0x0A | pcb_blocknum; @@ -1109,9 +1105,9 @@ uint8_t iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *r FpgaDisableTracing(); if (ret < 3) { - LED_A_OFF(); + LED_A_OFF(); return 0; - } + } // VALIDATE CRC if (!check_crc(CRC_14443_B, Demod.output, Demod.len)) { @@ -1123,7 +1119,7 @@ uint8_t iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *r memcpy(response, Demod.output, Demod.len); return Demod.len; - LED_A_OFF(); + LED_A_OFF(); } /** @@ -1280,7 +1276,7 @@ void iso14443b_setup(void) { SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Set up the synchronous serial port - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // Signal field is on with the appropriate LED FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); @@ -1439,15 +1435,15 @@ void RAMFUNC SniffIso14443b(void) { iso1444b_setup_sniff(); // The DMA buffer, used to stream samples from the FPGA - uint16_t *dmaBuf = (uint16_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE * sizeof(uint16_t)); - uint16_t *upTo = dmaBuf; - int lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; - int8_t ci, cq; - int maxBehindBy = 0; + uint16_t *dmaBuf = (uint16_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE * sizeof(uint16_t)); + uint16_t *upTo = dmaBuf; + int lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; + int8_t ci, cq; + int maxBehindBy = 0; - // Count of samples received so far, so that we can include timing - // information in the trace buffer. - int samples = 0; + // Count of samples received so far, so that we can include timing + // information in the trace buffer. + int samples = 0; // Setup and start DMA. if (!FpgaSetupSscDma((uint8_t *) dmaBuf, ISO14443B_DMA_BUFFER_SIZE)) { @@ -1462,38 +1458,38 @@ void RAMFUNC SniffIso14443b(void) { // loop and listen for(;;) { - int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE - 1); - if (behindBy > maxBehindBy) { - maxBehindBy = behindBy; - } + int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE - 1); + if (behindBy > maxBehindBy) { + maxBehindBy = behindBy; + } if (behindBy < 1) continue; - ci = *upTo >> 8; - cq = *upTo; - upTo++; - lastRxCounter--; + ci = *upTo >> 8; + cq = *upTo; + upTo++; + lastRxCounter--; if (upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. - upTo = dmaBuf; // start reading the circular buffer from the beginning again - lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; - if (behindBy > (9 * ISO14443B_DMA_BUFFER_SIZE / 10)) { - Dbprintf("About to blow circular buffer - aborted! behindBy=%d", behindBy); - break; - } - } + upTo = dmaBuf; // start reading the circular buffer from the beginning again + lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; + if (behindBy > (9 * ISO14443B_DMA_BUFFER_SIZE / 10)) { + Dbprintf("About to blow circular buffer - aborted! behindBy=%d", behindBy); + break; + } + } if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; // DMA Next Counter registers + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; // DMA Next Counter registers - WDT_HIT(); - if (BUTTON_PRESS()) { - DbpString("Button pressed, cancelled"); - break; - } - } + WDT_HIT(); + if (BUTTON_PRESS()) { + DbpString("Button pressed, cancelled"); + break; + } + } - samples++; + samples++; // no need to try decoding reader data if the tag is sending if (TagIsActive == false) { @@ -1538,7 +1534,7 @@ void RAMFUNC SniffIso14443b(void) { } } - FpgaDisableSscDma(); + FpgaDisableSscDma(); if (DBGLEVEL >= 2) { DbpString("[+] Sniff statistics:"); Dbprintf("[+] uart State: %x ByteCount: %i ByteCountMax: %i", Uart.state, Uart.byteCnt, Uart.byteCntMax); From a95f4b3745481ca38520dc2310a68550dd4b46a5 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 14 Jul 2020 15:46:32 +0200 Subject: [PATCH 071/139] chg: check buttonpress --- armsrc/iso15693.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index c12e2837b..ff59d260a 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -1066,16 +1066,16 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo samples++; } - if (gotFrame) { - break; - } - if (BUTTON_PRESS()) { DecodeReader.byteCount = -1; break; } WDT_HIT(); + + if (gotFrame) { + break; + } } FpgaDisableSscDma(); From 49192ca4c47665b0478f98cfa1d9d8d772465ec2 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 14 Jul 2020 15:47:47 +0200 Subject: [PATCH 072/139] add: new standalone mode. hf_iceclass --- armsrc/Standalone/Makefile.hal | 7 +- armsrc/Standalone/Makefile.inc | 6 +- armsrc/Standalone/hf_iceclass.c | 163 ++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 3 deletions(-) create mode 100644 armsrc/Standalone/hf_iceclass.c diff --git a/armsrc/Standalone/Makefile.hal b/armsrc/Standalone/Makefile.hal index c6dc9952e..bd66d538c 100644 --- a/armsrc/Standalone/Makefile.hal +++ b/armsrc/Standalone/Makefile.hal @@ -44,6 +44,9 @@ define KNOWN_STANDALONE_DEFINITIONS | HF_COLIN | Mifare ultra fast sniff/sim/clone | | (RDV4 only) | - Colin Brigato | +----------------------------------------------------------+ +| HF_ICECLASS | Simulate HID iCLASS legacy ags | +| (RDV4 only) | storing in flashmem | ++----------------------------------------------------------+ | HF_LEGIC | Read/simulate Legic Prime tags | | | storing in flashmem | +----------------------------------------------------------+ @@ -59,9 +62,9 @@ define KNOWN_STANDALONE_DEFINITIONS endef STANDALONE_MODES := LF_SKELETON LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RWC LF_HIDBRUTE LF_ICEHID LF_PROXBRUTE LF_SAMYRUN -STANDALONE_MODES += HF_14ASNIFF HF_BOG HF_COLIN HF_LEGIC HF_MATTYRUN HF_MSDSAL HF_YOUNG +STANDALONE_MODES += HF_14ASNIFF HF_BOG HF_COLIN HF_ICECLASS HF_LEGIC HF_MATTYRUN HF_MSDSAL HF_YOUNG STANDALONE_MODES_REQ_SMARTCARD := -STANDALONE_MODES_REQ_FLASH := LF_ICEHID HF_14ASNIFF HF_BOG HF_COLIN +STANDALONE_MODES_REQ_FLASH := LF_ICEHID HF_14ASNIFF HF_BOG HF_COLIN HF_ICECLASS ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES)),) STANDALONE_PLATFORM_DEFS += -DWITH_STANDALONE_$(STANDALONE) ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES_REQ_SMARTCARD)),) diff --git a/armsrc/Standalone/Makefile.inc b/armsrc/Standalone/Makefile.inc index a06cfe75e..131e26e79 100644 --- a/armsrc/Standalone/Makefile.inc +++ b/armsrc/Standalone/Makefile.inc @@ -57,7 +57,11 @@ endif ifneq (,$(findstring WITH_STANDALONE_HF_LEGIC,$(APP_CFLAGS))) SRC_STANDALONE = hf_legic.c endif -# WITH_STANDALONE_LF_MSDSAL +# WITH_STANDALONE_HF_MSDSAL ifneq (,$(findstring WITH_STANDALONE_HF_MSDSAL,$(APP_CFLAGS))) SRC_STANDALONE = hf_msdsal.c endif +# WITH_STANDALONE_HF_ICECLASS +ifneq (,$(findstring WITH_STANDALONE_HF_ICECLASS,$(APP_CFLAGS))) + SRC_STANDALONE = hf_iceclass.c +endif diff --git a/armsrc/Standalone/hf_iceclass.c b/armsrc/Standalone/hf_iceclass.c new file mode 100644 index 000000000..778a9808a --- /dev/null +++ b/armsrc/Standalone/hf_iceclass.c @@ -0,0 +1,163 @@ +//----------------------------------------------------------------------------- +// Christian Herrmann, 2020 +// +// 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 hf_iceclass by Iceman +//----------------------------------------------------------------------------- +#include "standalone.h" // standalone definitions +#include "proxmark3_arm.h" +#include "appmain.h" +#include "BigBuf.h" +#include "fpgaloader.h" +#include "util.h" +#include "dbprint.h" +#include "spiffs.h" +#include "iclass.h" +#include "optimized_cipher.h" + +#define NUM_CSNS 9 +#define MAC_RESPONSES_SIZE (16 * NUM_CSNS) +#define HF_ICLASS_FULLSIM_ORG_BIN "iceclass-orig.bin" +#define HF_ICLASS_FULLSIM_POST_BIN "iceclass-modified.bin" +#define HF_ICLASS_FULLSIM_POST_EML "iceclass-modified-lasttag.bin.eml" +#define HF_ICLASS_ATTACK_BIN "iceclass_mac_attack.bin" + +static uint8_t legacy_aa1_key[] = {0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78}; + +static uint8_t csns[8 * NUM_CSNS] = { + 0x01, 0x0A, 0x0F, 0xFF, 0xF7, 0xFF, 0x12, 0xE0, + 0x0C, 0x06, 0x0C, 0xFE, 0xF7, 0xFF, 0x12, 0xE0, + 0x10, 0x97, 0x83, 0x7B, 0xF7, 0xFF, 0x12, 0xE0, + 0x13, 0x97, 0x82, 0x7A, 0xF7, 0xFF, 0x12, 0xE0, + 0x07, 0x0E, 0x0D, 0xF9, 0xF7, 0xFF, 0x12, 0xE0, + 0x14, 0x96, 0x84, 0x76, 0xF7, 0xFF, 0x12, 0xE0, + 0x17, 0x96, 0x85, 0x71, 0xF7, 0xFF, 0x12, 0xE0, + 0xCE, 0xC5, 0x0F, 0x77, 0xF7, 0xFF, 0x12, 0xE0, + 0xD2, 0x5A, 0x82, 0xF8, 0xF7, 0xFF, 0x12, 0xE0 + //0x04, 0x08, 0x9F, 0x78, 0x6E, 0xFF, 0x12, 0xE0 +}; + +static void DownloadLogInstructions(uint8_t t) { + Dbprintf(""); + switch (t) { + case ICLASS_SIM_MODE_FULL: { + Dbprintf("The emulator memory was saved to flash. Try the following from flash and display it"); + Dbprintf("1. " _YELLOW_("mem spiffs dump o "HF_ICLASS_FULLSIM_POST_BIN" f "HF_ICLASS_FULLSIM_POST_BIN" e")); + Dbprintf("2. " _YELLOW_("exit proxmark3 client")); + Dbprintf("3. " _YELLOW_("cat "HF_ICLASS_FULLSIM_POST_EML)); + break; + } + case ICLASS_SIM_MODE_READER_ATTACK: { + Dbprintf("The emulator memory was saved to flash. Try the following from flash and display it"); + Dbprintf("1. " _YELLOW_("mem spiffs dump o "HF_ICLASS_FULLSIM_POST_BIN" f "HF_ICLASS_FULLSIM_POST_BIN" e")); + Dbprintf("2. " _YELLOW_("hf iclass loclass f "HF_ICLASS_ATTACK_BIN)); + break; + } + } +} + +void ModInfo(void) { + DbpString(" HF iCLASS mode - aka iceCLASS (iceman)"); +} + +void RunMod(void) { + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + BigBuf_Clear(); + + StandAloneMode(); + Dbprintf(_YELLOW_("HF iCLASS mode a.k.a iceCLASS started")); + + uint8_t simtype = ICLASS_SIM_MODE_FULL; + + for (;;) { + WDT_HIT(); + + // exit from RunMod, send a usbcommand. + if (data_available()) break; + + // Was our button held down or pressed? + int button_pressed = BUTTON_HELD(1000); + if (button_pressed != BUTTON_NO_CLICK) { + break; + } + + switch (simtype) { + case ICLASS_SIM_MODE_FULL: { + + Dbprintf("enter full simulation mode"); + + rdv40_spiffs_lazy_mount(); + // Look for a dump file in FLASH MEM. + if (exists_in_spiffs(HF_ICLASS_FULLSIM_ORG_BIN) == false) { + Dbprintf("error, '" _YELLOW_(HF_ICLASS_FULLSIM_ORG_BIN) "' file missing"); + Dbprintf("changing to reader attack mode instead"); + simtype = ICLASS_SIM_MODE_READER_ATTACK; + break; + } + + SpinOff(0); + uint8_t *emul = BigBuf_get_EM_addr(); + uint32_t fsize = size_in_spiffs(HF_ICLASS_FULLSIM_ORG_BIN); + int res = rdv40_spiffs_read_as_filetype(HF_ICLASS_FULLSIM_ORG_BIN, emul, fsize, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_lazy_unmount(); + Dbprintf("Found `" _YELLOW_(HF_ICLASS_FULLSIM_ORG_BIN) "` , loaded %u bytes to emulator memory", fsize); + + if ( memcmp(emul + (3 * 8), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8) == 0) { + // create diversified key if not in dump. + uint8_t ccnr[12] = {0}; + memcpy(ccnr, emul + (2 * 8), 8); + bool use_elite = false; + + iclass_calc_div_key(emul, legacy_aa1_key, emul + (3 * 8), use_elite); + + Dbhexdump(8, emul + (3 * 8), false); + } + + iclass_simulate(ICLASS_SIM_MODE_FULL, 0 , false, NULL, NULL, NULL); + + LED_B_ON(); + rdv40_spiffs_lazy_mount(); + res = rdv40_spiffs_write(HF_ICLASS_FULLSIM_POST_BIN, emul, fsize, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_lazy_unmount(); + LED_B_OFF(); + if (res != 0) { + Dbprintf("error writing '"HF_ICLASS_FULLSIM_POST_BIN"' to flash ( %d )", res); + } + DownloadLogInstructions(simtype); + simtype = 0; + break; + } + case ICLASS_SIM_MODE_READER_ATTACK: { + + Dbprintf("enter reader attack mode"); + uint16_t mac_response_len = 0; + uint8_t mac_responses[MAC_RESPONSES_SIZE] = {0}; + + iclass_simulate(ICLASS_SIM_MODE_READER_ATTACK, NUM_CSNS, false, csns, mac_responses, &mac_response_len); + + if (mac_response_len > 0) { + + LED_B_ON(); + rdv40_spiffs_lazy_mount(); + int res = rdv40_spiffs_write(HF_ICLASS_ATTACK_BIN, mac_responses, mac_response_len, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_lazy_unmount(); + LED_B_OFF(); + if (res != 0) { + Dbprintf("error writing '"HF_ICLASS_ATTACK_BIN"' to flash ( %d )", res); + } + } + DownloadLogInstructions(simtype); + simtype = 0; + break; + } + } // switch + } // for loop + + + LEDsoff(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); +} From 804e2661f3abf8e8d7a2e39c9bd9a9d9297f881c Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 14 Jul 2020 15:55:44 +0200 Subject: [PATCH 073/139] key file py script - @ikarus --- tools/pm3_key_file_diff.py | 80 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100755 tools/pm3_key_file_diff.py diff --git a/tools/pm3_key_file_diff.py b/tools/pm3_key_file_diff.py new file mode 100755 index 000000000..4069d6684 --- /dev/null +++ b/tools/pm3_key_file_diff.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +######################################################################## +# +# Copyright 2018 Gerhard Klostermeier +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +######################################################################## +# +# Usage: ./key-file-diff.py +# +######################################################################## +# +# Info: +# - Read two key files and show the keys that are in file B but not in A. +# - Keys must be 12 hex characters long and at the beginning of a line. +# +######################################################################## + + +import re + + +def main(args): + """ + Read two key files and show the keys that are in file B but not in A. + :param args: Path to two key files A and B (positional shell parameters). + :return: 0 if everything went fine. + """ + key_file_a = args[1] + key_file_b = args[2] + + with open(key_file_a, 'r') as f: + keys_a = parse_keys(f) + with open(key_file_b, 'r') as f: + keys_b = parse_keys(f) + + # Show all keys that are in B but not in A. + keys_diff = keys_b.difference(keys_a) + for key in keys_diff: + print(key) + + return 0 + + +def parse_keys(file): + """ + Parse keys from a file and return them as a set. + Keys must be 12 hex characters long and at the beginning of a line. + :param file: Path to a file containing keys. + :return: A set of keys read from the file. + """ + keys = set() + key_regex = re.compile('^[0-9a-fA-F]{12}') + for line in file: + key = key_regex.match(line) + try: + key = key.group(0).upper() + keys.add(key) + except AttributeError: + pass + return keys + + +if __name__ == '__main__': + import sys + sys.exit(main(sys.argv)) From 99e61f82c03149e905296998d082bfe9d7d19977 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 14 Jul 2020 15:57:21 +0200 Subject: [PATCH 074/139] rework --- armsrc/Standalone/lf_em4100rwc.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/armsrc/Standalone/lf_em4100rwc.c b/armsrc/Standalone/lf_em4100rwc.c index c54e54fa2..7b269e89b 100644 --- a/armsrc/Standalone/lf_em4100rwc.c +++ b/armsrc/Standalone/lf_em4100rwc.c @@ -34,7 +34,7 @@ #endif #define MAX_IND 16 // 4 LEDs - 2^4 combinations -#define CLOCK 64 //for 125kHz +#define LF_CLOCK 64 // for 125kHz // low & high - array for storage IDs. Its length must be equal. // Predefined IDs must be stored in low[]. @@ -57,34 +57,35 @@ static uint64_t rev_quads(uint64_t bits) { } static void fillbuff(uint8_t bit) { - memset(bba + buflen, bit, CLOCK / 2); - buflen += (CLOCK / 2); - memset(bba + buflen, bit ^ 1, CLOCK / 2); - buflen += (CLOCK / 2); + memset(bba + buflen, bit, LF_CLOCK / 2); + buflen += (LF_CLOCK / 2); + memset(bba + buflen, bit ^ 1, LF_CLOCK / 2); + buflen += (LF_CLOCK / 2); } static void construct_EM410x_emul(uint64_t id) { - + + int i, j; int binary[4] = {0}; int parity[4] = {0}; buflen = 0; - for (uint8_t i = 0; i < 9; i++) + for (i = 0; i < 9; i++) fillbuff(1); - for (uint8_t i = 0; i < 10; i++) { - for (uint8_t j = 3; j > 0; j--, id /= 2) + for (i = 0; i < 10; i++) { + for (j = 3; j >= 0; j--, id /= 2) binary[j] = id % 2; - for (uint8_t j = 0; j < 4; j++) + for (j = 0; j < 4; j++) fillbuff(binary[j]); fillbuff(binary[0] ^ binary[1] ^ binary[2] ^ binary[3]); - for (uint8_t j = 0; j < 4; j++) + for (j = 0; j < 4; j++) parity[j] ^= binary[j]; } - for (uint8_t j = 0; j < 4; j++) + for (j = 0; j < 4; j++) fillbuff(parity[j]); fillbuff(0); @@ -207,7 +208,7 @@ void RunMod(void) { state = 0; } else if (button_pressed == BUTTON_SINGLE_CLICK) { // Click - write ID to tag - copy_em410x_to_t55xx(0, CLOCK, (uint32_t)(low[selected] >> 32), (uint32_t)(low[selected] & 0xffffffff)); + copy_em410x_to_t55xx(0, LF_CLOCK, (uint32_t)(low[selected] >> 32), (uint32_t)(low[selected] & 0xffffffff)); led_slot(selected); state = 0; // Switch to select mode } From 091f9fb94cbb0a6255a53c404288f5ec84f4f8a7 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 14 Jul 2020 23:11:44 +0200 Subject: [PATCH 075/139] chg: adapt the eml block size based on filename --- client/src/cmdflashmemspiffs.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/client/src/cmdflashmemspiffs.c b/client/src/cmdflashmemspiffs.c index b994ea56c..d7f2b016c 100644 --- a/client/src/cmdflashmemspiffs.c +++ b/client/src/cmdflashmemspiffs.c @@ -322,7 +322,14 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) { if (filename[0] != '\0') { saveFile(filename, "", dump, len); if (eml) { - saveFileEML(filename, dump, len, 16); + uint8_t eml_len = 16; + + if (strstr(filename, "iclass") != NULL) + eml_len = 8; + else if (strstr(filename, "mfu") != NULL) + eml_len = 4; + + saveFileEML(filename, dump, len, eml_len); } } From 1a1ebcc330ca306461752d3304c367b94f1513d1 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 14 Jul 2020 23:12:28 +0200 Subject: [PATCH 076/139] adapt for external auth calls --- armsrc/iclass.c | 22 ++++++++++++++++++---- armsrc/iclass.h | 13 ++++++++----- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 078da13e5..f232c35ed 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1153,6 +1153,10 @@ void iClass_ReadCheck(uint8_t blockno, uint8_t keytype) { // which needs to authenticate before doing more things like read/write // selects and authenticate to a card, sends back div_key and mac to client. void iClass_Authentication(uint8_t *bytes) { + iclass_auth(bytes, true, NULL); +} + +bool iclass_auth(uint8_t *bytes, bool send_reply, uint8_t *dataout) { struct p { uint8_t key[8]; @@ -1176,8 +1180,10 @@ void iClass_Authentication(uint8_t *bytes) { packet.isOK = select_iclass_tag(card_data, payload->use_credit_key, &eof_time); if (packet.isOK == false) { - reply_ng(CMD_HF_ICLASS_AUTH, PM3_ESOFT, (uint8_t *)&packet, sizeof(packet)); - return; + if (send_reply) + reply_ng(CMD_HF_ICLASS_AUTH, PM3_ESOFT, (uint8_t *)&packet, sizeof(packet)); + + return false; } uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; @@ -1201,7 +1207,15 @@ void iClass_Authentication(uint8_t *bytes) { uint8_t resp[ICLASS_BUFFER_SIZE]; packet.isOK = iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); - reply_ng(CMD_HF_ICLASS_AUTH, (packet.isOK)? PM3_SUCCESS : PM3_ESOFT, (uint8_t *)&packet, sizeof(packet)); + if (send_reply) + reply_ng(CMD_HF_ICLASS_AUTH, (packet.isOK)? PM3_SUCCESS : PM3_ESOFT, (uint8_t *)&packet, sizeof(packet)); + + if (dataout) { + memcpy(dataout, card_data, sizeof(card_data)); + memcpy(dataout + (3 * 8), packet.div_key, sizeof(packet.div_key)); + } + + return true; } typedef struct iclass_premac { @@ -1300,7 +1314,7 @@ out: // Tries to read block. // retries 10times. -static bool iclass_readblock(uint8_t blockno, uint8_t *data) { +bool iclass_readblock(uint8_t blockno, uint8_t *data) { uint8_t resp[10]; uint8_t c[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00}; AddCrc(c + 1, 1); diff --git a/armsrc/iclass.h b/armsrc/iclass.h index 3b4de40f0..fa256c925 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -16,17 +16,20 @@ void SniffIClass(uint8_t jam_search_len, uint8_t *jam_search_string); void ReaderIClass(uint8_t arg0); void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac); -void iClass_Authentication(uint8_t *mac); -void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain); + void iClass_WriteBlock(uint8_t blockno, uint8_t *data); -void iClass_ReadBlk(uint8_t blockno); void iClass_Dump(uint8_t blockno, uint8_t numblks); void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data); void iClass_ReadCheck(uint8_t blockno, uint8_t keytype); - int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf); - void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_t *datain, uint8_t *dataout, uint16_t *dataoutlen); + +void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain); +void iClass_Authentication(uint8_t *bytes); +bool iclass_auth(uint8_t *bytes, bool send_reply, uint8_t *dataout); + +void iClass_ReadBlk(uint8_t blockno); +bool iclass_readblock(uint8_t blockno, uint8_t *data); #endif From 5039d69c6bc5c412ba9cc22cae2ebc68d18b52bd Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 14 Jul 2020 23:12:54 +0200 Subject: [PATCH 077/139] rename fct --- client/src/cmdhficlass.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 4f51042e9..2aeb9a03f 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -1057,7 +1057,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { return PM3_SUCCESS; } -static void iClassEncryptBlkData(uint8_t *blk_data, uint8_t *key) { +static void iclass_encrypt_block_data(uint8_t *blk_data, uint8_t *key) { uint8_t encrypted_data[16]; uint8_t *encrypted = encrypted_data; mbedtls_des3_context ctx; @@ -1118,7 +1118,7 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) { if (use_sc) { Encrypt(blk_data, blk_data); } else { - iClassEncryptBlkData(blk_data, key); + iclass_encrypt_block_data(blk_data, key); } PrintAndLogEx(SUCCESS, "encrypted block %s", sprint_hex(blk_data, 8)); return PM3_SUCCESS; From c99ed14c6e2486d7e36de3e86c7a17d7c5050338 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 15 Jul 2020 01:29:44 +0200 Subject: [PATCH 078/139] textual --- client/src/cmdflashmemspiffs.c | 57 ++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/client/src/cmdflashmemspiffs.c b/client/src/cmdflashmemspiffs.c index d7f2b016c..887784428 100644 --- a/client/src/cmdflashmemspiffs.c +++ b/client/src/cmdflashmemspiffs.c @@ -19,49 +19,58 @@ static int CmdHelp(const char *Cmd); static int usage_flashmemspiffs_remove(void) { - PrintAndLogEx(NORMAL, "Remove a file from spiffs filesystem"); - PrintAndLogEx(NORMAL, " Usage: mem spiffs remove "); + PrintAndLogEx(NORMAL, "Remove a file from spiffs filesystem\n"); + PrintAndLogEx(NORMAL, "Usage: mem spiffs remove "); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs remove lasttag.bin")); return PM3_SUCCESS; } static int usage_flashmemspiffs_rename(void) { - PrintAndLogEx(NORMAL, "Rename/move a file in spiffs filesystem"); - PrintAndLogEx(NORMAL, " Usage: mem spiffs rename "); + PrintAndLogEx(NORMAL, "Rename/move a file in spiffs filesystem\n"); + PrintAndLogEx(NORMAL, "Usage: mem spiffs rename "); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs rename lasttag.bin oldtag.bin")); return PM3_SUCCESS; } static int usage_flashmemspiffs_copy(void) { - PrintAndLogEx(NORMAL, "Copy a file to another (destructively) in spiffs filesystem"); - PrintAndLogEx(NORMAL, " Usage: mem spiffs copy "); + PrintAndLogEx(NORMAL, "Copy a file to another (destructively) in spiffs filesystem\n"); + PrintAndLogEx(NORMAL, "Usage: mem spiffs copy "); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs copy lasttag.bin lasttag_cpy.bin")); return PM3_SUCCESS; } static int usage_flashmemspiffs_dump(void) { PrintAndLogEx(NORMAL, "Dumps flash memory on device into a file or in console"); - PrintAndLogEx(NORMAL, "Size is handled by first sending a STAT command against file existence"); - PrintAndLogEx(NORMAL, " Usage: mem spiffs dump o [f [e]] [p]"); - PrintAndLogEx(NORMAL, " o : filename in SPIFFS"); - PrintAndLogEx(NORMAL, " f : file name to save to"); - PrintAndLogEx(NORMAL, " p : print dump in console"); - PrintAndLogEx(NORMAL, " e : also save in EML format (good for tags save and dictonnary files)"); + PrintAndLogEx(NORMAL, "Size is handled by first sending a STAT command against file existence\n"); + PrintAndLogEx(NORMAL, "Usage: mem spiffs dump o [f [e]] [p]"); + PrintAndLogEx(NORMAL, " o - filename in SPIFFS"); + PrintAndLogEx(NORMAL, " f - file name to save to "); + PrintAndLogEx(NORMAL, " p - print dump in console"); + PrintAndLogEx(NORMAL, " e - also save in EML format (good for tags save and dictonnary files)"); PrintAndLogEx(NORMAL, " You must specify at lease option f or option p, both if you wish"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " mem spiffs dump o hf_colin/lasttag f lasttag e"); - PrintAndLogEx(NORMAL, " mem spiffs dump o hf_colin/lasttag p"); + PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs dump o lasttag.bin f lasttag e")); + PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs dump o lasttag.bin p")); return PM3_SUCCESS; } static int usage_flashmemspiffs_load(void) { PrintAndLogEx(NORMAL, "Uploads binary-wise file into device filesystem"); - PrintAndLogEx(NORMAL, "Usage: mem spiffs load o f "); PrintAndLogEx(NORMAL, "Warning: mem area to be written must have been wiped first"); - PrintAndLogEx(NORMAL, "(this is already taken care when loading dictionaries)"); - PrintAndLogEx(NORMAL, " o : destination filename"); - PrintAndLogEx(NORMAL, " f : local filename"); + PrintAndLogEx(NORMAL, "(this is already taken care when loading dictionaries)\n"); + PrintAndLogEx(NORMAL, "Usage: mem spiffs load o f "); + PrintAndLogEx(NORMAL, " o - destination filename"); + PrintAndLogEx(NORMAL, " f - local filename"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " mem spiffs load f myfile o myapp.conf"); + PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs load f myfile o myapp.conf")); return PM3_SUCCESS; } @@ -277,12 +286,12 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) { } if ((filename[0] == '\0') && (!print)) { - PrintAndLogEx(FAILED, "No print asked and Local dump Filename missing or invalid"); + PrintAndLogEx(FAILED, "No print asked and local dump filename missing or invalid"); errors = true; } if (destfilename[0] == '\0') { - PrintAndLogEx(FAILED, "SPIFFS Filename missing or invalid"); + PrintAndLogEx(FAILED, "SPIFFS filename missing or invalid"); errors = true; } @@ -320,15 +329,15 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) { } if (filename[0] != '\0') { - saveFile(filename, "", dump, len); + saveFile(filename, ".bin", dump, len); if (eml) { uint8_t eml_len = 16; - if (strstr(filename, "iclass") != NULL) + if (strstr(filename, "class") != NULL) eml_len = 8; else if (strstr(filename, "mfu") != NULL) eml_len = 4; - + saveFileEML(filename, dump, len, eml_len); } } From ce0a2744cb719b0fe5515377c67c4ce3e86514cd Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 15 Jul 2020 01:31:13 +0200 Subject: [PATCH 079/139] hf_iceclass --- armsrc/Standalone/hf_iceclass.c | 323 ++++++++++++++++++++++++-------- 1 file changed, 248 insertions(+), 75 deletions(-) diff --git a/armsrc/Standalone/hf_iceclass.c b/armsrc/Standalone/hf_iceclass.c index 778a9808a..2d5bbb22f 100644 --- a/armsrc/Standalone/hf_iceclass.c +++ b/armsrc/Standalone/hf_iceclass.c @@ -20,11 +20,51 @@ #define NUM_CSNS 9 #define MAC_RESPONSES_SIZE (16 * NUM_CSNS) -#define HF_ICLASS_FULLSIM_ORG_BIN "iceclass-orig.bin" -#define HF_ICLASS_FULLSIM_POST_BIN "iceclass-modified.bin" -#define HF_ICLASS_FULLSIM_POST_EML "iceclass-modified-lasttag.bin.eml" -#define HF_ICLASS_ATTACK_BIN "iceclass_mac_attack.bin" +#define HF_ICLASS_FULLSIM_ORIG_BIN "iceclass-orig.bin" +#define HF_ICLASS_FULLSIM_MOD "iceclass-modified" +#define HF_ICLASS_FULLSIM_MOD_BIN HF_ICLASS_FULLSIM_MOD".bin" +#define HF_ICLASS_FULLSIM_MOD_EML HF_ICLASS_FULLSIM_MOD".eml" +#define HF_ICLASS_ATTACK_BIN "iclass_mac_attack.bin" +#define ICE_STATE_NONE 0 +#define ICE_STATE_FULLSIM 1 +#define ICE_STATE_ATTACK 2 +#define ICE_STATE_READER 3 + +typedef struct { + uint8_t app_limit; //[8] + uint8_t otp[2]; //[9-10] + uint8_t block_writelock;//[11] + uint8_t chip_config; //[12] + uint8_t mem_config; //[13] + uint8_t eas; //[14] + uint8_t fuses; //[15] +} picopass_conf_block_t; + +// iclass card descriptors +char * card_types[] = { + "PicoPass 16K / 16", // 000 + "PicoPass 32K with current book 16K / 16", // 001 + "Unknown Card Type!", // 010 + "Unknown Card Type!", // 011 + "PicoPass 2K", // 100 + "Unknown Card Type!", // 101 + "PicoPass 16K / 2", // 110 + "PicoPass 32K with current book 16K / 2", // 111 +}; + +uint8_t card_app2_limit[] = { + 0xff, + 0xff, + 0xff, + 0xff, + 0x1f, + 0xff, + 0xff, + 0xff, +}; + +static uint8_t aa2_key[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; static uint8_t legacy_aa1_key[] = {0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78}; static uint8_t csns[8 * NUM_CSNS] = { @@ -40,25 +80,198 @@ static uint8_t csns[8 * NUM_CSNS] = { //0x04, 0x08, 0x9F, 0x78, 0x6E, 0xFF, 0x12, 0xE0 }; -static void DownloadLogInstructions(uint8_t t) { +static void download_instructions(uint8_t t) { Dbprintf(""); switch (t) { - case ICLASS_SIM_MODE_FULL: { + case ICE_STATE_FULLSIM: { Dbprintf("The emulator memory was saved to flash. Try the following from flash and display it"); - Dbprintf("1. " _YELLOW_("mem spiffs dump o "HF_ICLASS_FULLSIM_POST_BIN" f "HF_ICLASS_FULLSIM_POST_BIN" e")); + Dbprintf("1. " _YELLOW_("mem spiffs dump o "HF_ICLASS_FULLSIM_MOD_BIN" f "HF_ICLASS_FULLSIM_MOD" e")); Dbprintf("2. " _YELLOW_("exit proxmark3 client")); - Dbprintf("3. " _YELLOW_("cat "HF_ICLASS_FULLSIM_POST_EML)); + Dbprintf("3. " _YELLOW_("cat "HF_ICLASS_FULLSIM_MOD_EML)); break; } - case ICLASS_SIM_MODE_READER_ATTACK: { + case ICE_STATE_ATTACK: { Dbprintf("The emulator memory was saved to flash. Try the following from flash and display it"); - Dbprintf("1. " _YELLOW_("mem spiffs dump o "HF_ICLASS_FULLSIM_POST_BIN" f "HF_ICLASS_FULLSIM_POST_BIN" e")); + Dbprintf("1. " _YELLOW_("mem spiffs dump o "HF_ICLASS_ATTACK_BIN" f "HF_ICLASS_ATTACK_BIN)); Dbprintf("2. " _YELLOW_("hf iclass loclass f "HF_ICLASS_ATTACK_BIN)); break; } + case ICE_STATE_READER: { + Dbprintf("The found tags was saved to flash. Try to download from flash and display it"); + Dbprintf("1. " _YELLOW_("mem spiffs tree")); + Dbprintf("2. " _YELLOW_("mem spiffs dump h")); + break; + } } } +static void save_to_flash(uint8_t *data, uint16_t datalen) { + + rdv40_spiffs_lazy_mount(); + + char fn[SPIFFS_OBJ_NAME_LEN]; + sprintf(fn, "iclass-%02X%02X%02X%02X%02X%02X%02X%02X.bin", + data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7] + ); + + if (exists_in_spiffs(fn) == false) { + int res = rdv40_spiffs_write(fn, data, datalen, RDV40_SPIFFS_SAFETY_SAFE); + if (res == SPIFFS_OK) { + Dbprintf("Saved to `" _YELLOW_("%s") "`", fn); + } else { + Dbprintf("error writing `" _YELLOW_("%s") "`", fn); + } + } + + rdv40_spiffs_lazy_unmount(); +} + +static int fullsim_mode(void) { + + rdv40_spiffs_lazy_mount(); + + // Look for a dump file in FLASH MEM. + if (exists_in_spiffs(HF_ICLASS_FULLSIM_ORIG_BIN) == false) { + Dbprintf("error, '" _YELLOW_(HF_ICLASS_FULLSIM_ORIG_BIN) "' file missing"); + return PM3_EIO; + } + + SpinOff(0); + uint8_t *emul = BigBuf_get_EM_addr(); + uint32_t fsize = size_in_spiffs(HF_ICLASS_FULLSIM_ORIG_BIN); + int res = rdv40_spiffs_read_as_filetype(HF_ICLASS_FULLSIM_ORIG_BIN, emul, fsize, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_lazy_unmount(); + Dbprintf("Found `" _YELLOW_(HF_ICLASS_FULLSIM_ORIG_BIN) "` , loaded %u bytes to emulator memory", fsize); + + if ( memcmp(emul + (3 * 8), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8) == 0) { + // create diversified key if not in dump. + uint8_t ccnr[12] = {0}; + memcpy(ccnr, emul + (2 * 8), 8); + bool use_elite = false; + + iclass_calc_div_key(emul, legacy_aa1_key, emul + (3 * 8), use_elite); +// Dbhexdump(8, emul + (3 * 8), false); + } + + iclass_simulate(ICLASS_SIM_MODE_FULL, 0 , false, NULL, NULL, NULL); + + LED_B_ON(); + rdv40_spiffs_lazy_mount(); + res = rdv40_spiffs_write(HF_ICLASS_FULLSIM_MOD_BIN, emul, fsize, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_lazy_unmount(); + LED_B_OFF(); + if (res != 0) { + Dbprintf("error writing '"HF_ICLASS_FULLSIM_MOD_BIN"' to flash ( %d )", res); + } + + return PM3_SUCCESS; +} + +static int reader_attack_mode(void) { + + BigBuf_free(); + uint16_t mac_response_len = 0; + uint8_t *mac_responses = BigBuf_malloc(MAC_RESPONSES_SIZE); + + iclass_simulate(ICLASS_SIM_MODE_READER_ATTACK, NUM_CSNS, false, csns, mac_responses, &mac_response_len); + + if (mac_response_len > 0) { + + LED_B_ON(); + rdv40_spiffs_lazy_mount(); + int res = rdv40_spiffs_write(HF_ICLASS_ATTACK_BIN, mac_responses, mac_response_len, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_lazy_unmount(); + LED_B_OFF(); + if (res != 0) { + Dbprintf("error writing '"HF_ICLASS_ATTACK_BIN"' to flash ( %d )", res); + } + } + return PM3_SUCCESS; +} + +static int reader_dump_mode(void) { + + BigBuf_free(); + uint8_t *card_data = BigBuf_malloc(0xFF * 8); + memset(card_data, 0xFF, sizeof(card_data)); + + struct p { + uint8_t key[8]; + bool use_raw; + bool use_elite; + bool use_credit_key; + } PACKED; + + for (;;) { + + if (BUTTON_PRESS()) { + DbpString("button pressed"); + break; + } + + // AA1 + struct p payload = { + .use_raw = false, + .use_elite = false, + .use_credit_key = false, + }; + memcpy(payload.key, legacy_aa1_key, sizeof(payload.key)); + + bool isOK = iclass_auth((uint8_t*)&payload, false, card_data); + if (isOK == false) { + continue; + } + + picopass_conf_block_t *conf = (picopass_conf_block_t*)(card_data + 8); + + // get 3 config bits + uint8_t type = (conf->chip_config & 0x10) >> 2; + type |= (conf->mem_config & 0x80) >> 6; + type |= (conf->mem_config & 0x20) >> 5; + + uint8_t app1_limit = conf->app_limit - 5; // minus header blocks + uint8_t app2_limit = card_app2_limit[type]; + + + uint16_t dumped = 0; + uint8_t block; + for (block = 5; block < app1_limit; block++) { + isOK = iclass_readblock(block, card_data + (8 * block)); + if (isOK) { + dumped++; + } + } + + // AA2 + payload.use_credit_key = true; + memcpy(payload.key, aa2_key, sizeof(payload.key)); + + isOK = iclass_auth((uint8_t*)&payload, false, card_data); + if (isOK) { + for (; block < app2_limit; block++) { + isOK = iclass_readblock(block, card_data + (8 * block)); + if (isOK) { + dumped++; + } + } + } + + Dbprintf("Found %s", card_types[type]); +/* + Dbprintf("APP1 Blocks: %d", app1_limit); + Dbprintf("APP2 Blocks: %d", app2_limit - app1_limit - 5); // minus app1 and header + Dbprintf("Got %d blocks (saving %u, %u bytes )", dumped, dumped + 5, ((dumped+5)*8) ); +*/ + if (5 + dumped > app1_limit) { + save_to_flash(card_data, (5 + dumped) * 8 ); + } + } + + Dbprintf("exit read & dump mode"); + return PM3_SUCCESS; +} + void ModInfo(void) { DbpString(" HF iCLASS mode - aka iceCLASS (iceman)"); } @@ -71,12 +284,10 @@ void RunMod(void) { StandAloneMode(); Dbprintf(_YELLOW_("HF iCLASS mode a.k.a iceCLASS started")); - uint8_t simtype = ICLASS_SIM_MODE_FULL; + uint8_t mode = ICE_STATE_READER; for (;;) { WDT_HIT(); - - // exit from RunMod, send a usbcommand. if (data_available()) break; // Was our button held down or pressed? @@ -85,78 +296,40 @@ void RunMod(void) { break; } - switch (simtype) { - case ICLASS_SIM_MODE_FULL: { + int res; + switch (mode) { + case ICE_STATE_FULLSIM: { Dbprintf("enter full simulation mode"); + res = fullsim_mode(); + if (res == PM3_SUCCESS) + download_instructions(mode); - rdv40_spiffs_lazy_mount(); - // Look for a dump file in FLASH MEM. - if (exists_in_spiffs(HF_ICLASS_FULLSIM_ORG_BIN) == false) { - Dbprintf("error, '" _YELLOW_(HF_ICLASS_FULLSIM_ORG_BIN) "' file missing"); - Dbprintf("changing to reader attack mode instead"); - simtype = ICLASS_SIM_MODE_READER_ATTACK; - break; - } - - SpinOff(0); - uint8_t *emul = BigBuf_get_EM_addr(); - uint32_t fsize = size_in_spiffs(HF_ICLASS_FULLSIM_ORG_BIN); - int res = rdv40_spiffs_read_as_filetype(HF_ICLASS_FULLSIM_ORG_BIN, emul, fsize, RDV40_SPIFFS_SAFETY_SAFE); - rdv40_spiffs_lazy_unmount(); - Dbprintf("Found `" _YELLOW_(HF_ICLASS_FULLSIM_ORG_BIN) "` , loaded %u bytes to emulator memory", fsize); - - if ( memcmp(emul + (3 * 8), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8) == 0) { - // create diversified key if not in dump. - uint8_t ccnr[12] = {0}; - memcpy(ccnr, emul + (2 * 8), 8); - bool use_elite = false; - - iclass_calc_div_key(emul, legacy_aa1_key, emul + (3 * 8), use_elite); - - Dbhexdump(8, emul + (3 * 8), false); - } - - iclass_simulate(ICLASS_SIM_MODE_FULL, 0 , false, NULL, NULL, NULL); - - LED_B_ON(); - rdv40_spiffs_lazy_mount(); - res = rdv40_spiffs_write(HF_ICLASS_FULLSIM_POST_BIN, emul, fsize, RDV40_SPIFFS_SAFETY_SAFE); - rdv40_spiffs_lazy_unmount(); - LED_B_OFF(); - if (res != 0) { - Dbprintf("error writing '"HF_ICLASS_FULLSIM_POST_BIN"' to flash ( %d )", res); - } - DownloadLogInstructions(simtype); - simtype = 0; + switch_off(); break; } - case ICLASS_SIM_MODE_READER_ATTACK: { - + case ICE_STATE_ATTACK: { Dbprintf("enter reader attack mode"); - uint16_t mac_response_len = 0; - uint8_t mac_responses[MAC_RESPONSES_SIZE] = {0}; + res = reader_attack_mode(); + if (res == PM3_SUCCESS) + download_instructions(mode); - iclass_simulate(ICLASS_SIM_MODE_READER_ATTACK, NUM_CSNS, false, csns, mac_responses, &mac_response_len); - - if (mac_response_len > 0) { - - LED_B_ON(); - rdv40_spiffs_lazy_mount(); - int res = rdv40_spiffs_write(HF_ICLASS_ATTACK_BIN, mac_responses, mac_response_len, RDV40_SPIFFS_SAFETY_SAFE); - rdv40_spiffs_lazy_unmount(); - LED_B_OFF(); - if (res != 0) { - Dbprintf("error writing '"HF_ICLASS_ATTACK_BIN"' to flash ( %d )", res); - } - } - DownloadLogInstructions(simtype); - simtype = 0; + mode = ICE_STATE_NONE; + switch_off(); break; } - } // switch - } // for loop + case ICE_STATE_READER: { + Dbprintf("enter read & dump mode"); + res = reader_dump_mode(); + if (res == PM3_SUCCESS) + download_instructions(mode); + mode = ICE_STATE_NONE; + switch_off(); + break; + } + } + } LEDsoff(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); From 8d5315b6afeeb20141ace10072d6e87c358aa33a Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 15 Jul 2020 11:34:14 +0200 Subject: [PATCH 080/139] fixes to mode --- armsrc/Standalone/hf_iceclass.c | 132 +++++++++++++++++++++++++------- include/pm3_cmd.h | 1 + 2 files changed, 107 insertions(+), 26 deletions(-) diff --git a/armsrc/Standalone/hf_iceclass.c b/armsrc/Standalone/hf_iceclass.c index 2d5bbb22f..19aa9626d 100644 --- a/armsrc/Standalone/hf_iceclass.c +++ b/armsrc/Standalone/hf_iceclass.c @@ -26,10 +26,15 @@ #define HF_ICLASS_FULLSIM_MOD_EML HF_ICLASS_FULLSIM_MOD".eml" #define HF_ICLASS_ATTACK_BIN "iclass_mac_attack.bin" -#define ICE_STATE_NONE 0 -#define ICE_STATE_FULLSIM 1 -#define ICE_STATE_ATTACK 2 -#define ICE_STATE_READER 3 +#define HF_ICLASS_CC_A "iceclass_cc_a.bin" +#define HF_ICLASS_CC_B "iceclass_cc_b.bin" +char* cc_files[] = { HF_ICLASS_CC_A, HF_ICLASS_CC_B }; + +#define ICE_STATE_NONE 0 +#define ICE_STATE_FULLSIM 1 +#define ICE_STATE_ATTACK 2 +#define ICE_STATE_READER 3 +#define ICE_STATE_CONFIGCARD 4 typedef struct { uint8_t app_limit; //[8] @@ -130,28 +135,23 @@ static void save_to_flash(uint8_t *data, uint16_t datalen) { static int fullsim_mode(void) { rdv40_spiffs_lazy_mount(); - - // Look for a dump file in FLASH MEM. - if (exists_in_spiffs(HF_ICLASS_FULLSIM_ORIG_BIN) == false) { - Dbprintf("error, '" _YELLOW_(HF_ICLASS_FULLSIM_ORIG_BIN) "' file missing"); - return PM3_EIO; - } SpinOff(0); uint8_t *emul = BigBuf_get_EM_addr(); uint32_t fsize = size_in_spiffs(HF_ICLASS_FULLSIM_ORIG_BIN); int res = rdv40_spiffs_read_as_filetype(HF_ICLASS_FULLSIM_ORIG_BIN, emul, fsize, RDV40_SPIFFS_SAFETY_SAFE); rdv40_spiffs_lazy_unmount(); - Dbprintf("Found `" _YELLOW_(HF_ICLASS_FULLSIM_ORIG_BIN) "` , loaded %u bytes to emulator memory", fsize); + if (res == SPIFFS_OK) { + Dbprintf("loaded '" _YELLOW_(HF_ICLASS_FULLSIM_ORIG_BIN) "' (%u bytes) to emulator memory", fsize); + } + // create diversified key if not in dump. if ( memcmp(emul + (3 * 8), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8) == 0) { - // create diversified key if not in dump. uint8_t ccnr[12] = {0}; memcpy(ccnr, emul + (2 * 8), 8); bool use_elite = false; - + iclass_calc_div_key(emul, legacy_aa1_key, emul + (3 * 8), use_elite); -// Dbhexdump(8, emul + (3 * 8), false); } iclass_simulate(ICLASS_SIM_MODE_FULL, 0 , false, NULL, NULL, NULL); @@ -161,7 +161,7 @@ static int fullsim_mode(void) { res = rdv40_spiffs_write(HF_ICLASS_FULLSIM_MOD_BIN, emul, fsize, RDV40_SPIFFS_SAFETY_SAFE); rdv40_spiffs_lazy_unmount(); LED_B_OFF(); - if (res != 0) { + if (res != SPIFFS_OK) { Dbprintf("error writing '"HF_ICLASS_FULLSIM_MOD_BIN"' to flash ( %d )", res); } @@ -177,13 +177,36 @@ static int reader_attack_mode(void) { iclass_simulate(ICLASS_SIM_MODE_READER_ATTACK, NUM_CSNS, false, csns, mac_responses, &mac_response_len); if (mac_response_len > 0) { + + bool success = (mac_response_len == MAC_RESPONSES_SIZE); + uint8_t num_mac = (mac_response_len >> 4); + Dbprintf("%u out of %d MAC obtained [%s]", num_mac, NUM_CSNS, (success) ? _GREEN_("OK") : _RED_("FAIL")); + + size_t dumplen = NUM_CSNS * 24; + + uint8_t *dump = BigBuf_malloc(dumplen); + if (dump == false) { + Dbprintf("failed to allocate memory"); + return PM3_EMALLOC; + } + + memset(dump, 0, dumplen);//<-- Need zeroes for the EPURSE - field + + for (uint8_t i = 0 ; i < NUM_CSNS ; i++) { + //copy CSN + memcpy(dump + (i * 24), csns + (i * 8), 8); + //copy epurse + memcpy(dump + (i * 24) + 8, mac_responses + (i * 16), 8); + // NR_MAC (eight bytes from the response) ( 8b csn + 8b epurse == 16) + memcpy(dump + (i * 24) + 16, mac_responses + (i * 16) + 8, 8); + } LED_B_ON(); rdv40_spiffs_lazy_mount(); - int res = rdv40_spiffs_write(HF_ICLASS_ATTACK_BIN, mac_responses, mac_response_len, RDV40_SPIFFS_SAFETY_SAFE); + int res = rdv40_spiffs_write(HF_ICLASS_ATTACK_BIN, dump, dumplen, RDV40_SPIFFS_SAFETY_SAFE); rdv40_spiffs_lazy_unmount(); LED_B_OFF(); - if (res != 0) { + if (res != SPIFFS_OK) { Dbprintf("error writing '"HF_ICLASS_ATTACK_BIN"' to flash ( %d )", res); } } @@ -272,6 +295,29 @@ static int reader_dump_mode(void) { return PM3_SUCCESS; } +static int config_sim_mode(void) { + + uint8_t *emul = BigBuf_get_EM_addr(); + + for (uint8_t i = 0; i < 2; i++) { + SpinOff(0); + uint32_t fsize = size_in_spiffs(cc_files[i]); + + rdv40_spiffs_lazy_mount(); + int res = rdv40_spiffs_read_as_filetype(cc_files[i], emul, fsize, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_lazy_unmount(); + + if (res == SPIFFS_OK) { + Dbprintf("loaded '" _YELLOW_("%s") "' (%u bytes) to emulator memory", cc_files[i], fsize); + } + + iclass_simulate(ICLASS_SIM_MODE_FULL, 0 , false, NULL, NULL, NULL); + } + + rdv40_spiffs_lazy_unmount(); + return PM3_SUCCESS; +} + void ModInfo(void) { DbpString(" HF iCLASS mode - aka iceCLASS (iceman)"); } @@ -284,28 +330,45 @@ void RunMod(void) { StandAloneMode(); Dbprintf(_YELLOW_("HF iCLASS mode a.k.a iceCLASS started")); - uint8_t mode = ICE_STATE_READER; + uint8_t mode = ICE_STATE_ATTACK; for (;;) { + WDT_HIT(); + + if (mode == ICE_STATE_NONE) break; if (data_available()) break; +/* // Was our button held down or pressed? int button_pressed = BUTTON_HELD(1000); if (button_pressed != BUTTON_NO_CLICK) { break; } + */ int res; switch (mode) { case ICE_STATE_FULLSIM: { Dbprintf("enter full simulation mode"); - res = fullsim_mode(); - if (res == PM3_SUCCESS) - download_instructions(mode); + + // Look for iCLASS dump file + rdv40_spiffs_lazy_mount(); + if (exists_in_spiffs(HF_ICLASS_FULLSIM_ORIG_BIN) == false) { + Dbprintf("error, '" _YELLOW_(HF_ICLASS_FULLSIM_ORIG_BIN) "' file missing"); + mode = ICE_STATE_NONE; + } + rdv40_spiffs_lazy_unmount(); - switch_off(); + if (mode == ICE_STATE_FULLSIM) { + res = fullsim_mode(); + if (res == PM3_SUCCESS) { + download_instructions(mode); + } + } + // the button press to exit sim, is captured in main loop here + mode = ICE_STATE_NONE; break; } case ICE_STATE_ATTACK: { @@ -315,7 +378,6 @@ void RunMod(void) { download_instructions(mode); mode = ICE_STATE_NONE; - switch_off(); break; } case ICE_STATE_READER: { @@ -325,12 +387,30 @@ void RunMod(void) { download_instructions(mode); mode = ICE_STATE_NONE; - switch_off(); + break; + } + case ICE_STATE_CONFIGCARD: { + Dbprintf("enter config card simulation mode"); + + // Look for config cards + rdv40_spiffs_lazy_mount(); + for (uint8_t i =0; i < 2; i++) { + if (exists_in_spiffs(cc_files[i]) == false) { + Dbprintf("error, '" _YELLOW_("%s") "' file missing", cc_files[i]); + mode = ICE_STATE_NONE; + } + } + rdv40_spiffs_lazy_unmount(); + + if (mode == ICE_STATE_CONFIGCARD) + config_sim_mode(); + + mode = ICE_STATE_NONE; break; } } } - LEDsoff(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + switch_off(); + Dbprintf("-=[ exit iceCLASS ]=-"); } diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index b85d996d6..3568bd50a 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -601,6 +601,7 @@ typedef struct { #define ICLASS_SIM_MODE_FULL 3 #define ICLASS_SIM_MODE_READER_ATTACK_KEYROLL 4 #define ICLASS_SIM_MODE_EXIT_AFTER_MAC 5 // note: device internal only +#define ICLASS_SIM_MODE_CONFIG_CARD 6 #define MODE_SIM_CSN 0 #define MODE_EXIT_AFTER_MAC 1 From d9571d8df7b28c8c98f924b51d8c1172c45e8d89 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 15 Jul 2020 11:35:03 +0200 Subject: [PATCH 081/139] loclass, print helptxt --- client/src/cmdhficlass.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 2aeb9a03f..80f6dfab5 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -2018,9 +2018,6 @@ static int CmdHFiClass_loclass(const char *Cmd) { char fileName[FILE_PATH_SIZE] = {0}; if (param_getstr(Cmd, 1, fileName, sizeof(fileName)) > 0) { return bruteforceFileNoKeys(fileName); - } else { - PrintAndLogEx(WARNING, "You must specify a filename"); - return PM3_EFILE; } } else if (opt == 't') { char opt2 = tolower(param_getchar(Cmd, 1)); @@ -2035,7 +2032,8 @@ static int CmdHFiClass_loclass(const char *Cmd) { return PM3_ESOFT; } - return PM3_SUCCESS; + + return usage_hf_iclass_loclass(); } void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t endblock, size_t filesize) { From a59aa7a570056129867da5575dfa831c933d6ad4 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 15 Jul 2020 11:35:27 +0200 Subject: [PATCH 082/139] rename fct --- armsrc/iclass.c | 18 +++++++++++------- armsrc/iclass.h | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index f232c35ed..d8892a74c 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -166,14 +166,14 @@ void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_ if (sim_type == ICLASS_SIM_MODE_CSN) { // Use the CSN from commandline memcpy(emulator, datain, 8); - doIClassSimulation(ICLASS_SIM_MODE_CSN, NULL); + do_iclass_simulation(ICLASS_SIM_MODE_CSN, NULL); } else if (sim_type == ICLASS_SIM_MODE_CSN_DEFAULT) { //Default CSN uint8_t csn[] = { 0x03, 0x1f, 0xec, 0x8a, 0xf7, 0xff, 0x12, 0xe0 }; // Use the CSN from commandline memcpy(emulator, csn, 8); - doIClassSimulation(ICLASS_SIM_MODE_CSN, NULL); + do_iclass_simulation(ICLASS_SIM_MODE_CSN, NULL); } else if (sim_type == ICLASS_SIM_MODE_READER_ATTACK) { @@ -187,7 +187,7 @@ void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_ memcpy(emulator, datain + (i * 8), 8); - if (doIClassSimulation(ICLASS_SIM_MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE)) { + if (do_iclass_simulation(ICLASS_SIM_MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE)) { if (dataoutlen) *dataoutlen = i * EPURSE_MAC_SIZE; @@ -207,7 +207,11 @@ void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_ } else if (sim_type == ICLASS_SIM_MODE_FULL) { //This is 'full sim' mode, where we use the emulator storage for data. //ie: BigBuf_get_EM_addr should be previously filled with data from the "eload" command - doIClassSimulation(ICLASS_SIM_MODE_FULL, NULL); + do_iclass_simulation(ICLASS_SIM_MODE_FULL, NULL); + } else if (sim_type == ICLASS_SIM_MODE_CONFIG_CARD) { + // config card + do_iclass_simulation(ICLASS_SIM_MODE_FULL, NULL); + // swap bin } else if (sim_type == ICLASS_SIM_MODE_READER_ATTACK_KEYROLL) { @@ -229,7 +233,7 @@ void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_ memcpy(emulator, datain + (i * 8), 8); // keyroll 1 - if (doIClassSimulation(ICLASS_SIM_MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE)) { + if (do_iclass_simulation(ICLASS_SIM_MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE)) { if (dataoutlen) *dataoutlen = i * EPURSE_MAC_SIZE * 2; @@ -242,7 +246,7 @@ void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_ } // keyroll 2 - if (doIClassSimulation(ICLASS_SIM_MODE_EXIT_AFTER_MAC, mac_responses + (i + num_csns) * EPURSE_MAC_SIZE)) { + if (do_iclass_simulation(ICLASS_SIM_MODE_EXIT_AFTER_MAC, mac_responses + (i + num_csns) * EPURSE_MAC_SIZE)) { if (dataoutlen) *dataoutlen = i * EPURSE_MAC_SIZE * 2; @@ -281,7 +285,7 @@ out: * @param csn - csn to use * @param breakAfterMacReceived if true, returns after reader MAC has been received. */ -int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { +int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { // free eventually allocated BigBuf memory BigBuf_free_keep_EM(); diff --git a/armsrc/iclass.h b/armsrc/iclass.h index fa256c925..cc93f771c 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -22,7 +22,7 @@ void iClass_Dump(uint8_t blockno, uint8_t numblks); void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data); void iClass_ReadCheck(uint8_t blockno, uint8_t keytype); -int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf); +int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf); void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_t *datain, uint8_t *dataout, uint16_t *dataoutlen); From 85be784d26827ed467abb54e29980133de5ea654 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 15 Jul 2020 11:55:21 +0200 Subject: [PATCH 083/139] ev3 detection enabled --- client/src/cmdhfmfdes.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index e7a8e3746..0e7e0f678 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -382,7 +382,7 @@ static char *getVersionStr(uint8_t major, uint8_t minor) { sprintf(retStr, "%x.%x (" _YELLOW_("DESFire EV1") ")", major, minor); else if (major == 0x12 && minor == 0x00) sprintf(retStr, "%x.%x (" _YELLOW_("DESFire EV2") ")", major, minor); - else if (major == 0x13 && minor == 0x00) + else if (major == 0x33 && minor == 0x00) sprintf(retStr, "%x.%x (" _YELLOW_("DESFire EV3") ")", major, minor); else if (major == 0x30 && minor == 0x00) sprintf(retStr, "%x.%x (" _YELLOW_("DESFire Light") ")", major, minor); @@ -644,8 +644,8 @@ static nxp_cardtype_t getCardType(uint8_t major, uint8_t minor) { return DESFIRE_EV1; if (major == 0x12 && minor == 0x00) return DESFIRE_EV2; -// if (major == 0x13 && minor == 0x00) -// return DESFIRE_EV3; + if (major == 0x33 && minor == 0x00) + return DESFIRE_EV3; if (major == 0x30 && minor == 0x00) return DESFIRE_LIGHT; if (major == 0x11 && minor == 0x00) From 3354f0d9d36ef171f5a3e3c43b9ddcf6f1844ea2 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 15 Jul 2020 15:16:35 +0200 Subject: [PATCH 084/139] unified static dma buffer as bigbuf_malloc, less pressure on stack size --- armsrc/BigBuf.c | 64 +++++++++++++--- armsrc/BigBuf.h | 14 +++- armsrc/iso14443a.c | 23 +++--- armsrc/iso14443b.c | 185 ++++++++++++++++++--------------------------- armsrc/iso15693.c | 83 ++++++++++---------- 5 files changed, 194 insertions(+), 175 deletions(-) diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 7cb440d82..338206d6e 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -36,6 +36,33 @@ static uint32_t s_bigbuf_hi = 0; // pointer to the emulator memory. static uint8_t *emulator_memory = NULL; +//============================================================================= +// The ToSend buffer. +// A buffer where we can queue things up to be sent through the FPGA, for +// any purpose (fake tag, as reader, whatever). We go MSB first, since that +// is the order in which they go out on the wire. +//============================================================================= +static tosend_t toSend = { + .max = -1, + .bit = 8, + .buf = NULL +}; +//============================================================================= +// The dmaBuf 16bit buffer. +// A buffer where we recive IQ samples sent from the FPGA, for demodulating +//============================================================================= +static dmabuf16_t dma_16 = { + .size = DMA_BUFFER_SIZE, + .buf = NULL +}; +// dmaBuf 8bit buffer +static dmabuf8_t dma_8 = { + .size = DMA_BUFFER_SIZE, + .buf = NULL +}; + + + // trace related variables static uint32_t trace_len = 0; static bool tracing = true; @@ -106,6 +133,9 @@ void BigBuf_free(void) { s_bigbuf_hi = s_bigbuf_size; emulator_memory = NULL; // shouldn't this empty BigBuf also? + toSend.buf = NULL; + dma_16.buf = NULL; + dma_8.buf = NULL; } // free allocated chunks EXCEPT the emulator memory @@ -114,6 +144,10 @@ void BigBuf_free_keep_EM(void) { s_bigbuf_hi = emulator_memory - (uint8_t *)BigBuf; else s_bigbuf_hi = s_bigbuf_size; + + toSend.buf = NULL; + dma_16.buf = NULL; + dma_8.buf = NULL; } void BigBuf_print_status(void) { @@ -123,6 +157,10 @@ void BigBuf_print_status(void) { DbpString(_CYAN_("Tracing")); Dbprintf(" tracing ................%d", tracing); Dbprintf(" traceLen ...............%d", trace_len); + + Dbprintf(" dma8 memory.............%d", dma_8.buf - BigBuf_get_addr()); + Dbprintf(" dma16 memory............%d", (uint8_t*)dma_16.buf - BigBuf_get_addr()); + Dbprintf(" toSend memory...........%d", toSend.buf - BigBuf_get_addr() ); } // return the maximum trace length (i.e. the unallocated size of BigBuf) @@ -228,17 +266,6 @@ uint8_t emlSet(uint8_t *data, uint32_t offset, uint32_t length) { } -//============================================================================= -// The ToSend buffer. -// A buffer where we can queue things up to be sent through the FPGA, for -// any purpose (fake tag, as reader, whatever). We go MSB first, since that -// is the order in which they go out on the wire. -//============================================================================= -static tosend_t toSend = { - .max = -1, - .bit = 8, - .buf = NULL -}; // get the address of the ToSend buffer. Allocate part of Bigbuf for it, if not yet done tosend_t *get_tosend(void) { @@ -270,3 +297,18 @@ void tosend_stuffbit(int b) { toSend.bit = 0; } } + +dmabuf16_t *get_dma16(void) { + if (dma_16.buf == NULL) + dma_16.buf = (uint16_t*)BigBuf_malloc(DMA_BUFFER_SIZE); + + return &dma_16; +} + +dmabuf8_t *get_dma8(void) { + if (dma_8.buf == NULL) + dma_8.buf = BigBuf_malloc(DMA_BUFFER_SIZE); + + return &dma_8; +} + diff --git a/armsrc/BigBuf.h b/armsrc/BigBuf.h index 33c454226..2f381f36b 100644 --- a/armsrc/BigBuf.h +++ b/armsrc/BigBuf.h @@ -19,7 +19,7 @@ #define MAX_MIFARE_FRAME_SIZE 18 // biggest Mifare frame is answer to a read (one block = 16 Bytes) + 2 Bytes CRC #define MAX_MIFARE_PARITY_SIZE 3 // need 18 parity bits for the 18 Byte above. 3 Bytes are enough to store these #define CARD_MEMORY_SIZE 4096 -#define DMA_BUFFER_SIZE 128 +#define DMA_BUFFER_SIZE 256 // 8 data bits and 1 parity bit per payload byte, 1 correction bit, 1 SOC bit, 2 EOC bits #define TOSEND_BUFFER_SIZE (9 * MAX_FRAME_SIZE + 1 + 1 + 2) @@ -56,4 +56,16 @@ tosend_t *get_tosend(void); void tosend_reset(void); void tosend_stuffbit(int b); +typedef struct { + uint16_t size; + uint8_t *buf; +} dmabuf8_t; + +typedef struct { + uint16_t size; + uint16_t *buf; +} dmabuf16_t; + +dmabuf8_t *get_dma8(void); +dmabuf16_t *get_dma16(void); #endif /* __BIGBUF_H */ diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index bac3008f9..53d1f6814 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -585,10 +585,6 @@ void RAMFUNC SniffIso14443a(uint8_t param) { 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 maxDataLen = 0, dataLen; bool TagIsActive = false; @@ -602,8 +598,12 @@ void RAMFUNC SniffIso14443a(uint8_t param) { DbpString("Starting to sniff"); + // The DMA buffer, used to stream samples from the FPGA + dmabuf8_t *dma = get_dma8(); + uint8_t *data = dma->buf; + // Setup and start DMA. - if (!FpgaSetupSscDma((uint8_t *) dmaBuf, DMA_BUFFER_SIZE)) { + if (!FpgaSetupSscDma((uint8_t *) dma->buf, DMA_BUFFER_SIZE)) { if (DBGLEVEL > 1) Dbprintf("FpgaSetupSscDma failed. Exiting"); return; } @@ -621,7 +621,7 @@ void RAMFUNC SniffIso14443a(uint8_t param) { WDT_HIT(); LED_A_ON(); - int register readBufDataP = data - dmaBuf; + int register readBufDataP = data - dma->buf; int register dmaBufDataP = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR; if (readBufDataP <= dmaBufDataP) dataLen = dmaBufDataP - readBufDataP; @@ -640,13 +640,13 @@ void RAMFUNC SniffIso14443a(uint8_t param) { // 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_RPR = (uint32_t) dma->buf; 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_RNPR = (uint32_t) dma->buf; AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; } @@ -710,16 +710,15 @@ void RAMFUNC SniffIso14443a(uint8_t param) { previous_data = *data; rx_samples++; data++; - if (data == dmaBuf + DMA_BUFFER_SIZE) { - data = dmaBuf; + if (data == dma->buf + DMA_BUFFER_SIZE) { + data = dma->buf; } } // end main loop FpgaDisableTracing(); if (DBGLEVEL >= DBG_ERROR) { - Dbprintf("maxDataLen=%d, Uart.state=%x, Uart.len=%d", maxDataLen, Uart.state, Uart.len); - Dbprintf("traceLen=" _YELLOW_("%d")", Uart.output[0]="_YELLOW_("%08x"), BigBuf_get_traceLen(), (uint32_t)Uart.output[0]); + Dbprintf("trace len = " _YELLOW_("%d"), BigBuf_get_traceLen()); } switch_off(); } diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index d9d08a966..e26ebb017 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -26,11 +26,8 @@ #include "ticks.h" -#ifndef ISO14443B_DMA_BUFFER_SIZE -# define ISO14443B_DMA_BUFFER_SIZE 128 -#endif #ifndef RECEIVE_MASK -# define RECEIVE_MASK (ISO14443B_DMA_BUFFER_SIZE-1) +# define RECEIVE_MASK (DMA_BUFFER_SIZE - 1) #endif #define RECEIVE_SAMPLES_TIMEOUT 64 @@ -480,13 +477,7 @@ static void TransmitFor14443b_AsTag(uint8_t *response, uint16_t len) { // Put byte into tx holding register as soon as it is ready if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { - AT91C_BASE_SSC->SSC_THR = response[++i]; - } - - // Prevent rx holding register from overflowing - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t b = AT91C_BASE_SSC->SSC_RHR; - (void)b; + AT91C_BASE_SSC->SSC_THR = response[i++]; } } } @@ -702,40 +693,17 @@ void SimulateIso14443bTag(uint32_t pupi) { * false if we are still waiting for some more * */ -static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { - int v = 0, myI = ABS(ci), myQ = ABS(cq); - -// The soft decision on the bit uses an estimate of just the -// quadrant of the reference angle, not the exact angle. -#define MAKE_SOFT_DECISION(void) { \ - if (Demod.sumI > 0) { \ - v = ci; \ - } else { \ - v = -ci; \ - } \ - if (Demod.sumQ > 0) { \ - v += cq; \ - } else { \ - v -= cq; \ - } \ - } +static RAMFUNC int Handle14443bTagSamplesDemod(uint16_t amplitude) { #define SUBCARRIER_DETECT_THRESHOLD 8 -//note: couldn't we just use MAX(ABS(ci),ABS(cq)) + (MIN(ABS(ci),ABS(cq))/2) from common.h - marshmellow -#define CHECK_FOR_SUBCARRIER(void) { v = MAX(myI, myQ) + (MIN(myI, myQ) >> 1); } - switch (Demod.state) { case DEMOD_UNSYNCD: - CHECK_FOR_SUBCARRIER(); - - // subcarrier detected - - if (v > SUBCARRIER_DETECT_THRESHOLD) { + if (amplitude > SUBCARRIER_DETECT_THRESHOLD) { Demod.state = DEMOD_PHASE_REF_TRAINING; - Demod.sumI = ci; - Demod.sumQ = cq; + Demod.sumI = amplitude; + Demod.sumQ = amplitude; Demod.posCount = 1; } break; @@ -743,13 +711,11 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { case DEMOD_PHASE_REF_TRAINING: if (Demod.posCount < 8) { - CHECK_FOR_SUBCARRIER(); - - if (v > SUBCARRIER_DETECT_THRESHOLD) { + if (amplitude > SUBCARRIER_DETECT_THRESHOLD) { // set the reference phase (will code a logic '1') by averaging over 32 1/fs. // note: synchronization time > 80 1/fs - Demod.sumI += ci; - Demod.sumQ += cq; + Demod.sumI += amplitude; + Demod.sumQ += amplitude; Demod.posCount++; } else { // subcarrier lost @@ -762,9 +728,7 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { case DEMOD_AWAITING_FALLING_EDGE_OF_SOF: - MAKE_SOFT_DECISION(); - - if (v < 0) { // logic '0' detected + if (amplitude == 0) { // logic '0' detected Demod.state = DEMOD_GOT_FALLING_EDGE_OF_SOF; Demod.posCount = 0; // start of SOF sequence } else { @@ -779,9 +743,7 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { case DEMOD_GOT_FALLING_EDGE_OF_SOF: Demod.posCount++; - MAKE_SOFT_DECISION(); - - if (v > 0) { + if (amplitude > 0) { // low phase of SOF too short (< 9 etu). Note: spec is >= 10, but FPGA tends to "smear" edges if (Demod.posCount < 9 * 2) { Demod.state = DEMOD_UNSYNCD; @@ -804,9 +766,7 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { case DEMOD_AWAITING_START_BIT: Demod.posCount++; - MAKE_SOFT_DECISION(); - - if (v > 0) { + if (amplitude > 0) { if (Demod.posCount > 6 * 2) { // max 19us between characters = 16 1/fs, max 3 etu after low phase of SOF = 24 1/fs LED_C_OFF(); if (Demod.bitCount == 0 && Demod.len == 0) { // received SOF only, this is valid for iClass/Picopass @@ -818,7 +778,7 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { } else { // start bit detected Demod.bitCount = 0; Demod.posCount = 1; // this was the first half - Demod.thisBit = v; + Demod.thisBit = amplitude; Demod.shiftReg = 0; Demod.state = DEMOD_RECEIVING_DATA; } @@ -826,15 +786,13 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { case DEMOD_RECEIVING_DATA: - MAKE_SOFT_DECISION(); - if (Demod.posCount == 0) { // first half of bit - Demod.thisBit = v; + Demod.thisBit = amplitude; Demod.posCount = 1; } else { // second half of bit - Demod.thisBit += v; + Demod.thisBit += amplitude; Demod.shiftReg >>= 1; // OR in a logic '1' @@ -882,64 +840,66 @@ static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { * Demodulate the samples we received from the tag, also log to tracebuffer */ static int GetTagSamplesFor14443bDemod(int timeout) { - int ret = 0; - int maxBehindBy = 0; - int lastRxCounter, samples = 0; - int8_t ci, cq; - uint32_t time_0 = 0, time_stop = 0; + + int samples = 0, ret = 0; BigBuf_free(); - // The response (tag -> reader) that we're receiving. - uint8_t *receivedResponse = BigBuf_malloc(MAX_FRAME_SIZE); - - // The DMA buffer, used to stream samples from the FPGA - uint16_t *dmaBuf = (uint16_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE * sizeof(uint16_t)); // Set up the demodulator for tag -> reader responses. - Demod14bInit(receivedResponse); + Demod14bInit(BigBuf_malloc(MAX_FRAME_SIZE)); // wait for last transfer to complete while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) // Setup and start DMA. FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); - if (FpgaSetupSscDma((uint8_t *) dmaBuf, ISO14443B_DMA_BUFFER_SIZE) == false) { + + // The DMA buffer, used to stream samples from the FPGA + dmabuf16_t *dma = get_dma16(); + if (FpgaSetupSscDma((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { if (DBGLEVEL > 1) Dbprintf("FpgaSetupSscDma failed. Exiting"); return -1; } - uint16_t *upTo = dmaBuf; - lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; - // Signal field is ON with the appropriate LED: LED_D_ON(); + // And put the FPGA in the appropriate mode FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_848_KHZ | FPGA_HF_READER_MODE_RECEIVE_IQ); +// uint32_t dma_start_time; + uint16_t *upTo = dma->buf; + for(;;) { - int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1); - if (behindBy > maxBehindBy) { - maxBehindBy = behindBy; - } + uint16_t behindBy = ((uint16_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); - if (behindBy < 1) continue; + if (behindBy == 0) continue; - ci = *upTo >> 8; - cq = *upTo; - upTo++; - lastRxCounter--; - if (upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. - upTo = dmaBuf; // start reading the circular buffer from the beginning - lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; - } - - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; // DMA Next Counter registers - } samples++; + /* + if (samples == 1) { + // DMA has transferred the very first data + dma_start_time = GetCountSspClk() & 0xfffffff0; + } + */ - if (Handle14443bTagSamplesDemod(ci, cq)) { + volatile uint16_t tagdata = *upTo++; + + if (upTo >= dma->buf + DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dma->buf; // start reading the circular buffer from the beginning + if (behindBy > (9 * DMA_BUFFER_SIZE / 10)) { + Dbprintf("About to blow circular buffer - aborted! behindBy=%d", behindBy); + ret = -1; + break; + } + } + + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; // DMA Next Counter registers + } + + if (Handle14443bTagSamplesDemod(tagdata)) { ret = Demod.len; break; } @@ -958,7 +918,7 @@ static int GetTagSamplesFor14443bDemod(int timeout) { } if (Demod.len > 0) { - LogTrace(Demod.output, Demod.len, time_0, time_stop, NULL, false); + LogTrace(Demod.output, Demod.len, 0, 0, NULL, false); } return ret; @@ -976,12 +936,14 @@ static void TransmitFor14443b_AsReader(void) { for (int c = 0; c < ts->max; c++) { uint8_t data = ts->buf[c]; + for (int i = 0; i < 8; i++) { uint16_t send_word = (data & 0x80) ? 0x0000 : 0xffff; while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; AT91C_BASE_SSC->SSC_THR = send_word; + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; AT91C_BASE_SSC->SSC_THR = send_word; @@ -1388,7 +1350,7 @@ static void iso1444b_setup_sniff(void) { Dbprintf("[+] trace: %i bytes", BigBuf_max_traceLen()); Dbprintf("[+] reader -> tag: %i bytes", MAX_FRAME_SIZE); Dbprintf("[+] tag -> reader: %i bytes", MAX_FRAME_SIZE); - Dbprintf("[+] DMA: %i bytes", ISO14443B_DMA_BUFFER_SIZE); + Dbprintf("[+] DMA: %i bytes", DMA_BUFFER_SIZE); } // connect Demodulated Signal to ADC: @@ -1432,12 +1394,7 @@ void RAMFUNC SniffIso14443b(void) { bool TagIsActive = false; bool ReaderIsActive = false; - iso1444b_setup_sniff(); - - // The DMA buffer, used to stream samples from the FPGA - uint16_t *dmaBuf = (uint16_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE * sizeof(uint16_t)); - uint16_t *upTo = dmaBuf; - int lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; + int lastRxCounter = DMA_BUFFER_SIZE; int8_t ci, cq; int maxBehindBy = 0; @@ -1445,8 +1402,14 @@ void RAMFUNC SniffIso14443b(void) { // information in the trace buffer. int samples = 0; + iso1444b_setup_sniff(); + + // The DMA buffer, used to stream samples from the FPGA + dmabuf16_t *dma = get_dma16(); + uint16_t *upTo = dma->buf; + // Setup and start DMA. - if (!FpgaSetupSscDma((uint8_t *) dmaBuf, ISO14443B_DMA_BUFFER_SIZE)) { + if (!FpgaSetupSscDma((uint8_t *) dma->buf, DMA_BUFFER_SIZE)) { if (DBGLEVEL > 1) Dbprintf("[!] FpgaSetupSscDma failed. Exiting"); BigBuf_free(); return; @@ -1458,29 +1421,31 @@ void RAMFUNC SniffIso14443b(void) { // loop and listen for(;;) { - int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE - 1); + int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (DMA_BUFFER_SIZE - 1); if (behindBy > maxBehindBy) { maxBehindBy = behindBy; } - if (behindBy < 1) continue; + if (behindBy == 0) continue; ci = *upTo >> 8; cq = *upTo; + uint16_t tagdata = *upTo; + upTo++; lastRxCounter--; - if (upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. - upTo = dmaBuf; // start reading the circular buffer from the beginning again - lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; - if (behindBy > (9 * ISO14443B_DMA_BUFFER_SIZE / 10)) { + if (upTo >= dma->buf + DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dma->buf; // start reading the circular buffer from the beginning again + lastRxCounter += DMA_BUFFER_SIZE; + if (behindBy > (9 * DMA_BUFFER_SIZE / 10)) { Dbprintf("About to blow circular buffer - aborted! behindBy=%d", behindBy); break; } } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; // DMA Next Counter registers + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; // DMA Next Counter registers WDT_HIT(); if (BUTTON_PRESS()) { @@ -1522,7 +1487,7 @@ void RAMFUNC SniffIso14443b(void) { // is this | 0x01 the error? & 0xfe in https://github.com/Proxmark/proxmark3/issues/103 // LSB is a fpga signal bit. - if (Handle14443bTagSamplesDemod(ci/2, cq/2) >= 0) { + if (Handle14443bTagSamplesDemod(tagdata) >= 0) { time_stop = GetCountSspClk() - time_0; LogTrace(Demod.output, Demod.len, time_start, time_stop, NULL, false); Uart14bReset(); diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index ff59d260a..36530f72b 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -91,7 +91,7 @@ /////////////////////////////////////////////////////////////////////// // buffers -#define ISO15693_DMA_BUFFER_SIZE 256 // must be a power of 2 +//#define ISO15693_DMA_BUFFER_SIZE 256 // must be a power of 2 #define ISO15693_MAX_RESPONSE_LENGTH 36 // allows read single block with the maximum block size of 256bits. Read multiple blocks not supported yet #define ISO15693_MAX_COMMAND_LENGTH 45 // allows write single block with the maximum block size of 256bits. Write multiple blocks not supported yet @@ -305,7 +305,7 @@ void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, uint8_t bits_to_shift = 0x00; uint8_t bits_to_send = 0x00; for (size_t c = 0; c < len; c++) { - for (int i = (c==0?4:7); i >= 0; i--) { + for (int i = (c == 0 ? 4 : 7); i >= 0; i--) { uint8_t cmd_bits = ((cmd[c] >> i) & 0x01) ? 0xff : 0x00; for (int j = 0; j < (slow ? 4 : 1); ) { if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { @@ -406,7 +406,7 @@ static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *De DecodeTag->threshold_sof = (amplitude - DecodeTag->previous_amplitude) / 2; } else { DecodeTag->posCount = 2; - DecodeTag->threshold_sof = DecodeTag->threshold_sof/2; + DecodeTag->threshold_sof = DecodeTag->threshold_sof / 2; } // DecodeTag->posCount = 2; DecodeTag->state = STATE_TAG_SOF_HIGH; @@ -621,7 +621,6 @@ static void DecodeTagReset(DecodeTag_t *DecodeTag) { int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeout, uint32_t *eof_time) { int samples = 0, ret = 0; - uint16_t dmaBuf[ISO15693_DMA_BUFFER_SIZE] = {0}; // the Decoder data structure DecodeTag_t DecodeTag = { 0 }; @@ -635,13 +634,18 @@ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeo // Setup and start DMA. FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); - FpgaSetupSscDma((uint8_t*) dmaBuf, ISO15693_DMA_BUFFER_SIZE); + + // The DMA buffer, used to stream samples from the FPGA + dmabuf16_t *dma = get_dma16(); + + FpgaSetupSscDma((uint8_t*) dma->buf, DMA_BUFFER_SIZE); + uint32_t dma_start_time = 0; - uint16_t *upTo = dmaBuf; + uint16_t *upTo = dma->buf; for(;;) { - uint16_t behindBy = ((uint16_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (ISO15693_DMA_BUFFER_SIZE-1); + uint16_t behindBy = ((uint16_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); if (behindBy == 0) continue; @@ -653,17 +657,17 @@ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeo volatile uint16_t tagdata = *upTo++; - if (upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. - upTo = dmaBuf; // start reading the circular buffer from the beginning - if (behindBy > (9 * ISO15693_DMA_BUFFER_SIZE / 10)) { + if (upTo >= dma->buf + DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dma->buf; // start reading the circular buffer from the beginning + if (behindBy > (9 * DMA_BUFFER_SIZE / 10)) { Dbprintf("About to blow circular buffer - aborted! behindBy=%d", behindBy); ret = -1; break; } } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO15693_DMA_BUFFER_SIZE; // DMA Next Counter registers + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; // DMA Next Counter registers } if (Handle15693SamplesFromTag(tagdata, &DecodeTag)) { @@ -1016,8 +1020,6 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo int samples = 0; bool gotFrame = false; - uint8_t dmaBuf[ISO15693_DMA_BUFFER_SIZE] = {0}; - // the decoder data structure DecodeReader_t DecodeReader = {0}; DecodeReaderInit(&DecodeReader, received, max_len, 0, NULL); @@ -1036,25 +1038,26 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo uint32_t dma_start_time = GetCountSspClk() & 0xfffffff8; // Setup and start DMA. - FpgaSetupSscDma(dmaBuf, ISO15693_DMA_BUFFER_SIZE); - uint8_t *upTo = dmaBuf; + dmabuf8_t *dma = get_dma8(); + FpgaSetupSscDma(dma->buf, DMA_BUFFER_SIZE); + uint8_t *upTo = dma->buf; for (;;) { - uint16_t behindBy = ((uint8_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (ISO15693_DMA_BUFFER_SIZE - 1); + uint16_t behindBy = ((uint8_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); if (behindBy == 0) continue; volatile uint8_t b = *upTo++; - if (upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. - upTo = dmaBuf; // start reading the circular buffer from the beginning - if (behindBy > (9*ISO15693_DMA_BUFFER_SIZE/10)) { + if (upTo >= dma->buf + DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dma->buf; // start reading the circular buffer from the beginning + if (behindBy > (9 * DMA_BUFFER_SIZE / 10)) { Dbprintf("About to blow circular buffer - aborted! behindBy %d", behindBy); break; } } if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO15693_DMA_BUFFER_SIZE; // DMA Next Counter registers + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; // DMA Next Counter registers } for (int i = 7; i >= 0; i--) { @@ -1146,12 +1149,8 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { clear_trace(); set_tracing(true); - - // The DMA buffer, used to stream samples from the FPGA - uint16_t dmaBuf[ISO15693_DMA_BUFFER_SIZE] = {0}; - + // Count of samples received so far, so that we can include timing - // information in the trace buffer. int samples = 0; DecodeTag_t DecodeTag = {0}; @@ -1168,29 +1167,32 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { Dbprintf(" Trace: %i bytes", BigBuf_max_traceLen()); Dbprintf(" Reader -> tag: %i bytes", ISO15693_MAX_COMMAND_LENGTH); Dbprintf(" tag -> Reader: %i bytes", ISO15693_MAX_RESPONSE_LENGTH); - Dbprintf(" DMA: %i bytes", ISO15693_DMA_BUFFER_SIZE * sizeof(uint16_t)); + Dbprintf(" DMA: %i bytes", DMA_BUFFER_SIZE * sizeof(uint16_t)); } Dbprintf("Sniff started. Press PM3 Button to stop."); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); LED_D_OFF(); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); StartCountSspClk(); - FpgaSetupSscDma((uint8_t*) dmaBuf, ISO15693_DMA_BUFFER_SIZE); + + // The DMA buffer, used to stream samples from the FPGA + dmabuf16_t *dma = get_dma16(); + FpgaSetupSscDma((uint8_t*)dma->buf, DMA_BUFFER_SIZE); + uint16_t *upTo = dma->buf; bool TagIsActive = false; bool ReaderIsActive = false; bool ExpectTagAnswer = false; uint32_t dma_start_time = 0; - uint16_t *upTo = dmaBuf; - uint16_t max_behindBy = 0; // And now we loop, receiving samples. for(;;) { - uint16_t behindBy = ((uint16_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (ISO15693_DMA_BUFFER_SIZE-1); + uint16_t behindBy = ((uint16_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); if (behindBy > max_behindBy) { max_behindBy = behindBy; } @@ -1204,17 +1206,16 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { } uint16_t sniffdata = *upTo++; - - if (upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. - upTo = dmaBuf; // start reading the circular buffer from the beginning - if (behindBy > (9*ISO15693_DMA_BUFFER_SIZE/10)) { - + + if (upTo >= dma->buf + DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dma->buf; // start reading the circular buffer from the beginning + if (behindBy > (9 * DMA_BUFFER_SIZE / 10)) { Dbprintf("About to blow circular buffer - aborted! behindBy=%d, samples=%d", behindBy, samples); break; } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO15693_DMA_BUFFER_SIZE; // DMA Next Counter registers + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; // DMA Next Counter registers WDT_HIT(); if (BUTTON_PRESS()) { DbpString("Sniff stopped."); From a1529b44ca77236cc15fd099e39a2fa6fd0e9b3b Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 19 Jul 2020 20:45:47 +0200 Subject: [PATCH 085/139] fix auth and --- armsrc/appmain.c | 6 + armsrc/iclass.c | 55 ++------ armsrc/iso15693.c | 228 +++++++++++++++++------------- client/src/cmdhficlass.c | 293 +++++++++++++++++++++++++-------------- client/src/cmdhficlass.h | 13 -- common/cardhelper.c | 11 ++ common/cardhelper.h | 1 + include/pm3_cmd.h | 17 +++ 8 files changed, 367 insertions(+), 257 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 094982a3c..7f4f24c43 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -960,6 +960,7 @@ static void PacketReceived(PacketCommandNG *packet) { case CMD_LF_HITAG_SNIFF: { // Eavesdrop Hitag tag, args = type SniffHitag2(); // SniffHitag2(packet->oldarg[0]); + reply_ng(CMD_LF_HITAG_SNIFF, PM3_SUCCESS, NULL, 0); break; } case CMD_LF_HITAG_SIMULATE: { // Simulate Hitag tag, args = memory content @@ -1027,6 +1028,7 @@ static void PacketReceived(PacketCommandNG *packet) { } PACKED; struct p *payload = (struct p *) packet->data.asBytes; SniffIso15693(payload->jam_search_len, payload->jam_search_string); + reply_ng(CMD_HF_ISO15693_SNIFF, PM3_SUCCESS, NULL, 0); break; } case CMD_HF_ISO15693_COMMAND: { @@ -1089,6 +1091,7 @@ static void PacketReceived(PacketCommandNG *packet) { } case CMD_HF_ISO14443B_SNIFF: { SniffIso14443b(); + reply_ng(CMD_HF_ISO14443B_SNIFF, PM3_SUCCESS, NULL, 0); break; } case CMD_HF_ISO14443B_SIMULATE: { @@ -1113,6 +1116,7 @@ static void PacketReceived(PacketCommandNG *packet) { } case CMD_HF_FELICA_SNIFF: { felica_sniff(packet->oldarg[0], packet->oldarg[1]); + reply_ng(CMD_HF_FELICA_SNIFF, PM3_SUCCESS, NULL, 0); break; } case CMD_HF_FELICALITE_DUMP: { @@ -1124,6 +1128,7 @@ static void PacketReceived(PacketCommandNG *packet) { #ifdef WITH_ISO14443a case CMD_HF_ISO14443A_SNIFF: { SniffIso14443a(packet->data.asBytes[0]); + reply_ng(CMD_HF_ISO14443A_SNIFF, PM3_SUCCESS, NULL, 0); break; } case CMD_HF_ISO14443A_READER: { @@ -1380,6 +1385,7 @@ static void PacketReceived(PacketCommandNG *packet) { } PACKED; struct p *payload = (struct p *) packet->data.asBytes; SniffIClass(payload->jam_search_len, payload->jam_search_string); + reply_ng(CMD_HF_ICLASS_SNIFF, PM3_SUCCESS, NULL, 0); break; } case CMD_HF_ICLASS_SIMULATE: { diff --git a/armsrc/iclass.c b/armsrc/iclass.c index d8892a74c..fa161b813 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -38,7 +38,7 @@ // The length of a received command will in most cases be no more than 18 bytes. // we expect max 34 bytes as tag answer (response to READ4) #ifndef ICLASS_BUFFER_SIZE -#define ICLASS_BUFFER_SIZE 34 +#define ICLASS_BUFFER_SIZE 34 + 2 #endif // iCLASS has a slightly different timing compared to ISO15693. According to the picopass data sheet the tag response is expected 330us after @@ -481,7 +481,7 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { uint8_t cmd, options, block; int len = 0; - bool exit_loop = 0; + bool exit_loop = false; while (exit_loop == false) { WDT_HIT(); @@ -863,12 +863,11 @@ static bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t static uint8_t identify[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x00, 0x73, 0x33 }; static uint8_t select[] = { 0x80 | ICLASS_CMD_SELECT, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static uint8_t read_conf[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x01, 0xfa, 0x22 }; - static uint8_t read_check_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; + uint8_t read_check_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; // Bit 4: K.If this bit equals to one, the READCHECK will use the Credit Key (Kc); if equals to zero, Debit Key (Kd) will be used // bit 7: parity. - if (use_credit_key) read_check_cc[0] = 0x10 | ICLASS_CMD_READCHECK; @@ -878,7 +877,6 @@ static bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t int len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_ACTALL, eof_time); if (len < 0) return false; - // send Identify start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; @@ -936,7 +934,7 @@ void ReaderIClass(uint8_t flags) { uint8_t card_data[6 * 8] = {0xFF}; // uint8_t last_csn[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - uint8_t resp[ICLASS_BUFFER_SIZE]; + uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; memset(resp, 0xFF, sizeof(resp)); // bool flag_readonce = flags & FLAG_ICLASS_READER_ONLY_ONCE; // flag to read until one tag is found successfully @@ -1162,21 +1160,9 @@ void iClass_Authentication(uint8_t *bytes) { bool iclass_auth(uint8_t *bytes, bool send_reply, uint8_t *dataout) { - struct p { - uint8_t key[8]; - bool use_raw; - bool use_elite; - bool use_credit_key; - } PACKED; - struct p *payload = (struct p *)bytes; - - // device response message - struct { - bool isOK; - uint8_t div_key[8]; - uint8_t mac[4]; - } PACKED packet; - + iclass_auth_req_t *payload = (iclass_auth_req_t *)bytes; + iclass_auth_resp_t packet; + Iso15693InitReader(); uint8_t card_data[3 * 8] = {0xFF}; @@ -1208,7 +1194,7 @@ bool iclass_auth(uint8_t *bytes, bool send_reply, uint8_t *dataout) { check[7] = packet.mac[2]; check[8] = packet.mac[3]; - uint8_t resp[ICLASS_BUFFER_SIZE]; + uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; packet.isOK = iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); if (send_reply) @@ -1236,12 +1222,12 @@ typedef struct iclass_premac { void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { uint8_t i = 0, isOK = 0; - uint8_t lastChunk = ((arg0 >> 8) & 0xFF); +// uint8_t lastChunk = ((arg0 >> 8) & 0xFF); bool use_credit_key = ((arg0 >> 16) & 0xFF); uint8_t keyCount = arg1 & 0xFF; uint8_t check[9] = { ICLASS_CMD_CHECK }; - uint8_t resp[ICLASS_BUFFER_SIZE]; + uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; uint8_t readcheck_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; if (use_credit_key) @@ -1260,8 +1246,8 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { Iso15693InitReader(); - uint32_t start_time = 0; - uint32_t eof_time = 0; + uint32_t start_time = 0, eof_time = 0; + if (select_iclass_tag(card_data, use_credit_key, &eof_time) == false) goto out; @@ -1305,15 +1291,7 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { out: // send keyindex. reply_mix(CMD_HF_ICLASS_CHKKEYS, isOK, i, 0, 0, 0); - - if (isOK >= 1 || lastChunk) { - LED_A_OFF(); - } - switch_off(); - - LED_B_OFF(); - LED_C_OFF(); } // Tries to read block. @@ -1338,8 +1316,8 @@ void iClass_ReadBlk(uint8_t blockno) { LED_A_ON(); result.isOK = iclass_readblock(blockno, result.blockdata); - switch_off(); reply_ng(CMD_HF_ICLASS_READBL, PM3_SUCCESS, (uint8_t *)&result, sizeof(result)); + switch_off(); } // Dump command seems to dump a block related portion of card memory. @@ -1372,11 +1350,6 @@ void iClass_Dump(uint8_t start_blockno, uint8_t numblks) { switch_off(); - // return pointer to dump memory in arg3 - // iceman: why not return | dataout - getbigbuf ? Should give exact location. -// Dbprintf("ICE:: dataout, %u max trace %u, bb start %u, data-bb %u ", dataout, BigBuf_max_traceLen(), BigBuf_get_addr(), dataout - BigBuf_get_addr() ); -// Dbprintf("ICE:: bb size %u, malloced %u (255*8)", BigBuf_get_size(), BigBuf_get_size() - (dataout - BigBuf_get_addr()) ); -// reply_mix(CMD_ACK, isOK, blkcnt, BigBuf_max_traceLen(), 0, 0); struct p { bool isOK; uint8_t block_cnt; @@ -1384,7 +1357,7 @@ void iClass_Dump(uint8_t start_blockno, uint8_t numblks) { } PACKED payload; payload.isOK = isOK; payload.block_cnt = blkcnt; - payload.bb_offset = BigBuf_max_traceLen(); + payload.bb_offset = dataout - BigBuf_get_addr(); reply_ng(CMD_HF_ICLASS_DUMP, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload)); BigBuf_free(); } diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 36530f72b..df576d3b3 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -91,7 +91,6 @@ /////////////////////////////////////////////////////////////////////// // buffers -//#define ISO15693_DMA_BUFFER_SIZE 256 // must be a power of 2 #define ISO15693_MAX_RESPONSE_LENGTH 36 // allows read single block with the maximum block size of 256bits. Read multiple blocks not supported yet #define ISO15693_MAX_COMMAND_LENGTH 45 // allows write single block with the maximum block size of 256bits. Write multiple blocks not supported yet @@ -106,7 +105,6 @@ #define AddCrc15(data, len) compute_crc(CRC_15693, (data), (len), (data)+(len), (data)+(len)+1) static void BuildIdentifyRequest(uint8_t *cmd); -static void BuildInventoryResponse(uint8_t *uid); // --------------------------- // Signal Processing @@ -255,12 +253,13 @@ void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time) { *start_time = (GetCountSspClk() + 16) & 0xfffffff0; // next possible time } - while (GetCountSspClk() < *start_time) - /* wait */ ; + // wait + while (GetCountSspClk() < *start_time) ; LED_B_ON(); for (int c = 0; c < len; c++) { volatile uint8_t data = cmd[c]; + for (uint8_t i = 0; i < 8; i++) { uint16_t send_word = (data & 0x80) ? 0xffff : 0x0000; while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; @@ -294,8 +293,8 @@ void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, } } - while (GetCountSspClk() < (modulation_start_time & 0xfffffff8)) - /* wait */ ; + // wait + while (GetCountSspClk() < (modulation_start_time & 0xfffffff8)) ; uint8_t shift_delay = modulation_start_time & 0x00000007; @@ -304,9 +303,12 @@ void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, LED_C_ON(); uint8_t bits_to_shift = 0x00; uint8_t bits_to_send = 0x00; + for (size_t c = 0; c < len; c++) { for (int i = (c == 0 ? 4 : 7); i >= 0; i--) { + uint8_t cmd_bits = ((cmd[c] >> i) & 0x01) ? 0xff : 0x00; + for (int j = 0; j < (slow ? 4 : 1); ) { if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { bits_to_send = bits_to_shift << (8 - shift_delay) | cmd_bits >> shift_delay; @@ -380,7 +382,7 @@ typedef struct DecodeTag { //----------------------------------------------------------------------------- static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *DecodeTag) { switch (DecodeTag->state) { - case STATE_TAG_SOF_LOW: + case STATE_TAG_SOF_LOW: { // waiting for a rising edge if (amplitude > NOISE_THRESHOLD + DecodeTag->previous_amplitude) { if (DecodeTag->posCount > 10) { @@ -395,8 +397,8 @@ static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *De DecodeTag->previous_amplitude = amplitude; } break; - - case STATE_TAG_SOF_RISING_EDGE: + } + case STATE_TAG_SOF_RISING_EDGE: { if (amplitude > DecodeTag->threshold_sof + DecodeTag->previous_amplitude) { // edge still rising if (amplitude > DecodeTag->threshold_sof + DecodeTag->threshold_sof) { // steeper edge, take this as time reference DecodeTag->posCount = 1; @@ -411,8 +413,8 @@ static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *De // DecodeTag->posCount = 2; DecodeTag->state = STATE_TAG_SOF_HIGH; break; - - case STATE_TAG_SOF_HIGH: + } + case STATE_TAG_SOF_HIGH: { // waiting for 10 times high. Take average over the last 8 if (amplitude > DecodeTag->threshold_sof) { DecodeTag->posCount++; @@ -429,8 +431,8 @@ static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *De DecodeTag->state = STATE_TAG_SOF_LOW; } break; - - case STATE_TAG_SOF_HIGH_END: + } + case STATE_TAG_SOF_HIGH_END: { // check for falling edge if (DecodeTag->posCount == 13 && amplitude < DecodeTag->threshold_sof) { DecodeTag->lastBit = SOF_PART1; // detected 1st part of SOF (12 samples low and 12 samples high) @@ -458,8 +460,8 @@ static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *De } } break; - - case STATE_TAG_RECEIVING_DATA: + } + case STATE_TAG_RECEIVING_DATA: { // FpgaDisableTracing(); // DEBUGGING // Dbprintf("amplitude = %d, threshold_sof = %d, threshold_half/4 = %d, previous_amplitude = %d", // amplitude, @@ -547,8 +549,8 @@ static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *De } DecodeTag->posCount++; break; - - case STATE_TAG_EOF: + } + case STATE_TAG_EOF: { if (DecodeTag->posCount == 1) { DecodeTag->sum1 = 0; DecodeTag->sum2 = 0; @@ -571,8 +573,8 @@ static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *De } DecodeTag->posCount++; break; - - case STATE_TAG_EOF_TAIL: + } + case STATE_TAG_EOF_TAIL: { if (DecodeTag->posCount == 1) { DecodeTag->sum1 = 0; DecodeTag->sum2 = 0; @@ -595,6 +597,7 @@ static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *De } DecodeTag->posCount++; break; + } } return false; @@ -690,20 +693,22 @@ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeo FpgaDisableSscDma(); + uint32_t sof_time = *eof_time - (DecodeTag.len * 8 * 8 * 16) // time for byte transfers - (32 * 16) // time for SOF transfer - (DecodeTag.lastBit != SOF_PART2 ? (32 * 16) : 0); // time for EOF transfer if (DBGLEVEL >= DBG_EXTENDED) { - Dbprintf("samples = %d, ret = %d, Decoder: state = %d, lastBit = %d, len = %d, bitCount = %d, posCount = %d", + Dbprintf("samples = %d, ret = %d, Decoder: state = %d, lastBit = %d, len = %d, bitCount = %d, posCount = %d, maxlen = %u", samples, ret, DecodeTag.state, DecodeTag.lastBit, DecodeTag.len, DecodeTag.bitCount, - DecodeTag.posCount + DecodeTag.posCount, + DecodeTag.max_len ); Dbprintf("timing: sof_time = %d, eof_time = %d", (sof_time * 4), (*eof_time * 4)); } @@ -890,11 +895,11 @@ static RAMFUNC int Handle15693SampleFromReader(bool bit, DecodeReader_t *DecodeR case STATE_READER_RECEIVE_DATA_1_OUT_OF_4: DecodeReader->posCount++; if (DecodeReader->posCount == 1) { - DecodeReader->sum1 = bit?1:0; + DecodeReader->sum1 = bit ? 1 : 0; } else if (DecodeReader->posCount <= 4) { if (bit) DecodeReader->sum1++; } else if (DecodeReader->posCount == 5) { - DecodeReader->sum2 = bit?1:0; + DecodeReader->sum2 = bit ? 1 : 0; } else { if (bit) DecodeReader->sum2++; } @@ -935,11 +940,11 @@ static RAMFUNC int Handle15693SampleFromReader(bool bit, DecodeReader_t *DecodeR case STATE_READER_RECEIVE_DATA_1_OUT_OF_256: DecodeReader->posCount++; if (DecodeReader->posCount == 1) { - DecodeReader->sum1 = bit?1:0; + DecodeReader->sum1 = bit ? 1 : 0; } else if (DecodeReader->posCount <= 4) { if (bit) DecodeReader->sum1++; } else if (DecodeReader->posCount == 5) { - DecodeReader->sum2 = bit?1:0; + DecodeReader->sum2 = bit ? 1 : 0; } else if (bit) { DecodeReader->sum2++; } @@ -976,20 +981,20 @@ static RAMFUNC int Handle15693SampleFromReader(bool bit, DecodeReader_t *DecodeR case STATE_READER_RECEIVE_JAMMING: DecodeReader->posCount++; if (DecodeReader->Coding == CODING_1_OUT_OF_4) { - if (DecodeReader->posCount == 7*16) { // 7 bits jammed + if (DecodeReader->posCount == 7 * 16) { // 7 bits jammed FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); // stop jamming // FpgaDisableTracing(); LED_D_OFF(); - } else if (DecodeReader->posCount == 8*16) { + } else if (DecodeReader->posCount == 8 * 16) { DecodeReader->posCount = 0; DecodeReader->output[DecodeReader->byteCount++] = 0x00; DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_4; } } else { - if (DecodeReader->posCount == 7*256) { // 7 bits jammend + if (DecodeReader->posCount == 7 * 256) { // 7 bits jammend FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); // stop jamming LED_D_OFF(); - } else if (DecodeReader->posCount == 8*256) { + } else if (DecodeReader->posCount == 8 * 256) { DecodeReader->posCount = 0; DecodeReader->output[DecodeReader->byteCount++] = 0x00; DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_256; @@ -1083,8 +1088,11 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo FpgaDisableSscDma(); - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("samples = %d, gotFrame = %d, Decoder: state = %d, len = %d, bitCount = %d, posCount = %d", - samples, gotFrame, DecodeReader.state, DecodeReader.byteCount, DecodeReader.bitCount, DecodeReader.posCount); + if (DBGLEVEL >= DBG_EXTENDED) { + Dbprintf("samples = %d, gotFrame = %d, Decoder: state = %d, len = %d, bitCount = %d, posCount = %d", + samples, gotFrame, DecodeReader.state, DecodeReader.byteCount, + DecodeReader.bitCount, DecodeReader.posCount); + } if (DecodeReader.byteCount > 0) { uint32_t sof_time = *eof_time @@ -1154,11 +1162,11 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { int samples = 0; DecodeTag_t DecodeTag = {0}; - uint8_t response[ISO15693_MAX_RESPONSE_LENGTH]; + uint8_t response[ISO15693_MAX_RESPONSE_LENGTH] = {0}; DecodeTagInit(&DecodeTag, response, sizeof(response)); DecodeReader_t DecodeReader = {0}; - uint8_t cmd[ISO15693_MAX_COMMAND_LENGTH]; + uint8_t cmd[ISO15693_MAX_COMMAND_LENGTH] = {0}; DecodeReaderInit(&DecodeReader, cmd, sizeof(cmd), jam_search_len, jam_search_string); // Print some debug information about the buffer sizes @@ -1166,7 +1174,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { Dbprintf("Sniffing buffers initialized:"); Dbprintf(" Trace: %i bytes", BigBuf_max_traceLen()); Dbprintf(" Reader -> tag: %i bytes", ISO15693_MAX_COMMAND_LENGTH); - Dbprintf(" tag -> Reader: %i bytes", ISO15693_MAX_RESPONSE_LENGTH); + Dbprintf(" Tag -> Reader: %i bytes", ISO15693_MAX_RESPONSE_LENGTH); Dbprintf(" DMA: %i bytes", DMA_BUFFER_SIZE * sizeof(uint16_t)); } @@ -1184,9 +1192,9 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { FpgaSetupSscDma((uint8_t*)dma->buf, DMA_BUFFER_SIZE); uint16_t *upTo = dma->buf; - bool TagIsActive = false; - bool ReaderIsActive = false; - bool ExpectTagAnswer = false; + bool tag_is_active = false; + bool reader_is_active = false; + bool expect_tag_answer = false; uint32_t dma_start_time = 0; uint16_t max_behindBy = 0; @@ -1225,7 +1233,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { } // no need to try decoding reader data if the tag is sending - if (TagIsActive == false) { + if (tag_is_active == false) { if (Handle15693SampleFromReader(sniffdata & 0x02, &DecodeReader)) { @@ -1242,8 +1250,8 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { // And also reset the demod code, which might have been // false-triggered by the commands from the reader. DecodeTagReset(&DecodeTag); - ReaderIsActive = false; - ExpectTagAnswer = true; + reader_is_active = false; + expect_tag_answer = true; } else if (Handle15693SampleFromReader(sniffdata & 0x01, &DecodeReader)) { @@ -1261,15 +1269,15 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { // And also reset the demod code, which might have been // false-triggered by the commands from the reader. DecodeTagReset(&DecodeTag); - ReaderIsActive = false; - ExpectTagAnswer = true; + reader_is_active = false; + expect_tag_answer = true; } else { - ReaderIsActive = (DecodeReader.state >= STATE_READER_RECEIVE_DATA_1_OUT_OF_4); + reader_is_active = (DecodeReader.state >= STATE_READER_RECEIVE_DATA_1_OUT_OF_4); } } - if (!ReaderIsActive && ExpectTagAnswer) { // no need to try decoding tag data if the reader is currently sending or no answer expected yet + if (!reader_is_active && expect_tag_answer) { // no need to try decoding tag data if the reader is currently sending or no answer expected yet if (Handle15693SamplesFromTag(sniffdata >> 2, &DecodeTag)) { uint32_t eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM_SNIFF; // end of EOF @@ -1285,19 +1293,18 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { // And ready to receive another response. DecodeTagReset(&DecodeTag); DecodeReaderReset(&DecodeReader); - ExpectTagAnswer = false; - TagIsActive = false; + expect_tag_answer = false; + tag_is_active = false; } else { - TagIsActive = (DecodeTag.state >= STATE_TAG_RECEIVING_DATA); + tag_is_active = (DecodeTag.state >= STATE_TAG_RECEIVING_DATA); } } } - - FpgaDisableSscDma(); + switch_off(); DbpString("Sniff statistics:"); - Dbprintf(" ExpectTagAnswer: %d, TagIsActive: %d, ReaderIsActive: %d", ExpectTagAnswer, TagIsActive, ReaderIsActive); + Dbprintf(" ExpectTagAnswer: %d, TagIsActive: %d, ReaderIsActive: %d", expect_tag_answer, tag_is_active, reader_is_active); Dbprintf(" DecodeTag State: %d", DecodeTag.state); Dbprintf(" DecodeTag byteCnt: %d", DecodeTag.len); Dbprintf(" DecodeTag posCount: %d", DecodeTag.posCount); @@ -1305,18 +1312,18 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { Dbprintf(" DecodeReader byteCnt: %d", DecodeReader.byteCount); Dbprintf(" DecodeReader posCount: %d", DecodeReader.posCount); Dbprintf(" Trace length: %d", BigBuf_get_traceLen()); - Dbprintf(" Max behindBy: %d", max_behindBy); + Dbprintf(" Max behindBy: %d", max_behindBy); } // Initialize Proxmark3 as ISO15693 reader void Iso15693InitReader(void) { - + + LEDsoff(); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Start from off (no field generated) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - SpinDelay(50); + SpinDelay(10); // switch field on FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); @@ -1329,7 +1336,7 @@ void Iso15693InitReader(void) { set_tracing(true); // give tags some time to energize - SpinDelay(200); + SpinDelay(250); StartCountSspClk(); } @@ -1354,31 +1361,6 @@ static void BuildIdentifyRequest(uint8_t *cmd) { AddCrc15(cmd, 3); } -// uid is in transmission order (which is reverse of display order) - -// When SIM: now the VICC>VCD responses when we are simulating a tag -static void BuildInventoryResponse(uint8_t *uid) { - - uint8_t cmd[CMD_INV_RESP] = {0}; - - cmd[0] = 0; // No error, no protocol format extension - cmd[1] = 0; // DSFID (data storage format identifier). 0x00 = not supported - - // 64-bit UID - cmd[2] = uid[7]; - cmd[3] = uid[6]; - cmd[4] = uid[5]; - cmd[5] = uid[4]; - cmd[6] = uid[3]; - cmd[7] = uid[2]; - cmd[8] = uid[1]; - cmd[9] = uid[0]; - - // CRC - AddCrc15(cmd, 10); - CodeIso15693AsTag(cmd, CMD_INV_RESP); -} - // Universal Method for sending to and recv bytes from a tag // init ... should we initialize the reader? // speed ... 0 low speed, 1 hi speed @@ -1403,7 +1385,7 @@ int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t tosend_t *ts = get_tosend(); TransmitTo15693Tag(ts->buf, ts->max, &start_time); - uint32_t end_time = start_time + 32 * (8 * ts->max - 4); // substract the 4 padding bits after EOF + uint32_t end_time = start_time + 32 * ((8 * ts->max) - 4); // substract the 4 padding bits after EOF LogTrace(send, sendlen, (start_time * 4), (end_time * 4), NULL, true); int res = 0; @@ -1573,9 +1555,9 @@ void Iso15693InitTag(void) { FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + clear_trace(); set_tracing(true); - // turn on clock StartCountSspClk(); } @@ -1583,44 +1565,90 @@ void Iso15693InitTag(void) { // all demodulation performed in arm rather than host. - greg void SimTagIso15693(uint8_t *uid) { + // free eventually allocated BigBuf memory + BigBuf_free_keep_EM(); + Iso15693InitTag(); LED_A_ON(); Dbprintf("ISO-15963 Simulating uid: %02X%02X%02X%02X%02X%02X%02X%02X", uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6], uid[7]); - uint8_t buf[ISO15_MAX_FRAME]; - memset(buf, 0x00, sizeof(buf)); - LED_C_ON(); - // Build a suitable reponse to the reader INVENTORY cocmmand - // not so obvious, but in the call to BuildInventoryResponse, the command is copied to the global ToSend buffer used below. - BuildInventoryResponse(uid); + // Build INVENTORY command + uint8_t resp_inv[CMD_INV_RESP] = {0}; + + resp_inv[0] = 0; // No error, no protocol format extension + resp_inv[1] = 0; // DSFID (data storage format identifier). 0x00 = not supported + + // 64-bit UID + resp_inv[2] = uid[7]; + resp_inv[3] = uid[6]; + resp_inv[4] = uid[5]; + resp_inv[5] = uid[4]; + resp_inv[6] = uid[3]; + resp_inv[7] = uid[2]; + resp_inv[8] = uid[1]; + resp_inv[9] = uid[0]; + + // CRC + AddCrc15(resp_inv, 10); + CodeIso15693AsTag(resp_inv, CMD_INV_RESP); tosend_t *ts = get_tosend(); - while (!BUTTON_PRESS()) { + enum { NO_FIELD, IDLE, ACTIVATED, SELECTED, HALTED } chip_state = NO_FIELD; + + bool button_pressed = false; + int vHf = 0; // in mV + + bool exit_loop = false; + while (exit_loop == false) { WDT_HIT(); + // find reader field + if (chip_state == NO_FIELD) { + +#if defined RDV4 + vHf = (MAX_ADC_HF_VOLTAGE_RDV40 * SumAdc(ADC_CHAN_HF_RDV40, 32)) >> 15; +#else + vHf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; +#endif + if (vHf > MF_MINFIELDV) { + chip_state = IDLE; + LED_A_ON(); + } else { + continue; + } + } + // Listen to reader uint8_t cmd[ISO15693_MAX_COMMAND_LENGTH]; - uint32_t eof_time = 0, start_time = 0; - int cmd_len = GetIso15693CommandFromReader(cmd, sizeof(cmd), &eof_time); - - if ((cmd_len >= 5) && (cmd[0] & ISO15_REQ_INVENTORY) && (cmd[1] == ISO15_CMD_INVENTORY)) { // TODO: check more flags - bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH); - start_time = eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM; - TransmitTo15693Reader(ts->buf, ts->max, &start_time, 0, slow); + uint32_t reader_eof_time = 0; + int cmd_len = GetIso15693CommandFromReader(cmd, sizeof(cmd), &reader_eof_time); + if (cmd_len < 0) { + Dbprintf("button pressed, exiting"); + button_pressed = true; + exit_loop = true; + break; } - if (DBGLEVEL >= DBG_EXTENDED) { - Dbprintf(" %d bytes read from reader:", cmd_len); - Dbhexdump(cmd_len, cmd, false); + // TODO: check more flags + if ((cmd_len >= 5) && (cmd[0] & ISO15_REQ_INVENTORY) && (cmd[1] == ISO15_CMD_INVENTORY)) { + bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH); + uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM; + TransmitTo15693Reader(ts->buf, ts->max, &response_time, 0, slow); + LogTrace(resp_inv, CMD_INV_RESP, response_time * 32, (response_time * 32) + (ts->max * 32 * 64), NULL, false); + + chip_state = SELECTED; } } - + switch_off(); + + if (button_pressed) + DbpString("button pressed"); } // Since there is no standardized way of reading the AFI out of a tag, we will brute force it diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 80f6dfab5..d063f1607 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -47,7 +47,7 @@ static uint8_t iClass_Key_Table[ICLASS_KEYS_MAX][8] = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, }; static int usage_hf_iclass_sim(void) { @@ -379,6 +379,28 @@ static inline uint32_t leadingzeros(uint64_t a) { #endif } +// iclass card descriptors +const char * card_types[] = { + "PicoPass 16K / 16", // 000 + "PicoPass 32K with current book 16K / 16", // 001 + "Unknown Card Type!", // 010 + "Unknown Card Type!", // 011 + "PicoPass 2K", // 100 + "Unknown Card Type!", // 101 + "PicoPass 16K / 2", // 110 + "PicoPass 32K with current book 16K / 2", // 111 +}; + +uint8_t card_app2_limit[] = { + 0xff, + 0xff, + 0xff, + 0xff, + 0x1f, + 0xff, + 0xff, + 0xff, +}; static uint8_t isset(uint8_t val, uint8_t mask) { return (val & mask); @@ -434,7 +456,7 @@ static void fuse_config(const picopass_hdr *hdr) { ); } -static void getMemConfig(uint8_t mem_cfg, uint8_t chip_cfg, uint8_t *max_blk, uint8_t *app_areas, uint8_t *kb) { +static void getMemConfig(uint8_t mem_cfg, uint8_t chip_cfg, uint8_t *app_areas, uint8_t *kb) { // mem-bit 5, mem-bit 7, chip-bit 4: defines chip type uint8_t k16 = isset(mem_cfg, 0x80); //uint8_t k2 = isset(mem_cfg, 0x08); @@ -443,47 +465,54 @@ static void getMemConfig(uint8_t mem_cfg, uint8_t chip_cfg, uint8_t *max_blk, ui if (isset(chip_cfg, 0x10) && !k16 && !book) { *kb = 2; *app_areas = 2; - *max_blk = 31; } else if (isset(chip_cfg, 0x10) && k16 && !book) { *kb = 16; *app_areas = 2; - *max_blk = 255; //16kb } else if (notset(chip_cfg, 0x10) && !k16 && !book) { *kb = 16; *app_areas = 16; - *max_blk = 255; //16kb } else if (isset(chip_cfg, 0x10) && k16 && book) { *kb = 32; *app_areas = 3; - *max_blk = 255; //16kb } else if (notset(chip_cfg, 0x10) && !k16 && book) { *kb = 32; *app_areas = 17; - *max_blk = 255; //16kb } else { *kb = 32; *app_areas = 2; - *max_blk = 255; } } +static uint8_t get_mem_config(const picopass_hdr *hdr) { + uint8_t mem = hdr->conf.mem_config; + uint8_t chip = hdr->conf.chip_config; + // three configuration bits that decides sizes + uint8_t type = (chip & 0x10) >> 2; + type |= (mem & 0x80) >> 6; + type |= (mem & 0x20) >> 5; + return type; +} + static void mem_app_config(const picopass_hdr *hdr) { uint8_t mem = hdr->conf.mem_config; uint8_t chip = hdr->conf.chip_config; - uint8_t applimit = hdr->conf.app_limit; uint8_t kb = 2; uint8_t app_areas = 2; - uint8_t max_blk = 31; - getMemConfig(mem, chip, &max_blk, &app_areas, &kb); + getMemConfig(mem, chip, &app_areas, &kb); - if (applimit < 6) applimit = 26; - if (kb == 2 && (applimit > 0x1f)) applimit = 26; + // three configuration bits that decides sizes + uint8_t type = (chip & 0x10) >> 2; + type |= (mem & 0x80) >> 6; + type |= (mem & 0x20) >> 5; + + uint8_t app1_limit = hdr->conf.app_limit - 5; // minus header blocks + uint8_t app2_limit = card_app2_limit[type]; PrintAndLogEx(INFO, "------ " _CYAN_("Memory") " ------"); - PrintAndLogEx(INFO, " %u KBits/%u App Areas (%u bytes), max blocks 0x%02X (%02d)", kb, app_areas, max_blk * 8, mem, mem); - PrintAndLogEx(INFO, " AA1 blocks %u { 0x06 - 0x%02X (06 - %02d) }", applimit - 5 , applimit, applimit); - PrintAndLogEx(INFO, " AA2 blocks %u { 0x%02X - 0x%02X (%02d - %02d) }", max_blk - applimit, applimit + 1, max_blk, applimit + 1, max_blk); + PrintAndLogEx(INFO, " %u KBits/%u App Areas (%u bytes)", kb, app_areas, app2_limit * 8); + PrintAndLogEx(INFO, " AA1 blocks %u { 0x06 - 0x%02X (06 - %02d) }", app1_limit , app1_limit + 5, app1_limit + 5); + PrintAndLogEx(INFO, " AA2 blocks %u { 0x%02X - 0x%02X (%02d - %02d) }", app2_limit - app1_limit, app1_limit + 5 + 1, app2_limit, app1_limit + 5 + 1, app2_limit); PrintAndLogEx(INFO, "------ " _CYAN_("KeyAccess") " ------"); PrintAndLogEx(INFO, " Kd = Debit key (AA1), Kc = Credit key (AA2)"); @@ -552,7 +581,15 @@ static int CmdHFiClassSniff(const char *Cmd) { payload.jam_search_len = sizeof(update_epurse_sequence); memcpy(payload.jam_search_string, update_epurse_sequence, sizeof(payload.jam_search_string)); } + + PacketResponseNG resp; + clearCommandBuffer(); SendCommandNG(CMD_HF_ICLASS_SNIFF, (uint8_t *)&payload, sizeof(payload)); + + WaitForResponse(CMD_HF_ICLASS_SNIFF, &resp); + + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass list") "` to look at the collected trace"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("trace save h") "` to save tracelog for later analysing"); return PM3_SUCCESS; } @@ -956,6 +993,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { } if (have_file) { + picopass_hdr *hdr = (picopass_hdr *)decrypted; uint8_t mem = hdr->conf.mem_config; @@ -963,10 +1001,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { uint8_t applimit = hdr->conf.app_limit; uint8_t kb = 2; uint8_t app_areas = 2; - uint8_t max_blk = 31; - getMemConfig(mem, chip, &max_blk, &app_areas, &kb); - - + getMemConfig(mem, chip, &app_areas, &kb); BLOCK79ENCRYPTION aa1_encryption = (decrypted[(6 * 8) + 7] & 0x03); @@ -976,7 +1011,8 @@ static int CmdHFiClassDecrypt(const char *Cmd) { PrintAndLogEx(WARNING, "Actual file len " _YELLOW_("%zu") " vs HID app-limit len " _YELLOW_("%u"), decryptedlen, applimit * 8); PrintAndLogEx(INFO, "Setting limit to " _GREEN_("%u"), limit * 8); } - uint8_t numblocks4userid = GetNumberBlocksForUserId(decrypted + (6 * 8)); + + //uint8_t numblocks4userid = GetNumberBlocksForUserId(decrypted + (6 * 8)); for (uint16_t blocknum = 0; blocknum < limit; ++blocknum) { @@ -987,7 +1023,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { continue; // Decrypted block 7,8,9 if configured. - if (blocknum > 6 && blocknum <= 6 + numblocks4userid && memcmp(enc_data, empty, 8) != 0) { + if (blocknum > 6 && blocknum <= 9 && memcmp(enc_data, empty, 8) != 0) { if (use_sc) { Decrypt(enc_data, decrypted + idx); } else { @@ -996,13 +1032,14 @@ static int CmdHFiClassDecrypt(const char *Cmd) { } } - //Use the first block (CSN) for filename + // use the first block (CSN) for filename char *fptr = calloc(50, sizeof(uint8_t)); - if (!fptr) { + if (fptr == false) { PrintAndLogEx(WARNING, "Failed to allocate memory"); free(decrypted); return PM3_EMALLOC; } + strcat(fptr, "hf-iclass-"); FillFileNameByUID(fptr, hdr->csn, "-dump-decrypted", sizeof(hdr->csn)); @@ -1013,6 +1050,8 @@ static int CmdHFiClassDecrypt(const char *Cmd) { PrintAndLogEx(INFO, "Following output skips CSN / block0"); printIclassDumpContents(decrypted, 1, (decryptedlen / 8), decryptedlen); + PrintAndLogEx(NORMAL, ""); + // decode block 6 if (memcmp(decrypted + (8 * 6), empty, 8) != 0) { if (use_sc) { @@ -1029,7 +1068,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { mid = bytes_to_num(decrypted + (8 * 7), 4); bot = bytes_to_num(decrypted + (8 * 7) + 4, 4); - PrintAndLogEx(INFO, "Block 7 binary"); + PrintAndLogEx(INFO, "Block 7 decoder"); char hexstr[8 + 1] = {0}; hex_to_buffer((uint8_t *)hexstr, decrypted + (8 * 7), 8, sizeof(hexstr) - 1, 0, 0, true); @@ -1039,15 +1078,36 @@ static int CmdHFiClassDecrypt(const char *Cmd) { uint8_t i = 0; while (i < strlen(binstr) && binstr[i++] == '0'); - PrintAndLogEx(SUCCESS, "%s", binstr + i); + PrintAndLogEx(SUCCESS, "Binary..................... " _GREEN_("%s"), binstr + i); PrintAndLogEx(INFO, "Wiegand decode"); wiegand_message_t packed = initialize_message_object(top, mid, bot); HIDTryUnpack(&packed, true); - PrintAndLogEx(INFO, "-----------------------------------------------------------------"); + } else { PrintAndLogEx(INFO, "No credential found."); } + + // decode block 9 + if (memcmp(decrypted + (8 * 9), empty, 8) != 0) { + + uint8_t usr_blk_len = GetNumberBlocksForUserId(decrypted + (8 * 6)); + if (usr_blk_len < 3) { + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Block 9 decoder"); + uint8_t pinsize = 10; + if (use_sc) { + pinsize = GetPinSize(decrypted + (8 * 6)); + } + uint64_t pin = bytes_to_num(decrypted + (8 * 9), 5); + char tmp[17] = {0}; + sprintf(tmp, "%."PRIu64, BCD2DEC(pin)); + PrintAndLogEx(INFO, "PIN........................ " _GREEN_("%.*s"), pinsize, tmp); + } + } + + PrintAndLogEx(INFO, "-----------------------------------------------------------------"); free(decrypted); free(fptr); @@ -1178,7 +1238,7 @@ static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool u .use_credit_key = use_credit_key }; memcpy(payload.key, KEY, 8); - + SendCommandNG(CMD_HF_ICLASS_AUTH, (uint8_t*)&payload, sizeof(payload)); PacketResponseNG resp; clearCommandBuffer(); @@ -1205,7 +1265,9 @@ static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool u if (MAC) memcpy(MAC, packet->mac, sizeof(packet->mac)); - if (verbose) PrintAndLogEx(SUCCESS, "authing with %s: %s", rawkey ? "raw key" : "diversified key", sprint_hex(div_key, 8)); + if (verbose) + PrintAndLogEx(SUCCESS, "authing with %s: %s", rawkey ? "raw key" : "diversified key", sprint_hex(div_key, 8)); + return true; } @@ -1214,15 +1276,11 @@ static int CmdHFiClassDump(const char *Cmd) { uint8_t MAC[4] = {0x00, 0x00, 0x00, 0x00}; uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t c_div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t blockno = 0; - uint8_t numblks = 0; - uint8_t maxBlk = 31; - uint8_t app_areas = 1; - uint8_t kb = 2; uint8_t KEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t CreditKEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t keyNbr = 0; uint8_t dataLen = 0; + uint8_t app_limit1, app_limit2 = 0; uint8_t fileNameLen = 0; char filename[FILE_PATH_SIZE] = {0}; char tempStr[50] = {0}; @@ -1329,6 +1387,7 @@ static int CmdHFiClassDump(const char *Cmd) { uint8_t readStatus = resp.oldarg[0] & 0xff; uint8_t *data = resp.data.asBytes; + picopass_hdr *hdr = (picopass_hdr *)data; if (readStatus == 0) { PrintAndLogEx(FAILED, "no tag found"); @@ -1338,11 +1397,14 @@ static int CmdHFiClassDump(const char *Cmd) { if (readStatus & (FLAG_ICLASS_CSN | FLAG_ICLASS_CONF | FLAG_ICLASS_CC)) { memcpy(tag_data, data, 8 * 3); - blockno += 2; // 2 to force re-read of block 2 later. (seems to respond differently..) - numblks = data[8]; - getMemConfig(data[13], data[12], &maxBlk, &app_areas, &kb); - // large memory - not able to dump pages currently - if (numblks > maxBlk) numblks = maxBlk; + + uint8_t type = get_mem_config(hdr); + app_limit1 = hdr->conf.app_limit; + app_limit2 = card_app2_limit[type]; + } else { + PrintAndLogEx(FAILED, "failed to read block 0,1,2"); + DropField(); + return PM3_ESOFT; } // authenticate debit key and get div_key - later store in dump block 3 @@ -1356,8 +1418,8 @@ static int CmdHFiClassDump(const char *Cmd) { uint8_t start_blockno; uint8_t numblks; } PACKED payload; - payload.start_blockno = blockno; - payload.numblks = numblks - blockno + 1; + payload.start_blockno = 5; + payload.numblks = app_limit1 - 5; clearCommandBuffer(); SendCommandNG(CMD_HF_ICLASS_DUMP, (uint8_t*)&payload, sizeof(payload)); @@ -1397,20 +1459,24 @@ static int CmdHFiClassDump(const char *Cmd) { return PM3_ESOFT; } + uint16_t offset = (5 * 8); uint32_t startindex = packet->bb_offset; - if (blocks_read * 8 > sizeof(tag_data) - (blockno * 8)) { + if (blocks_read * 8 > sizeof(tag_data) - offset) { PrintAndLogEx(FAILED, "data exceeded buffer size!"); - blocks_read = (sizeof(tag_data) / 8) - blockno; + blocks_read = (sizeof(tag_data) / 8) - 5; } // response ok - now get bigbuf content of the dump - if (!GetFromDevice(BIG_BUF, tag_data + (blockno * 8), blocks_read * 8, startindex, NULL, 0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, tag_data + offset, blocks_read * 8, startindex, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } + + PrintAndLogEx(INFO, "BB start index :: %u", startindex); + PrintAndLogEx(INFO, "BB :: %s", sprint_hex(tag_data + (5*8), 32)); - size_t gotBytes = blocks_read * 8 + blockno * 8; + offset += (blocks_read * 8); // try AA2 Kc, Credit if (have_credit_key) { @@ -1424,46 +1490,44 @@ static int CmdHFiClassDump(const char *Cmd) { return PM3_ESOFT; } - // do we still need to read more block? (aa2 enabled?) - if (maxBlk > blockno + numblks + 1) { + payload.start_blockno = app_limit1; + payload.numblks = app_limit2 - app_limit1 - 5; - payload.start_blockno = blockno + blocks_read; - payload.numblks = maxBlk - (blockno + blocks_read); + clearCommandBuffer(); + SendCommandNG(CMD_HF_ICLASS_DUMP, (uint8_t*)&payload, sizeof(payload)); - clearCommandBuffer(); - SendCommandNG(CMD_HF_ICLASS_DUMP, (uint8_t*)&payload, sizeof(payload)); - - if (!WaitForResponseTimeout(CMD_HF_ICLASS_DUMP, &resp, 2000)) { - PrintAndLogEx(WARNING, "command execute timeout 2"); - return PM3_ETIMEOUT; - } - - if (resp.status != PM3_SUCCESS) { - PrintAndLogEx(ERR, "failed to communicate with card"); - return resp.status; - } - - packet = (struct p_resp *)resp.data.asBytes; - if (packet->isOK == false) { - PrintAndLogEx(WARNING, "read block failed using credit key"); - return PM3_ESOFT; - } - - blocks_read = packet->block_cnt; - startindex = packet->bb_offset; - - if (blocks_read * 8 > sizeof(tag_data) - gotBytes) { - PrintAndLogEx(FAILED, "data exceeded buffer size!"); - blocks_read = (sizeof(tag_data) - gotBytes) / 8; - } - // get dumped data from bigbuf - if (!GetFromDevice(BIG_BUF, tag_data + gotBytes, blocks_read * 8, startindex, NULL, 0, NULL, 2500, false)) { - PrintAndLogEx(WARNING, "command execution time out"); - return PM3_ETIMEOUT; - } - - gotBytes += blocks_read * 8; + if (!WaitForResponseTimeout(CMD_HF_ICLASS_DUMP, &resp, 2000)) { + PrintAndLogEx(WARNING, "command execute timeout 2"); + return PM3_ETIMEOUT; } + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(ERR, "failed to communicate with card"); + return resp.status; + } + + packet = (struct p_resp *)resp.data.asBytes; + if (packet->isOK == false) { + PrintAndLogEx(WARNING, "read block failed using credit key"); + return PM3_ESOFT; + } + + // + blocks_read = packet->block_cnt; + startindex = packet->bb_offset; + + if (blocks_read * 8 > sizeof(tag_data) - offset) { + PrintAndLogEx(FAILED, "data exceeded buffer size!"); + blocks_read = (sizeof(tag_data) - offset) / 8; + } + + // get dumped data from bigbuf + if (!GetFromDevice(BIG_BUF, tag_data + offset, blocks_read * 8, startindex, NULL, 0, NULL, 2500, false)) { + PrintAndLogEx(WARNING, "command execution time out"); + return PM3_ETIMEOUT; + } + + offset += blocks_read * 8; } DropField(); @@ -1480,7 +1544,7 @@ static int CmdHFiClassDump(const char *Cmd) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "------+--+-------------------------+----------"); PrintAndLogEx(INFO, " CSN |00| " _GREEN_("%s") "|", sprint_hex(tag_data, 8)); - printIclassDumpContents(tag_data, 1, (gotBytes / 8), gotBytes); + printIclassDumpContents(tag_data, 1, (offset / 8), offset); if (filename[0] == 0) { //Use the first block (CSN) for filename @@ -1489,10 +1553,10 @@ static int CmdHFiClassDump(const char *Cmd) { } // save the dump to .bin file - PrintAndLogEx(SUCCESS, "saving dump file - %zu blocks read", gotBytes / 8); - saveFile(filename, ".bin", tag_data, gotBytes); - saveFileEML(filename, tag_data, gotBytes, 8); - saveFileJSON(filename, jsfIclass, tag_data, gotBytes, NULL); + PrintAndLogEx(SUCCESS, "saving dump file - %zu blocks read", offset / 8); + saveFile(filename, ".bin", tag_data, offset); + saveFileEML(filename, tag_data, offset, 8); + saveFileJSON(filename, jsfIclass, tag_data, offset, NULL); return PM3_SUCCESS; } @@ -1825,25 +1889,22 @@ static int CmdHFiClassCloneTag(const char *Cmd) { static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool verbose, bool auth) { - // return data. - struct p { - bool isOK; - uint8_t blockdata[8]; - } PACKED; - struct p *result = NULL; - // block 0,1 should always be able to read, and block 5 on some cards. if (auth || blockno >= 2) { uint8_t MAC[4] = {0x00, 0x00, 0x00, 0x00}; uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; if (select_and_auth(KEY, MAC, div_key, (keyType == 0x18), elite, rawkey, verbose) == false) { + if (verbose) PrintAndLogEx(FAILED, "select/auth failed"); + DropField(); return PM3_ESOFT; } } else { uint8_t CSN[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; if (select_only(CSN, CCNR, (keyType == 0x18), verbose) == false) { - return PM3_ESOFT; + if (verbose) PrintAndLogEx(FAILED, "select only failed"); + DropField(); + return PM3_ESOFT; } } @@ -1851,22 +1912,29 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo clearCommandBuffer(); SendCommandNG(CMD_HF_ICLASS_READBL, (uint8_t *)&blockno, sizeof(uint8_t)); - if (WaitForResponseTimeout(CMD_HF_ICLASS_READBL, &resp, 2000) == 0) { + if (WaitForResponseTimeout(CMD_HF_ICLASS_READBL, &resp, 2000) == false) { if (verbose) PrintAndLogEx(WARNING, "Command execute timeout"); DropField(); return PM3_ETIMEOUT; } + DropField(); + if (resp.status != PM3_SUCCESS) { if (verbose) PrintAndLogEx(ERR, "failed to communicate with card"); return PM3_EWRONGANSWER; } - result = (struct p *)resp.data.asBytes; - if (result->isOK == false) - return PM3_ESOFT; + // return data. + struct p { + bool isOK; + uint8_t blockdata[8]; + } PACKED; - DropField(); + struct p *result = (struct p *)resp.data.asBytes; + if (result->isOK == false) { + return PM3_ESOFT; + } PrintAndLogEx(SUCCESS, " block %02X : " _GREEN_("%s"), blockno, sprint_hex(result->blockdata, sizeof(result->blockdata))); @@ -1943,6 +2011,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { bool auth = false; bool verbose = false; uint8_t cmdp = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { case 'h': @@ -2000,10 +2069,10 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { if (got_blockno == false) errors = true; - if (errors || cmdp < 4) return usage_hf_iclass_readblock(); + if (errors) return usage_hf_iclass_readblock(); - if (!auth) - PrintAndLogEx(FAILED, "warning: no authentication used with read, only a few specific blocks can be read accurately without authentication."); + if (auth == false) + PrintAndLogEx(WARNING, "warning: no authentication used with read, only a few specific blocks can be read accurately without authentication."); return iclass_read_block(KEY, blockno, keyType, elite, rawkey, verbose, auth); } @@ -2993,6 +3062,20 @@ int CmdHFiClass(const char *Cmd) { return CmdsParse(CommandTable, Cmd); } + +//static void test_credential_type(void) { + // need AA1 key + // Block 5 -> tells if its a legacy or SIO, also tells which key to use. + + // tech | blocks used | desc | num of payloads + // -------+-----------------------+-----------------------------------+------ + // legacy | 6,7,8,9 | AA!, Access control payload | 1 + // SE | 6,7,8,9,10,11,12 | AA1, Secure identity object (SIO) | 1 + // SR | 6,7,8,9, | AA1, Access control payload | 2 + // | 10,11,12,13,14,15,16 | AA1, Secure identity object (SIO) | + // SEOS | | | +//} + int readIclass(bool loop, bool verbose) { bool tagFound = false; @@ -3079,6 +3162,10 @@ int readIclass(bool loop, bool verbose) { } } + uint8_t cardtype = get_mem_config(hdr); + PrintAndLogEx(SUCCESS, "%s", card_types[cardtype]); + + if (tagFound && !loop) { PrintAndLogEx(NORMAL, ""); DropField(); diff --git a/client/src/cmdhficlass.h b/client/src/cmdhficlass.h index 19b994d0c..53880d647 100644 --- a/client/src/cmdhficlass.h +++ b/client/src/cmdhficlass.h @@ -27,19 +27,6 @@ typedef struct iclass_prekey { uint8_t key[8]; } iclass_prekey_t; -typedef struct { - uint8_t key[8]; - bool use_raw; - bool use_elite; - bool use_credit_key; -} PACKED iclass_auth_req_t; - -typedef struct { - bool isOK; - uint8_t div_key[8]; - uint8_t mac[4]; -} PACKED iclass_auth_resp_t; - int CmdHFiClass(const char *Cmd); int readIclass(bool loop, bool verbose); diff --git a/common/cardhelper.c b/common/cardhelper.c index fbdaf2002..e34780896 100644 --- a/common/cardhelper.c +++ b/common/cardhelper.c @@ -19,6 +19,7 @@ #define CARD_INS_ENCRYPT 0x02 #define CARD_INS_DECODE 0x06 #define CARD_INS_NUMBLOCKS 0x07 +#define CARD_INS_PINSIZE 0x08 static uint8_t cmd[] = {0x96, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // look for CryptoHelper @@ -91,3 +92,13 @@ uint8_t GetNumberBlocksForUserId(uint8_t *src) { ExchangeAPDUSC(true, c, sizeof(c), false, true, resp, sizeof(resp), &resp_len); return resp[8]; } + +// Call with block6 +uint8_t GetPinSize(uint8_t *src) { + int resp_len = 0; + uint8_t resp[254] = {0}; + uint8_t c[] = {0x96, CARD_INS_PINSIZE, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + memcpy(c + 5, src, 8); + ExchangeAPDUSC(true, c, sizeof(c), false, true, resp, sizeof(resp), &resp_len); + return resp[8]; +} diff --git a/common/cardhelper.h b/common/cardhelper.h index eaf7ff38f..d55ae6701 100644 --- a/common/cardhelper.h +++ b/common/cardhelper.h @@ -19,4 +19,5 @@ bool Encrypt(uint8_t *src, uint8_t *dest); bool Decrypt(uint8_t *src, uint8_t *dest); void DecodeBlock6(uint8_t *src); uint8_t GetNumberBlocksForUserId(uint8_t *src); +uint8_t GetPinSize(uint8_t *src); #endif diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 3568bd50a..766e78c54 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -289,6 +289,23 @@ typedef struct { const char *value; } PACKED ecdsa_publickey_t; + +// iCLASS auth request data structure +typedef struct { + uint8_t key[8]; + bool use_raw; + bool use_elite; + bool use_credit_key; +} PACKED iclass_auth_req_t; + +// iCLASS auth response data structure +typedef struct { + bool isOK; + uint8_t div_key[8]; + uint8_t mac[4]; +} PACKED iclass_auth_resp_t; + + // For the bootloader #define CMD_DEVICE_INFO 0x0000 //#define CMD_SETUP_WRITE 0x0001 From 53174faaf6b0b21b74f38ca051ed28bed4570b3c Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 19 Jul 2020 23:08:53 +0200 Subject: [PATCH 086/139] adapt return value --- common/cardhelper.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/cardhelper.c b/common/cardhelper.c index e34780896..3a5def846 100644 --- a/common/cardhelper.c +++ b/common/cardhelper.c @@ -100,5 +100,9 @@ uint8_t GetPinSize(uint8_t *src) { uint8_t c[] = {0x96, CARD_INS_PINSIZE, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; memcpy(c + 5, src, 8); ExchangeAPDUSC(true, c, sizeof(c), false, true, resp, sizeof(resp), &resp_len); - return resp[8]; + + if ( resp[resp_len - 2] == 0x90 && resp[resp_len - 1] == 0x00) { + return resp[8]; + } + return 0; } From 20ad16bfca7f8179bff241de1e0c606152e896cf Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 19 Jul 2020 23:09:21 +0200 Subject: [PATCH 087/139] fix iclass dump.. --- client/src/cmdhficlass.c | 53 +++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index d063f1607..614555cb0 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -1096,14 +1096,18 @@ static int CmdHFiClassDecrypt(const char *Cmd) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Block 9 decoder"); - uint8_t pinsize = 10; + uint8_t pinsize = 0; if (use_sc) { pinsize = GetPinSize(decrypted + (8 * 6)); + + if (pinsize > 0) { + + uint64_t pin = bytes_to_num(decrypted + (8 * 9), 5); + char tmp[17] = {0}; + sprintf(tmp, "%."PRIu64, BCD2DEC(pin)); + PrintAndLogEx(INFO, "PIN........................ " _GREEN_("%.*s"), pinsize, tmp); + } } - uint64_t pin = bytes_to_num(decrypted + (8 * 9), 5); - char tmp[17] = {0}; - sprintf(tmp, "%."PRIu64, BCD2DEC(pin)); - PrintAndLogEx(INFO, "PIN........................ " _GREEN_("%.*s"), pinsize, tmp); } } @@ -1280,7 +1284,7 @@ static int CmdHFiClassDump(const char *Cmd) { uint8_t CreditKEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t keyNbr = 0; uint8_t dataLen = 0; - uint8_t app_limit1, app_limit2 = 0; + uint8_t app_limit1 = 0, app_limit2 = 0; uint8_t fileNameLen = 0; char filename[FILE_PATH_SIZE] = {0}; char tempStr[50] = {0}; @@ -1370,7 +1374,7 @@ static int CmdHFiClassDump(const char *Cmd) { // if no debit key given try credit key on AA1 (not for iclass but for some picopass this will work) if (!have_debit_key && have_credit_key) use_credit_key = true; - uint32_t flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE); + uint32_t flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE | FLAG_ICLASS_READER_AIA); //get config and first 3 blocks PacketResponseNG resp; @@ -1395,8 +1399,8 @@ static int CmdHFiClassDump(const char *Cmd) { return PM3_ESOFT; } - if (readStatus & (FLAG_ICLASS_CSN | FLAG_ICLASS_CONF | FLAG_ICLASS_CC)) { - memcpy(tag_data, data, 8 * 3); + if (readStatus & (FLAG_ICLASS_CSN | FLAG_ICLASS_CONF | FLAG_ICLASS_CC | FLAG_ICLASS_AIA)) { + memcpy(tag_data, data, 8 * 6); uint8_t type = get_mem_config(hdr); app_limit1 = hdr->conf.app_limit; @@ -1418,7 +1422,7 @@ static int CmdHFiClassDump(const char *Cmd) { uint8_t start_blockno; uint8_t numblks; } PACKED payload; - payload.start_blockno = 5; + payload.start_blockno = 6; payload.numblks = app_limit1 - 5; clearCommandBuffer(); @@ -1443,7 +1447,7 @@ static int CmdHFiClassDump(const char *Cmd) { return resp.status; } - // dump cmd switch off at device when finised. + // dump cmd switch off at device when finished. struct p_resp { bool isOK; @@ -1452,30 +1456,26 @@ static int CmdHFiClassDump(const char *Cmd) { } PACKED; struct p_resp *packet = (struct p_resp *)resp.data.asBytes; - uint32_t blocks_read = packet->block_cnt; - if (packet->isOK == false) { PrintAndLogEx(WARNING, "read block failed"); return PM3_ESOFT; } - uint16_t offset = (5 * 8); - uint32_t startindex = packet->bb_offset; - - if (blocks_read * 8 > sizeof(tag_data) - offset) { - PrintAndLogEx(FAILED, "data exceeded buffer size!"); - blocks_read = (sizeof(tag_data) / 8) - 5; + uint32_t blocks_read = packet->block_cnt; + if (blocks_read == app_limit1 - 5) { + PrintAndLogEx(INFO, "ICE: got all AA1"); } + uint16_t offset = (6 * 8); + uint32_t startindex = packet->bb_offset; + // response ok - now get bigbuf content of the dump if (!GetFromDevice(BIG_BUF, tag_data + offset, blocks_read * 8, startindex, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } - - PrintAndLogEx(INFO, "BB start index :: %u", startindex); - PrintAndLogEx(INFO, "BB :: %s", sprint_hex(tag_data + (5*8), 32)); + PrintAndLogEx(INFO, "ICE: blocks_read (13) == %u (0x%02x)", blocks_read, blocks_read); offset += (blocks_read * 8); // try AA2 Kc, Credit @@ -1491,12 +1491,12 @@ static int CmdHFiClassDump(const char *Cmd) { } payload.start_blockno = app_limit1; - payload.numblks = app_limit2 - app_limit1 - 5; + payload.numblks = app_limit2 - app_limit1; clearCommandBuffer(); SendCommandNG(CMD_HF_ICLASS_DUMP, (uint8_t*)&payload, sizeof(payload)); - if (!WaitForResponseTimeout(CMD_HF_ICLASS_DUMP, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_HF_ICLASS_DUMP, &resp, 2000) == false) { PrintAndLogEx(WARNING, "command execute timeout 2"); return PM3_ETIMEOUT; } @@ -1511,10 +1511,13 @@ static int CmdHFiClassDump(const char *Cmd) { PrintAndLogEx(WARNING, "read block failed using credit key"); return PM3_ESOFT; } - // blocks_read = packet->block_cnt; startindex = packet->bb_offset; + + if (blocks_read == app_limit2 - app_limit1) { + PrintAndLogEx(INFO, "ICE: got all AA2"); + } if (blocks_read * 8 > sizeof(tag_data) - offset) { PrintAndLogEx(FAILED, "data exceeded buffer size!"); From 9eb479fc7f0bbd37f23be73bafd4b9630827c555 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 20 Jul 2020 00:16:16 +0200 Subject: [PATCH 088/139] iceclass calc diversified key if not present --- armsrc/Standalone/hf_iceclass.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/armsrc/Standalone/hf_iceclass.c b/armsrc/Standalone/hf_iceclass.c index 19aa9626d..d8df74773 100644 --- a/armsrc/Standalone/hf_iceclass.c +++ b/armsrc/Standalone/hf_iceclass.c @@ -70,6 +70,7 @@ uint8_t card_app2_limit[] = { }; static uint8_t aa2_key[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + static uint8_t legacy_aa1_key[] = {0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78}; static uint8_t csns[8 * NUM_CSNS] = { @@ -145,15 +146,22 @@ static int fullsim_mode(void) { Dbprintf("loaded '" _YELLOW_(HF_ICLASS_FULLSIM_ORIG_BIN) "' (%u bytes) to emulator memory", fsize); } - // create diversified key if not in dump. + // create diversified key AA1/KD if not in dump. if ( memcmp(emul + (3 * 8), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8) == 0) { uint8_t ccnr[12] = {0}; memcpy(ccnr, emul + (2 * 8), 8); bool use_elite = false; - iclass_calc_div_key(emul, legacy_aa1_key, emul + (3 * 8), use_elite); } + // create diversified key AA2/KC if not in dump. + if ( memcmp(emul + (4 * 8), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8) == 0) { + uint8_t ccnr[12] = {0}; + memcpy(ccnr, emul + (2 * 8), 8); + bool use_elite = false; + iclass_calc_div_key(emul, aa2_key, emul + (4 * 8), use_elite); + } + iclass_simulate(ICLASS_SIM_MODE_FULL, 0 , false, NULL, NULL, NULL); LED_B_ON(); @@ -298,23 +306,21 @@ static int reader_dump_mode(void) { static int config_sim_mode(void) { uint8_t *emul = BigBuf_get_EM_addr(); - + for (uint8_t i = 0; i < 2; i++) { SpinOff(0); - uint32_t fsize = size_in_spiffs(cc_files[i]); - + rdv40_spiffs_lazy_mount(); + uint32_t fsize = size_in_spiffs(cc_files[i]); int res = rdv40_spiffs_read_as_filetype(cc_files[i], emul, fsize, RDV40_SPIFFS_SAFETY_SAFE); rdv40_spiffs_lazy_unmount(); if (res == SPIFFS_OK) { Dbprintf("loaded '" _YELLOW_("%s") "' (%u bytes) to emulator memory", cc_files[i], fsize); + iclass_simulate(ICLASS_SIM_MODE_FULL, 0 , false, NULL, NULL, NULL); } - - iclass_simulate(ICLASS_SIM_MODE_FULL, 0 , false, NULL, NULL, NULL); } - rdv40_spiffs_lazy_unmount(); return PM3_SUCCESS; } @@ -330,7 +336,7 @@ void RunMod(void) { StandAloneMode(); Dbprintf(_YELLOW_("HF iCLASS mode a.k.a iceCLASS started")); - uint8_t mode = ICE_STATE_ATTACK; + uint8_t mode = ICE_STATE_FULLSIM; for (;;) { From 2d368ba2d8264db7e7e9335b1fdb8f8d3a701efe Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 20 Jul 2020 00:52:46 +0200 Subject: [PATCH 089/139] fix sim reply --- client/src/cmdhficlass.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 614555cb0..29c5eee98 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -649,9 +649,9 @@ static int CmdHFiClassSim(const char *Cmd) { PrintAndLogEx(INFO, "press " _YELLOW_("`enter`") " to cancel"); PacketResponseNG resp; clearCommandBuffer(); - SendCommandMIX(CMD_HF_ICLASS_SIMULATE, sim_type, NUM_CSNS, 0, csns, 8 * NUM_CSNS); + SendCommandMIX(CMD_HF_ICLASS_SIMULATE, sim_type, NUM_CSNS, 1, csns, 8 * NUM_CSNS); - while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + while (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { tries++; if (kbd_enter_pressed()) { PrintAndLogEx(WARNING, "\naborted via keyboard."); @@ -698,9 +698,9 @@ static int CmdHFiClassSim(const char *Cmd) { PrintAndLogEx(INFO, "press Enter to cancel"); PacketResponseNG resp; clearCommandBuffer(); - SendCommandMIX(CMD_HF_ICLASS_SIMULATE, sim_type, NUM_CSNS, 0, csns, 8 * NUM_CSNS); + SendCommandMIX(CMD_HF_ICLASS_SIMULATE, sim_type, NUM_CSNS, 1, csns, 8 * NUM_CSNS); - while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + while (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { tries++; if (kbd_enter_pressed()) { PrintAndLogEx(WARNING, "\naborted via keyboard."); @@ -1457,10 +1457,11 @@ static int CmdHFiClassDump(const char *Cmd) { struct p_resp *packet = (struct p_resp *)resp.data.asBytes; if (packet->isOK == false) { - PrintAndLogEx(WARNING, "read block failed"); + PrintAndLogEx(WARNING, "read AA1 blocks failed"); return PM3_ESOFT; } + // 13 uint32_t blocks_read = packet->block_cnt; if (blocks_read == app_limit1 - 5) { PrintAndLogEx(INFO, "ICE: got all AA1"); From 0f5e58a81c07554fb74375450b1333e882b4671e Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 20 Jul 2020 17:42:10 +0200 Subject: [PATCH 090/139] fix iclass sof tracelog --- client/src/cmdtrace.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index f2b20ab68..750bc9953 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -275,13 +275,12 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr if (data_len == 0) { if (protocol == ICLASS && duration == 2048) { - sprintf(line[0], " "); + sprintf(line[0], ""); } else if (protocol == ISO_15693 && duration == 512) { - sprintf(line[0], " "); + sprintf(line[0], ""); } else { sprintf(line[0], ""); } - return tracepos; } for (int j = 0; j < data_len && j / 18 < 18; j++) { From e94cbd8da58a9e2d41fb609e06282c72277a0d54 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 21 Jul 2020 10:48:29 +0200 Subject: [PATCH 091/139] list 15693 --- client/src/cmdhflist.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhflist.c b/client/src/cmdhflist.c index 8ade2f7dc..202e5b41d 100644 --- a/client/src/cmdhflist.c +++ b/client/src/cmdhflist.c @@ -507,8 +507,10 @@ void annotateIso15693(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { break; } - if (cmd[1] >= 0x2D && cmd[1] <= 0x9F) snprintf(exp, size, "Optional RFU"); - else if (cmd[1] >= 0xA0 && cmd[1] <= 0xDF) snprintf(exp, size, "Cust IC MFG dependent"); + if (cmd[1] > ISO15693_STAYQUIET && cmd[1] < ISO15693_READBLOCK) snprintf(exp, size, "Mandatory RFU"); + else if (cmd[1] > ISO15693_READ_MULTI_SECSTATUS && cmd[1] <= 0x9F) snprintf(exp, size, "Optional RFU"); +// else if (cmd[1] >= 0xA0 && cmd[1] <= 0xDF) snprintf(exp, size, "Cust IC MFG dependent"); + else if (cmd[1] > ISO15693_READ_SIGNATURE && cmd[1] <= 0xDF) snprintf(exp, size, "Cust IC MFG dependent"); else if (cmd[1] >= 0xE0) snprintf(exp, size, "Proprietary IC MFG dependent"); else snprintf(exp, size, "?"); From df404a652a4a1294249400fc4b9410d555810907 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 21 Jul 2020 10:49:29 +0200 Subject: [PATCH 092/139] duration isnt 15bits, its 16. length is 15bits. --- armsrc/BigBuf.c | 17 +++++++++++++---- armsrc/BigBuf.h | 4 ++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 338206d6e..608c38040 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -221,17 +221,17 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ duration = (UINT32_MAX - timestamp_start) + timestamp_end; } - if (duration > 0x7FFF) { + if (duration > 0xFFFF) { /* if (DBGLEVEL >= DBG_DEBUG) { - Dbprintf("Error in LogTrace: duration too long for 15 bits encoding: 0x%08x start: 0x%08x end: 0x%08x", duration, timestamp_start, timestamp_end); + Dbprintf("Error in LogTrace: duration too long for 16 bits encoding: 0x%08x start: 0x%08x end: 0x%08x", duration, timestamp_start, timestamp_end); } */ - duration /= 32; + duration = 0; } hdr->timestamp = timestamp_start; - hdr->duration = duration & 0x7FFF; + hdr->duration = duration & 0xFFFF; hdr->data_len = iLen; hdr->isResponse = !readerToTag; trace_len += TRACELOG_HDR_LEN; @@ -254,6 +254,15 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ return true; } +// specific LogTrace function for ISO15693: the duration needs to be scaled because otherwise it won't fit into a uint16_t +bool LogTrace_ISO15693(const uint8_t *bytes, uint16_t len, uint32_t ts_start, uint32_t ts_end, uint8_t *parity, bool reader2tag) { + uint32_t duration = ts_end - ts_start; + duration /= 32; + ts_end = ts_start + duration; + return LogTrace(bytes, len, ts_start, ts_end, parity, reader2tag); +} + + // Emulator memory uint8_t emlSet(uint8_t *data, uint32_t offset, uint32_t length) { uint8_t *mem = BigBuf_get_EM_addr(); diff --git a/armsrc/BigBuf.h b/armsrc/BigBuf.h index 2f381f36b..8b570cb8c 100644 --- a/armsrc/BigBuf.h +++ b/armsrc/BigBuf.h @@ -42,7 +42,11 @@ void clear_trace(void); void set_tracing(bool enable); void set_tracelen(uint32_t value); bool get_tracing(void); + bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag); +bool LogTrace_ISO15693(const uint8_t *bytes, uint16_t len, uint32_t ts_start, uint32_t ts_end, uint8_t *parity, bool reader2tag); + + uint8_t emlSet(uint8_t *data, uint32_t offset, uint32_t length); From 42f21e8d658bf60077e260d60c0fe09c9eb63ec9 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 21 Jul 2020 10:50:23 +0200 Subject: [PATCH 093/139] adapt iclass sim --- armsrc/iclass.c | 65 ++++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index fa161b813..16988f74e 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -393,7 +393,7 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { int trace_data_size = 0; // Respond SOF -- takes 1 bytes - uint8_t *resp_sof = BigBuf_malloc(1); + uint8_t *resp_sof = BigBuf_malloc(2); int resp_sof_len; // Anticollision CSN (rotated CSN) @@ -524,6 +524,32 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { } goto send; + } else if (cmd == ICLASS_CMD_SELECT && len == 9) { + // Reader selects anticollision CSN. + // Tag sends the corresponding real CSN + if (chip_state == ACTIVATED || chip_state == SELECTED) { + if (!memcmp(receivedCmd + 1, anticoll_data, 8)) { + modulated_response = resp_csn; + modulated_response_size = resp_csn_len; + trace_data = csn_data; + trace_data_size = sizeof(csn_data); + chip_state = SELECTED; + } else { + chip_state = IDLE; + } + } else if (chip_state == HALTED) { + // RESELECT with CSN + if (!memcmp(receivedCmd + 1, csn_data, 8)) { + modulated_response = resp_csn; + modulated_response_size = resp_csn_len; + trace_data = csn_data; + trace_data_size = sizeof(csn_data); + chip_state = SELECTED; + } + } + goto send; + + } else if (cmd == ICLASS_CMD_READ_OR_IDENTIFY && len == 4) { // 0x0C if (chip_state != SELECTED) { @@ -580,38 +606,14 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { trace_data = data_generic_trace; trace_data_size = 10; CodeIso15693AsTag(trace_data, trace_data_size); - memcpy(modulated_response, ts->buf, ts->max); + memcpy(data_response, ts->buf, ts->max); + modulated_response = data_response; modulated_response_size = ts->max; } goto send; } } // swith - } else if (cmd == ICLASS_CMD_SELECT && len == 9) { // 0x81 - // Reader selects anticollission CSN. - // Tag sends the corresponding real CSN - if (chip_state == ACTIVATED || chip_state == SELECTED) { - if (!memcmp(receivedCmd + 1, anticoll_data, 8)) { - modulated_response = resp_csn; - modulated_response_size = resp_csn_len; - trace_data = csn_data; - trace_data_size = sizeof(csn_data); - chip_state = SELECTED; - } else { - chip_state = IDLE; - } - } else if (chip_state == HALTED) { - // RESELECT with CSN - if (!memcmp(receivedCmd + 1, csn_data, 8)) { - modulated_response = resp_csn; - modulated_response_size = resp_csn_len; - trace_data = csn_data; - trace_data_size = sizeof(csn_data); - chip_state = SELECTED; - } - } - goto send; - } else if (cmd == ICLASS_CMD_READCHECK) { // 0x88 // Read e-purse KD (88 02) KC (18 02) if (chip_state != SELECTED) { @@ -701,11 +703,12 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { trace_data = data_generic_trace; trace_data_size = 34; CodeIso15693AsTag(trace_data, trace_data_size); - memcpy(modulated_response, ts->buf, ts->max); + memcpy(data_response, ts->buf, ts->max); + modulated_response = data_response; modulated_response_size = ts->max; goto send; - } else if (simulationMode == ICLASS_SIM_MODE_FULL && cmd == ICLASS_CMD_UPDATE && (len == 12 || len == 14)) { + } else if (cmd == ICLASS_CMD_UPDATE && (len == 12 || len == 14)) { // We're expected to respond with the data+crc, exactly what's already in the receivedCmd // receivedCmd is now UPDATE 1b | ADDRESS 1b | DATA 8b | Signature 4b or CRC 2b @@ -810,7 +813,7 @@ send: if (modulated_response_size > 0) { uint32_t response_time = reader_eof_time + DELAY_ICLASS_VCD_TO_VICC_SIM; TransmitTo15693Reader(modulated_response, modulated_response_size, &response_time, 0, false); - LogTrace(trace_data, trace_data_size, response_time * 32, (response_time * 32) + (modulated_response_size * 32 * 64), NULL, false); + LogTrace_ISO15693(trace_data, trace_data_size, response_time * 32, (response_time * 32) + (modulated_response_size * 32 * 64), NULL, false); } } @@ -829,7 +832,7 @@ static void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time, tosend_t *ts = get_tosend(); TransmitTo15693Tag(ts->buf, ts->max, start_time); *end_time = *start_time + (32 * ((8 * ts->max) - 4)); // substract the 4 padding bits after EOF - LogTrace(frame, len, (*start_time * 4), (*end_time * 4), NULL, true); + LogTrace_ISO15693(frame, len, (*start_time * 4), (*end_time * 4), NULL, true); } static bool iclass_send_cmd_with_retries(uint8_t* cmd, size_t cmdsize, uint8_t* resp, size_t max_resp_size, From c51565fd7cae3dd96d3db06cc29f847361275753 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 21 Jul 2020 10:51:48 +0200 Subject: [PATCH 094/139] iso15 sim, make decoders dynamic bigbuff malloc --- armsrc/iso15693.c | 797 ++++++++++++++++++++++++++-------------------- armsrc/iso15693.h | 2 +- 2 files changed, 444 insertions(+), 355 deletions(-) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index df576d3b3..379d094af 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -106,14 +106,22 @@ static void BuildIdentifyRequest(uint8_t *cmd); -// --------------------------- +// --------------------------- + // Signal Processing // --------------------------- // prepare data using "1 out of 4" code for later transmission // resulting data rate is 26.48 kbit/s (fc/512) // cmd ... data -// n ... length of data +// n ... length of data +static uint8_t encode15_lut[] = { + 0x40, // 01000000 + 0x10, // 00010000 + 0x04, // 00000100 + 0x01 // 00000001 +}; + void CodeIso15693AsReader(uint8_t *cmd, int n) { tosend_reset(); @@ -124,23 +132,18 @@ void CodeIso15693AsReader(uint8_t *cmd, int n) { // data for (int i = 0; i < n; i++) { - for (int j = 0; j < 8; j += 2) { - uint8_t these = (cmd[i] >> j) & 0x03; - switch(these) { - case 0: - ts->buf[++ts->max] = 0x40; //01000000 - break; - case 1: - ts->buf[++ts->max] = 0x10; //00010000 - break; - case 2: - ts->buf[++ts->max] = 0x04; //00000100 - break; - case 3: - ts->buf[++ts->max] = 0x01; //00000001 - break; - } - } + + volatile uint8_t b = (cmd[i] >> 0) & 0x03; + ts->buf[++ts->max] = encode15_lut[b]; + + b = (cmd[i] >> 2) & 0x03; + ts->buf[++ts->max] = encode15_lut[b]; + + b = (cmd[i] >> 4) & 0x03; + ts->buf[++ts->max] = encode15_lut[b]; + + b = (cmd[i] >> 6) & 0x03; + ts->buf[++ts->max] = encode15_lut[b]; } // EOF @@ -234,7 +237,6 @@ void CodeIso15693AsTag(uint8_t *cmd, size_t len) { // EOF ts->buf[++ts->max] = 0xB8; // 10111000 - ts->max++; } @@ -348,7 +350,7 @@ void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, #define NOISE_THRESHOLD 80 // don't try to correlate noise #define MAX_PREVIOUS_AMPLITUDE (-1 - NOISE_THRESHOLD) -typedef struct DecodeTag { +typedef struct { enum { STATE_TAG_SOF_LOW, STATE_TAG_SOF_RISING_EDGE, @@ -380,222 +382,211 @@ typedef struct DecodeTag { //----------------------------------------------------------------------------- // DEMODULATE tag answer //----------------------------------------------------------------------------- -static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *DecodeTag) { - switch (DecodeTag->state) { +static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *tag) { + + switch (tag->state) { case STATE_TAG_SOF_LOW: { // waiting for a rising edge - if (amplitude > NOISE_THRESHOLD + DecodeTag->previous_amplitude) { - if (DecodeTag->posCount > 10) { - DecodeTag->threshold_sof = amplitude - DecodeTag->previous_amplitude; // to be divided by 2 - DecodeTag->threshold_half = 0; - DecodeTag->state = STATE_TAG_SOF_RISING_EDGE; + if (amplitude > NOISE_THRESHOLD + tag->previous_amplitude) { + if (tag->posCount > 10) { + tag->threshold_sof = amplitude - tag->previous_amplitude; // to be divided by 2 + tag->threshold_half = 0; + tag->state = STATE_TAG_SOF_RISING_EDGE; } else { - DecodeTag->posCount = 0; + tag->posCount = 0; } } else { - DecodeTag->posCount++; - DecodeTag->previous_amplitude = amplitude; + tag->posCount++; + tag->previous_amplitude = amplitude; } break; } case STATE_TAG_SOF_RISING_EDGE: { - if (amplitude > DecodeTag->threshold_sof + DecodeTag->previous_amplitude) { // edge still rising - if (amplitude > DecodeTag->threshold_sof + DecodeTag->threshold_sof) { // steeper edge, take this as time reference - DecodeTag->posCount = 1; + if (amplitude > tag->threshold_sof + tag->previous_amplitude) { // edge still rising + if (amplitude > tag->threshold_sof + tag->threshold_sof) { // steeper edge, take this as time reference + tag->posCount = 1; } else { - DecodeTag->posCount = 2; + tag->posCount = 2; } - DecodeTag->threshold_sof = (amplitude - DecodeTag->previous_amplitude) / 2; + tag->threshold_sof = (amplitude - tag->previous_amplitude) / 2; } else { - DecodeTag->posCount = 2; - DecodeTag->threshold_sof = DecodeTag->threshold_sof / 2; + tag->posCount = 2; + tag->threshold_sof = tag->threshold_sof / 2; } - // DecodeTag->posCount = 2; - DecodeTag->state = STATE_TAG_SOF_HIGH; + tag->state = STATE_TAG_SOF_HIGH; break; } case STATE_TAG_SOF_HIGH: { // waiting for 10 times high. Take average over the last 8 - if (amplitude > DecodeTag->threshold_sof) { - DecodeTag->posCount++; - if (DecodeTag->posCount > 2) { - DecodeTag->threshold_half += amplitude; // keep track of average high value + if (amplitude > tag->threshold_sof) { + tag->posCount++; + if (tag->posCount > 2) { + tag->threshold_half += amplitude; // keep track of average high value } - if (DecodeTag->posCount == 10) { - DecodeTag->threshold_half >>= 2; // (4 times 1/2 average) - DecodeTag->state = STATE_TAG_SOF_HIGH_END; + if (tag->posCount == 10) { + tag->threshold_half >>= 2; // (4 times 1/2 average) + tag->state = STATE_TAG_SOF_HIGH_END; } } else { // high phase was too short - DecodeTag->posCount = 1; - DecodeTag->previous_amplitude = amplitude; - DecodeTag->state = STATE_TAG_SOF_LOW; + tag->posCount = 1; + tag->previous_amplitude = amplitude; + tag->state = STATE_TAG_SOF_LOW; } break; } case STATE_TAG_SOF_HIGH_END: { // check for falling edge - if (DecodeTag->posCount == 13 && amplitude < DecodeTag->threshold_sof) { - DecodeTag->lastBit = SOF_PART1; // detected 1st part of SOF (12 samples low and 12 samples high) - DecodeTag->shiftReg = 0; - DecodeTag->bitCount = 0; - DecodeTag->len = 0; - DecodeTag->sum1 = amplitude; - DecodeTag->sum2 = 0; - DecodeTag->posCount = 2; - DecodeTag->state = STATE_TAG_RECEIVING_DATA; - // FpgaDisableTracing(); // DEBUGGING - // Dbprintf("amplitude = %d, threshold_sof = %d, threshold_half/4 = %d, previous_amplitude = %d", - // amplitude, - // DecodeTag->threshold_sof, - // DecodeTag->threshold_half/4, - // DecodeTag->previous_amplitude); // DEBUGGING + if (tag->posCount == 13 && amplitude < tag->threshold_sof) { + tag->lastBit = SOF_PART1; // detected 1st part of SOF (12 samples low and 12 samples high) + tag->shiftReg = 0; + tag->bitCount = 0; + tag->len = 0; + tag->sum1 = amplitude; + tag->sum2 = 0; + tag->posCount = 2; + tag->state = STATE_TAG_RECEIVING_DATA; LED_C_ON(); } else { - DecodeTag->posCount++; - if (DecodeTag->posCount > 13) { // high phase too long - DecodeTag->posCount = 0; - DecodeTag->previous_amplitude = amplitude; - DecodeTag->state = STATE_TAG_SOF_LOW; + tag->posCount++; + if (tag->posCount > 13) { // high phase too long + tag->posCount = 0; + tag->previous_amplitude = amplitude; + tag->state = STATE_TAG_SOF_LOW; LED_C_OFF(); } } break; } case STATE_TAG_RECEIVING_DATA: { - // FpgaDisableTracing(); // DEBUGGING - // Dbprintf("amplitude = %d, threshold_sof = %d, threshold_half/4 = %d, previous_amplitude = %d", - // amplitude, - // DecodeTag->threshold_sof, - // DecodeTag->threshold_half/4, - // DecodeTag->previous_amplitude); // DEBUGGING - if (DecodeTag->posCount == 1) { - DecodeTag->sum1 = 0; - DecodeTag->sum2 = 0; + if (tag->posCount == 1) { + tag->sum1 = 0; + tag->sum2 = 0; } - if (DecodeTag->posCount <= 4) { - DecodeTag->sum1 += amplitude; + if (tag->posCount <= 4) { + tag->sum1 += amplitude; } else { - DecodeTag->sum2 += amplitude; + tag->sum2 += amplitude; } - if (DecodeTag->posCount == 8) { - if (DecodeTag->sum1 > DecodeTag->threshold_half && DecodeTag->sum2 > DecodeTag->threshold_half) { // modulation in both halves - if (DecodeTag->lastBit == LOGIC0) { // this was already part of EOF - DecodeTag->state = STATE_TAG_EOF; + if (tag->posCount == 8) { + if (tag->sum1 > tag->threshold_half && tag->sum2 > tag->threshold_half) { // modulation in both halves + if (tag->lastBit == LOGIC0) { // this was already part of EOF + tag->state = STATE_TAG_EOF; } else { - DecodeTag->posCount = 0; - DecodeTag->previous_amplitude = amplitude; - DecodeTag->state = STATE_TAG_SOF_LOW; + tag->posCount = 0; + tag->previous_amplitude = amplitude; + tag->state = STATE_TAG_SOF_LOW; LED_C_OFF(); } - } else if (DecodeTag->sum1 < DecodeTag->threshold_half && DecodeTag->sum2 > DecodeTag->threshold_half) { // modulation in second half + } else if (tag->sum1 < tag->threshold_half && tag->sum2 > tag->threshold_half) { // modulation in second half // logic 1 - if (DecodeTag->lastBit == SOF_PART1) { // still part of SOF - DecodeTag->lastBit = SOF_PART2; // SOF completed + if (tag->lastBit == SOF_PART1) { // still part of SOF + tag->lastBit = SOF_PART2; // SOF completed } else { - DecodeTag->lastBit = LOGIC1; - DecodeTag->shiftReg >>= 1; - DecodeTag->shiftReg |= 0x80; - DecodeTag->bitCount++; - if (DecodeTag->bitCount == 8) { - DecodeTag->output[DecodeTag->len] = DecodeTag->shiftReg; - DecodeTag->len++; - // if (DecodeTag->shiftReg == 0x12 && DecodeTag->len == 1) FpgaDisableTracing(); // DEBUGGING - if (DecodeTag->len > DecodeTag->max_len) { + tag->lastBit = LOGIC1; + tag->shiftReg >>= 1; + tag->shiftReg |= 0x80; + tag->bitCount++; + if (tag->bitCount == 8) { + tag->output[tag->len] = tag->shiftReg; + tag->len++; + + if (tag->len > tag->max_len) { // buffer overflow, give up LED_C_OFF(); return true; } - DecodeTag->bitCount = 0; - DecodeTag->shiftReg = 0; + tag->bitCount = 0; + tag->shiftReg = 0; } } - } else if (DecodeTag->sum1 > DecodeTag->threshold_half && DecodeTag->sum2 < DecodeTag->threshold_half) { // modulation in first half + } else if (tag->sum1 > tag->threshold_half && tag->sum2 < tag->threshold_half) { // modulation in first half // logic 0 - if (DecodeTag->lastBit == SOF_PART1) { // incomplete SOF - DecodeTag->posCount = 0; - DecodeTag->previous_amplitude = amplitude; - DecodeTag->state = STATE_TAG_SOF_LOW; + if (tag->lastBit == SOF_PART1) { // incomplete SOF + tag->posCount = 0; + tag->previous_amplitude = amplitude; + tag->state = STATE_TAG_SOF_LOW; LED_C_OFF(); } else { - DecodeTag->lastBit = LOGIC0; - DecodeTag->shiftReg >>= 1; - DecodeTag->bitCount++; - if (DecodeTag->bitCount == 8) { - DecodeTag->output[DecodeTag->len] = DecodeTag->shiftReg; - DecodeTag->len++; - // if (DecodeTag->shiftReg == 0x12 && DecodeTag->len == 1) FpgaDisableTracing(); // DEBUGGING - if (DecodeTag->len > DecodeTag->max_len) { + tag->lastBit = LOGIC0; + tag->shiftReg >>= 1; + tag->bitCount++; + + if (tag->bitCount == 8) { + tag->output[tag->len] = tag->shiftReg; + tag->len++; + + if (tag->len > tag->max_len) { // buffer overflow, give up - DecodeTag->posCount = 0; - DecodeTag->previous_amplitude = amplitude; - DecodeTag->state = STATE_TAG_SOF_LOW; + tag->posCount = 0; + tag->previous_amplitude = amplitude; + tag->state = STATE_TAG_SOF_LOW; LED_C_OFF(); } - DecodeTag->bitCount = 0; - DecodeTag->shiftReg = 0; + tag->bitCount = 0; + tag->shiftReg = 0; } } } else { // no modulation - if (DecodeTag->lastBit == SOF_PART2) { // only SOF (this is OK for iClass) + if (tag->lastBit == SOF_PART2) { // only SOF (this is OK for iClass) LED_C_OFF(); return true; } else { - DecodeTag->posCount = 0; - DecodeTag->state = STATE_TAG_SOF_LOW; + tag->posCount = 0; + tag->state = STATE_TAG_SOF_LOW; LED_C_OFF(); } } - DecodeTag->posCount = 0; + tag->posCount = 0; } - DecodeTag->posCount++; + tag->posCount++; break; } case STATE_TAG_EOF: { - if (DecodeTag->posCount == 1) { - DecodeTag->sum1 = 0; - DecodeTag->sum2 = 0; + if (tag->posCount == 1) { + tag->sum1 = 0; + tag->sum2 = 0; } - if (DecodeTag->posCount <= 4) { - DecodeTag->sum1 += amplitude; + if (tag->posCount <= 4) { + tag->sum1 += amplitude; } else { - DecodeTag->sum2 += amplitude; + tag->sum2 += amplitude; } - if (DecodeTag->posCount == 8) { - if (DecodeTag->sum1 > DecodeTag->threshold_half && DecodeTag->sum2 < DecodeTag->threshold_half) { // modulation in first half - DecodeTag->posCount = 0; - DecodeTag->state = STATE_TAG_EOF_TAIL; + if (tag->posCount == 8) { + if (tag->sum1 > tag->threshold_half && tag->sum2 < tag->threshold_half) { // modulation in first half + tag->posCount = 0; + tag->state = STATE_TAG_EOF_TAIL; } else { - DecodeTag->posCount = 0; - DecodeTag->previous_amplitude = amplitude; - DecodeTag->state = STATE_TAG_SOF_LOW; + tag->posCount = 0; + tag->previous_amplitude = amplitude; + tag->state = STATE_TAG_SOF_LOW; LED_C_OFF(); } } - DecodeTag->posCount++; + tag->posCount++; break; } case STATE_TAG_EOF_TAIL: { - if (DecodeTag->posCount == 1) { - DecodeTag->sum1 = 0; - DecodeTag->sum2 = 0; + if (tag->posCount == 1) { + tag->sum1 = 0; + tag->sum2 = 0; } - if (DecodeTag->posCount <= 4) { - DecodeTag->sum1 += amplitude; + if (tag->posCount <= 4) { + tag->sum1 += amplitude; } else { - DecodeTag->sum2 += amplitude; + tag->sum2 += amplitude; } - if (DecodeTag->posCount == 8) { - if (DecodeTag->sum1 < DecodeTag->threshold_half && DecodeTag->sum2 < DecodeTag->threshold_half) { // no modulation in both halves + if (tag->posCount == 8) { + if (tag->sum1 < tag->threshold_half && tag->sum2 < tag->threshold_half) { // no modulation in both halves LED_C_OFF(); return true; } else { - DecodeTag->posCount = 0; - DecodeTag->previous_amplitude = amplitude; - DecodeTag->state = STATE_TAG_SOF_LOW; + tag->posCount = 0; + tag->previous_amplitude = amplitude; + tag->state = STATE_TAG_SOF_LOW; LED_C_OFF(); } } - DecodeTag->posCount++; + tag->posCount++; break; } } @@ -603,21 +594,18 @@ static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *De return false; } -static void DecodeTagInit(DecodeTag_t *DecodeTag, uint8_t *data, uint16_t max_len) { - DecodeTag->previous_amplitude = MAX_PREVIOUS_AMPLITUDE; - DecodeTag->posCount = 0; - DecodeTag->state = STATE_TAG_SOF_LOW; - DecodeTag->output = data; - DecodeTag->max_len = max_len; +static void DecodeTagReset(DecodeTag_t *tag) { + tag->posCount = 0; + tag->state = STATE_TAG_SOF_LOW; + tag->previous_amplitude = MAX_PREVIOUS_AMPLITUDE; } -static void DecodeTagReset(DecodeTag_t *DecodeTag) { - DecodeTag->posCount = 0; - DecodeTag->state = STATE_TAG_SOF_LOW; - DecodeTag->previous_amplitude = MAX_PREVIOUS_AMPLITUDE; +static void DecodeTagInit(DecodeTag_t *tag, uint8_t *data, uint16_t max_len) { + tag->output = data; + tag->max_len = max_len; + DecodeTagReset(tag); } - /* * Receive and decode the tag response, also log to tracebuffer */ @@ -641,14 +629,18 @@ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeo // The DMA buffer, used to stream samples from the FPGA dmabuf16_t *dma = get_dma16(); - FpgaSetupSscDma((uint8_t*) dma->buf, DMA_BUFFER_SIZE); + // Setup and start DMA. + if (FpgaSetupSscDma((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { + if (DBGLEVEL > DBG_ERROR) Dbprintf("FpgaSetupSscDma failed. Exiting"); + return -4; + } uint32_t dma_start_time = 0; uint16_t *upTo = dma->buf; for(;;) { - uint16_t behindBy = ((uint16_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); + volatile uint16_t behindBy = ((uint16_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); if (behindBy == 0) continue; @@ -693,7 +685,6 @@ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeo FpgaDisableSscDma(); - uint32_t sof_time = *eof_time - (DecodeTag.len * 8 * 8 * 16) // time for byte transfers - (32 * 16) // time for SOF transfer @@ -713,11 +704,11 @@ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeo Dbprintf("timing: sof_time = %d, eof_time = %d", (sof_time * 4), (*eof_time * 4)); } - if (ret == -1) { + if (ret < 0) { return ret; } - - LogTrace(DecodeTag.output, DecodeTag.len, (sof_time * 4), (*eof_time * 4), NULL, false); + + LogTrace_ISO15693(DecodeTag.output, DecodeTag.len, (sof_time * 4), (*eof_time * 4), NULL, false); return DecodeTag.len; } @@ -735,7 +726,7 @@ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeo // false if we are still waiting for some more //============================================================================= -typedef struct DecodeReader { +typedef struct { enum { STATE_READER_UNSYNCD, STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF, @@ -762,50 +753,50 @@ typedef struct DecodeReader { uint8_t *jam_search_string; } DecodeReader_t; -static void DecodeReaderInit(DecodeReader_t* DecodeReader, uint8_t *data, uint16_t max_len, uint8_t jam_search_len, uint8_t *jam_search_string) { - DecodeReader->output = data; - DecodeReader->byteCountMax = max_len; - DecodeReader->state = STATE_READER_UNSYNCD; - DecodeReader->byteCount = 0; - DecodeReader->bitCount = 0; - DecodeReader->posCount = 1; - DecodeReader->shiftReg = 0; - DecodeReader->jam_search_len = jam_search_len; - DecodeReader->jam_search_string = jam_search_string; +static void DecodeReaderInit(DecodeReader_t* reader, uint8_t *data, uint16_t max_len, uint8_t jam_search_len, uint8_t *jam_search_string) { + reader->output = data; + reader->byteCountMax = max_len; + reader->state = STATE_READER_UNSYNCD; + reader->byteCount = 0; + reader->bitCount = 0; + reader->posCount = 1; + reader->shiftReg = 0; + reader->jam_search_len = jam_search_len; + reader->jam_search_string = jam_search_string; } -static void DecodeReaderReset(DecodeReader_t* DecodeReader) { - DecodeReader->state = STATE_READER_UNSYNCD; +static void DecodeReaderReset(DecodeReader_t* reader) { + reader->state = STATE_READER_UNSYNCD; } -static RAMFUNC int Handle15693SampleFromReader(bool bit, DecodeReader_t *DecodeReader) { - switch (DecodeReader->state) { +static RAMFUNC int Handle15693SampleFromReader(bool bit, DecodeReader_t *reader) { + switch (reader->state) { case STATE_READER_UNSYNCD: // wait for unmodulated carrier if (bit) { - DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + reader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; } break; case STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF: if (!bit) { // we went low, so this could be the beginning of a SOF - DecodeReader->posCount = 1; - DecodeReader->state = STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF; + reader->posCount = 1; + reader->state = STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF; } break; case STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF: - DecodeReader->posCount++; + reader->posCount++; if (bit) { // detected rising edge - if (DecodeReader->posCount < 4) { // rising edge too early (nominally expected at 5) - DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + if (reader->posCount < 4) { // rising edge too early (nominally expected at 5) + reader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; } else { // SOF - DecodeReader->state = STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF; + reader->state = STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF; } } else { - if (DecodeReader->posCount > 5) { // stayed low for too long - DecodeReaderReset(DecodeReader); + if (reader->posCount > 5) { // stayed low for too long + DecodeReaderReset(reader); } else { // do nothing, keep waiting } @@ -813,22 +804,26 @@ static RAMFUNC int Handle15693SampleFromReader(bool bit, DecodeReader_t *DecodeR break; case STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF: - DecodeReader->posCount++; - if (!bit) { // detected a falling edge - if (DecodeReader->posCount < 20) { // falling edge too early (nominally expected at 21 earliest) - DecodeReaderReset(DecodeReader); - } else if (DecodeReader->posCount < 23) { // SOF for 1 out of 4 coding - DecodeReader->Coding = CODING_1_OUT_OF_4; - DecodeReader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; - } else if (DecodeReader->posCount < 28) { // falling edge too early (nominally expected at 29 latest) - DecodeReaderReset(DecodeReader); + + reader->posCount++; + + if (bit == false) { // detected a falling edge + + if (reader->posCount < 20) { // falling edge too early (nominally expected at 21 earliest) + DecodeReaderReset(reader); + } else if (reader->posCount < 23) { // SOF for 1 out of 4 coding + reader->Coding = CODING_1_OUT_OF_4; + reader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; + } else if (reader->posCount < 28) { // falling edge too early (nominally expected at 29 latest) + DecodeReaderReset(reader); } else { // SOF for 1 out of 256 coding - DecodeReader->Coding = CODING_1_OUT_OF_256; - DecodeReader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; + reader->Coding = CODING_1_OUT_OF_256; + reader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; } + } else { - if (DecodeReader->posCount > 29) { // stayed high for too long - DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + if (reader->posCount > 29) { // stayed high for too long + reader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; } else { // do nothing, keep waiting } @@ -836,37 +831,39 @@ static RAMFUNC int Handle15693SampleFromReader(bool bit, DecodeReader_t *DecodeR break; case STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF: - DecodeReader->posCount++; + + reader->posCount++; + if (bit) { // detected rising edge - if (DecodeReader->Coding == CODING_1_OUT_OF_256) { - if (DecodeReader->posCount < 32) { // rising edge too early (nominally expected at 33) - DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + if (reader->Coding == CODING_1_OUT_OF_256) { + if (reader->posCount < 32) { // rising edge too early (nominally expected at 33) + reader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; } else { - DecodeReader->posCount = 1; - DecodeReader->bitCount = 0; - DecodeReader->byteCount = 0; - DecodeReader->sum1 = 1; - DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_256; + reader->posCount = 1; + reader->bitCount = 0; + reader->byteCount = 0; + reader->sum1 = 1; + reader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_256; LED_B_ON(); } } else { // CODING_1_OUT_OF_4 - if (DecodeReader->posCount < 24) { // rising edge too early (nominally expected at 25) - DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + if (reader->posCount < 24) { // rising edge too early (nominally expected at 25) + reader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; } else { - DecodeReader->posCount = 1; - DecodeReader->state = STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4; + reader->posCount = 1; + reader->state = STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4; } } } else { - if (DecodeReader->Coding == CODING_1_OUT_OF_256) { - if (DecodeReader->posCount > 34) { // signal stayed low for too long - DecodeReaderReset(DecodeReader); + if (reader->Coding == CODING_1_OUT_OF_256) { + if (reader->posCount > 34) { // signal stayed low for too long + DecodeReaderReset(reader); } else { // do nothing, keep waiting } } else { // CODING_1_OUT_OF_4 - if (DecodeReader->posCount > 26) { // signal stayed low for too long - DecodeReaderReset(DecodeReader); + if (reader->posCount > 26) { // signal stayed low for too long + DecodeReaderReset(reader); } else { // do nothing, keep waiting } @@ -875,136 +872,162 @@ static RAMFUNC int Handle15693SampleFromReader(bool bit, DecodeReader_t *DecodeR break; case STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4: - DecodeReader->posCount++; + + reader->posCount++; + if (bit) { - if (DecodeReader->posCount == 9) { - DecodeReader->posCount = 1; - DecodeReader->bitCount = 0; - DecodeReader->byteCount = 0; - DecodeReader->sum1 = 1; - DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_4; + if (reader->posCount == 9) { + reader->posCount = 1; + reader->bitCount = 0; + reader->byteCount = 0; + reader->sum1 = 1; + reader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_4; LED_B_ON(); } else { // do nothing, keep waiting } } else { // unexpected falling edge - DecodeReaderReset(DecodeReader); + DecodeReaderReset(reader); } break; case STATE_READER_RECEIVE_DATA_1_OUT_OF_4: - DecodeReader->posCount++; - if (DecodeReader->posCount == 1) { - DecodeReader->sum1 = bit ? 1 : 0; - } else if (DecodeReader->posCount <= 4) { - if (bit) DecodeReader->sum1++; - } else if (DecodeReader->posCount == 5) { - DecodeReader->sum2 = bit ? 1 : 0; + + reader->posCount++; + + if (reader->posCount == 1) { + + reader->sum1 = bit ? 1 : 0; + + } else if (reader->posCount <= 4) { + + if (bit) + reader->sum1++; + + } else if (reader->posCount == 5) { + + reader->sum2 = bit ? 1 : 0; + } else { - if (bit) DecodeReader->sum2++; + if (bit) + reader->sum2++; } - if (DecodeReader->posCount == 8) { - DecodeReader->posCount = 0; - if (DecodeReader->sum1 <= 1 && DecodeReader->sum2 >= 3) { // EOF + + if (reader->posCount == 8) { + reader->posCount = 0; + if (reader->sum1 <= 1 && reader->sum2 >= 3) { // EOF LED_B_OFF(); // Finished receiving - DecodeReaderReset(DecodeReader); - if (DecodeReader->byteCount != 0) { + DecodeReaderReset(reader); + if (reader->byteCount != 0) { return true; } - } else if (DecodeReader->sum1 >= 3 && DecodeReader->sum2 <= 1) { // detected a 2bit position - DecodeReader->shiftReg >>= 2; - DecodeReader->shiftReg |= (DecodeReader->bitCount << 6); + + } else if (reader->sum1 >= 3 && reader->sum2 <= 1) { // detected a 2bit position + reader->shiftReg >>= 2; + reader->shiftReg |= (reader->bitCount << 6); } - if (DecodeReader->bitCount == 15) { // we have a full byte - DecodeReader->output[DecodeReader->byteCount++] = DecodeReader->shiftReg; - if (DecodeReader->byteCount > DecodeReader->byteCountMax) { + + if (reader->bitCount == 15) { // we have a full byte + + reader->output[reader->byteCount++] = reader->shiftReg; + if (reader->byteCount > reader->byteCountMax) { // buffer overflow, give up LED_B_OFF(); - DecodeReaderReset(DecodeReader); + DecodeReaderReset(reader); } - DecodeReader->bitCount = 0; - DecodeReader->shiftReg = 0; - if (DecodeReader->byteCount == DecodeReader->jam_search_len) { - if (!memcmp(DecodeReader->output, DecodeReader->jam_search_string, DecodeReader->jam_search_len)) { + + reader->bitCount = 0; + reader->shiftReg = 0; + if (reader->byteCount == reader->jam_search_len) { + if (!memcmp(reader->output, reader->jam_search_string, reader->jam_search_len)) { LED_D_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_JAM); - DecodeReader->state = STATE_READER_RECEIVE_JAMMING; + reader->state = STATE_READER_RECEIVE_JAMMING; } } + } else { - DecodeReader->bitCount++; + reader->bitCount++; } } break; case STATE_READER_RECEIVE_DATA_1_OUT_OF_256: - DecodeReader->posCount++; - if (DecodeReader->posCount == 1) { - DecodeReader->sum1 = bit ? 1 : 0; - } else if (DecodeReader->posCount <= 4) { - if (bit) DecodeReader->sum1++; - } else if (DecodeReader->posCount == 5) { - DecodeReader->sum2 = bit ? 1 : 0; + + reader->posCount++; + + if (reader->posCount == 1) { + reader->sum1 = bit ? 1 : 0; + } else if (reader->posCount <= 4) { + if (bit) reader->sum1++; + } else if (reader->posCount == 5) { + reader->sum2 = bit ? 1 : 0; } else if (bit) { - DecodeReader->sum2++; + reader->sum2++; } - if (DecodeReader->posCount == 8) { - DecodeReader->posCount = 0; - if (DecodeReader->sum1 <= 1 && DecodeReader->sum2 >= 3) { // EOF + + if (reader->posCount == 8) { + reader->posCount = 0; + if (reader->sum1 <= 1 && reader->sum2 >= 3) { // EOF LED_B_OFF(); // Finished receiving - DecodeReaderReset(DecodeReader); - if (DecodeReader->byteCount != 0) { + DecodeReaderReset(reader); + if (reader->byteCount != 0) { return true; } - } else if (DecodeReader->sum1 >= 3 && DecodeReader->sum2 <= 1) { // detected the bit position - DecodeReader->shiftReg = DecodeReader->bitCount; + + } else if (reader->sum1 >= 3 && reader->sum2 <= 1) { // detected the bit position + reader->shiftReg = reader->bitCount; } - if (DecodeReader->bitCount == 255) { // we have a full byte - DecodeReader->output[DecodeReader->byteCount++] = DecodeReader->shiftReg; - if (DecodeReader->byteCount > DecodeReader->byteCountMax) { + + if (reader->bitCount == 255) { // we have a full byte + reader->output[reader->byteCount++] = reader->shiftReg; + if (reader->byteCount > reader->byteCountMax) { // buffer overflow, give up LED_B_OFF(); - DecodeReaderReset(DecodeReader); + DecodeReaderReset(reader); } - if (DecodeReader->byteCount == DecodeReader->jam_search_len) { - if (!memcmp(DecodeReader->output, DecodeReader->jam_search_string, DecodeReader->jam_search_len)) { + + if (reader->byteCount == reader->jam_search_len) { + if (!memcmp(reader->output, reader->jam_search_string, reader->jam_search_len)) { LED_D_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_JAM); - DecodeReader->state = STATE_READER_RECEIVE_JAMMING; + reader->state = STATE_READER_RECEIVE_JAMMING; } } } - DecodeReader->bitCount++; + reader->bitCount++; } break; case STATE_READER_RECEIVE_JAMMING: - DecodeReader->posCount++; - if (DecodeReader->Coding == CODING_1_OUT_OF_4) { - if (DecodeReader->posCount == 7 * 16) { // 7 bits jammed + + reader->posCount++; + + if (reader->Coding == CODING_1_OUT_OF_4) { + if (reader->posCount == 7 * 16) { // 7 bits jammed FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); // stop jamming // FpgaDisableTracing(); LED_D_OFF(); - } else if (DecodeReader->posCount == 8 * 16) { - DecodeReader->posCount = 0; - DecodeReader->output[DecodeReader->byteCount++] = 0x00; - DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_4; + } else if (reader->posCount == 8 * 16) { + reader->posCount = 0; + reader->output[reader->byteCount++] = 0x00; + reader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_4; } } else { - if (DecodeReader->posCount == 7 * 256) { // 7 bits jammend + if (reader->posCount == 7 * 256) { // 7 bits jammend FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); // stop jamming LED_D_OFF(); - } else if (DecodeReader->posCount == 8 * 256) { - DecodeReader->posCount = 0; - DecodeReader->output[DecodeReader->byteCount++] = 0x00; - DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_256; + } else if (reader->posCount == 8 * 256) { + reader->posCount = 0; + reader->output[reader->byteCount++] = 0x00; + reader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_256; } } break; default: LED_B_OFF(); - DecodeReaderReset(DecodeReader); + DecodeReaderReset(reader); break; } @@ -1040,17 +1063,25 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo (void) temp; while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) ; - uint32_t dma_start_time = GetCountSspClk() & 0xfffffff8; - // Setup and start DMA. dmabuf8_t *dma = get_dma8(); - FpgaSetupSscDma(dma->buf, DMA_BUFFER_SIZE); + if (FpgaSetupSscDma(dma->buf, DMA_BUFFER_SIZE) == false) { + if (DBGLEVEL > DBG_ERROR) Dbprintf("FpgaSetupSscDma failed. Exiting"); + return -4; + } uint8_t *upTo = dma->buf; - for (;;) { - uint16_t behindBy = ((uint8_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); +// uint32_t dma_start_time = GetCountSspClk() & 0xfffffff8; + uint32_t dma_start_time; + for (;;) { + volatile uint16_t behindBy = ((uint8_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); if (behindBy == 0) continue; + + if (samples == 0) { + // DMA has transferred the very first data + dma_start_time = GetCountSspClk() & 0xfffffff0; + } volatile uint8_t b = *upTo++; if (upTo >= dma->buf + DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. @@ -1074,16 +1105,16 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo samples++; } + if (gotFrame) { + break; + } + if (BUTTON_PRESS()) { DecodeReader.byteCount = -1; break; } WDT_HIT(); - - if (gotFrame) { - break; - } } FpgaDisableSscDma(); @@ -1094,12 +1125,12 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo DecodeReader.bitCount, DecodeReader.posCount); } - if (DecodeReader.byteCount > 0) { + if (DecodeReader.byteCount >= 0) { uint32_t sof_time = *eof_time - DecodeReader.byteCount * (DecodeReader.Coding == CODING_1_OUT_OF_4 ? 128 : 2048) // time for byte transfers - 32 // time for SOF transfer - 16; // time for EOF transfer - LogTrace(DecodeReader.output, DecodeReader.byteCount, (sof_time * 32), (*eof_time * 32), NULL, true); + LogTrace_ISO15693(DecodeReader.output, DecodeReader.byteCount, (sof_time * 32), (*eof_time * 32), NULL, true); } return DecodeReader.byteCount; @@ -1155,19 +1186,26 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + BigBuf_free(); clear_trace(); set_tracing(true); // Count of samples received so far, so that we can include timing int samples = 0; - DecodeTag_t DecodeTag = {0}; - uint8_t response[ISO15693_MAX_RESPONSE_LENGTH] = {0}; - DecodeTagInit(&DecodeTag, response, sizeof(response)); +// DecodeTag_t dtag = {0}; +// uint8_t response[ISO15693_MAX_RESPONSE_LENGTH] = {0}; +// DecodeTagInit(&dtag, response, sizeof(response)); + DecodeTag_t *dtag = (DecodeTag_t *)BigBuf_malloc(sizeof(DecodeTag_t)); + uint8_t *response = BigBuf_malloc(ISO15693_MAX_RESPONSE_LENGTH); + DecodeTagInit(dtag, response, ISO15693_MAX_RESPONSE_LENGTH); - DecodeReader_t DecodeReader = {0}; - uint8_t cmd[ISO15693_MAX_COMMAND_LENGTH] = {0}; - DecodeReaderInit(&DecodeReader, cmd, sizeof(cmd), jam_search_len, jam_search_string); +// DecodeReader_t dreader = {0}; +// uint8_t cmd[ISO15693_MAX_COMMAND_LENGTH] = {0}; +// DecodeReaderInit(&dreader, cmd, sizeof(cmd), jam_search_len, jam_search_string); + DecodeReader_t *dreader = (DecodeReader_t *)BigBuf_malloc(sizeof(DecodeReader_t)); + uint8_t *cmd = BigBuf_malloc(ISO15693_MAX_COMMAND_LENGTH); + DecodeReaderInit(dreader, cmd, ISO15693_MAX_COMMAND_LENGTH, jam_search_len, jam_search_string); // Print some debug information about the buffer sizes if (DBGLEVEL >= DBG_EXTENDED) { @@ -1178,7 +1216,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { Dbprintf(" DMA: %i bytes", DMA_BUFFER_SIZE * sizeof(uint16_t)); } - Dbprintf("Sniff started. Press PM3 Button to stop."); + Dbprintf("Starting to sniff. Press PM3 Button to stop."); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); LED_D_OFF(); @@ -1189,38 +1227,79 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { // The DMA buffer, used to stream samples from the FPGA dmabuf16_t *dma = get_dma16(); - FpgaSetupSscDma((uint8_t*)dma->buf, DMA_BUFFER_SIZE); uint16_t *upTo = dma->buf; + // Setup and start DMA. + if (FpgaSetupSscDma((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { + if (DBGLEVEL > DBG_ERROR) Dbprintf("FpgaSetupSscDma failed. Exiting"); + switch_off(); + return; + } + bool tag_is_active = false; bool reader_is_active = false; bool expect_tag_answer = false; - uint32_t dma_start_time = 0; - uint16_t max_behindBy = 0; + int dma_start_time = 0; + +// int max_data_len = 0, data_len; // And now we loop, receiving samples. for(;;) { - uint16_t behindBy = ((uint16_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); - if (behindBy > max_behindBy) { - max_behindBy = behindBy; - } - - if (behindBy == 0) continue; samples++; if (samples == 1) { // DMA has transferred the very first data dma_start_time = GetCountSspClk() & 0xfffffff0; } - uint16_t sniffdata = *upTo++; + /* if (upTo >= dma->buf + DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + + upTo = dma->buf; + + int register read_bufdata_p = upTo - dma->buf; + int register dma_buf_data_p = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR; + if (read_bufdata_p <= dma_buf_data_p) + data_len = dma_buf_data_p - read_bufdata_p; + else + data_len = DMA_BUFFER_SIZE - read_bufdata_p + dma_buf_data_p; + + // test for length of buffer + if (data_len > max_data_len) { + max_data_len = data_len; + if (data_len > (9 * DMA_BUFFER_SIZE / 10)) { + Dbprintf("[!] blew circular buffer! | datalen %u", data_len); + break; + } + } + + // primary buffer was stopped( <-- we lost data! + if (AT91C_BASE_PDC_SSC->PDC_RCR == false) { + AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dma->buf; + AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; + } + // secondary buffer sets as primary, secondary buffer was stopped + if (AT91C_BASE_PDC_SSC->PDC_RNCR == false) { + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; + } + + + } +*/ + + uint16_t behindBy = ((uint16_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); + if (behindBy == 0) continue; + + if (upTo >= dma->buf + DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dma->buf; // start reading the circular buffer from the beginning if (behindBy > (9 * DMA_BUFFER_SIZE / 10)) { Dbprintf("About to blow circular buffer - aborted! behindBy=%d, samples=%d", behindBy, samples); break; } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; // refresh the DMA Next Buffer and AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; // DMA Next Counter registers @@ -1232,87 +1311,97 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { } } +/* + WDT_HIT(); + if (BUTTON_PRESS()) { + DbpString("Sniff stopped."); + break; + } +*/ // no need to try decoding reader data if the tag is sending if (tag_is_active == false) { - if (Handle15693SampleFromReader(sniffdata & 0x02, &DecodeReader)) { + if (Handle15693SampleFromReader(sniffdata & 0x02, dreader)) { uint32_t eof_time = dma_start_time + (samples * 16) + 8 - DELAY_READER_TO_ARM_SNIFF; // end of EOF - if (DecodeReader.byteCount > 0) { + if (dreader->byteCount > 0) { uint32_t sof_time = eof_time - - DecodeReader.byteCount * (DecodeReader.Coding == CODING_1_OUT_OF_4 ? 128 * 16 : 2048 * 16) // time for byte transfers + - dreader->byteCount * (dreader->Coding == CODING_1_OUT_OF_4 ? 128 * 16 : 2048 * 16) // time for byte transfers - 32 * 16 // time for SOF transfer - 16 * 16; // time for EOF transfer - LogTrace(DecodeReader.output, DecodeReader.byteCount, (sof_time * 4), (eof_time * 4), NULL, true); + LogTrace_ISO15693(dreader->output, dreader->byteCount, (sof_time * 4), (eof_time * 4), NULL, true); } // And ready to receive another command. - DecodeReaderReset(&DecodeReader); + DecodeReaderReset(dreader); // And also reset the demod code, which might have been // false-triggered by the commands from the reader. - DecodeTagReset(&DecodeTag); + DecodeTagReset(dtag); reader_is_active = false; expect_tag_answer = true; - } else if (Handle15693SampleFromReader(sniffdata & 0x01, &DecodeReader)) { + } else if (Handle15693SampleFromReader(sniffdata & 0x01, dreader)) { uint32_t eof_time = dma_start_time + (samples * 16) + 16 - DELAY_READER_TO_ARM_SNIFF; // end of EOF - if (DecodeReader.byteCount > 0) { + if (dreader->byteCount > 0) { uint32_t sof_time = eof_time - - DecodeReader.byteCount * (DecodeReader.Coding == CODING_1_OUT_OF_4 ? 128 * 16 : 2048 * 16) // time for byte transfers + - dreader->byteCount * (dreader->Coding == CODING_1_OUT_OF_4 ? 128 * 16 : 2048 * 16) // time for byte transfers - 32 * 16 // time for SOF transfer - 16 * 16; // time for EOF transfer - LogTrace(DecodeReader.output, DecodeReader.byteCount, (sof_time * 4), (eof_time * 4), NULL, true); + LogTrace_ISO15693(dreader->output, dreader->byteCount, (sof_time * 4), (eof_time * 4), NULL, true); } // And ready to receive another command - DecodeReaderReset(&DecodeReader); + DecodeReaderReset(dreader); // And also reset the demod code, which might have been // false-triggered by the commands from the reader. - DecodeTagReset(&DecodeTag); + DecodeTagReset(dtag); reader_is_active = false; expect_tag_answer = true; } else { - reader_is_active = (DecodeReader.state >= STATE_READER_RECEIVE_DATA_1_OUT_OF_4); + reader_is_active = (dreader->state >= STATE_READER_RECEIVE_DATA_1_OUT_OF_4); } } - if (!reader_is_active && expect_tag_answer) { // no need to try decoding tag data if the reader is currently sending or no answer expected yet - if (Handle15693SamplesFromTag(sniffdata >> 2, &DecodeTag)) { + if (reader_is_active == false && expect_tag_answer) { // no need to try decoding tag data if the reader is currently sending or no answer expected yet + + if (Handle15693SamplesFromTag(sniffdata >> 2, dtag)) { uint32_t eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM_SNIFF; // end of EOF - if (DecodeTag.lastBit == SOF_PART2) { + if (dtag->lastBit == SOF_PART2) { eof_time -= (8 * 16); // needed 8 additional samples to confirm single SOF (iCLASS) } uint32_t sof_time = eof_time - - DecodeTag.len * 8 * 8 * 16 // time for byte transfers + - dtag->len * 8 * 8 * 16 // time for byte transfers - (32 * 16) // time for SOF transfer - - (DecodeTag.lastBit != SOF_PART2 ? (32 * 16) : 0); // time for EOF transfer + - (dtag->lastBit != SOF_PART2 ? (32 * 16) : 0); // time for EOF transfer - LogTrace(DecodeTag.output, DecodeTag.len, (sof_time * 4), (eof_time * 4), NULL, false); + LogTrace_ISO15693(dtag->output, dtag->len, (sof_time * 4), (eof_time * 4), NULL, false); // And ready to receive another response. - DecodeTagReset(&DecodeTag); - DecodeReaderReset(&DecodeReader); + DecodeTagReset(dtag); + DecodeReaderReset(dreader); expect_tag_answer = false; tag_is_active = false; } else { - tag_is_active = (DecodeTag.state >= STATE_TAG_RECEIVING_DATA); + tag_is_active = (dtag->state >= STATE_TAG_RECEIVING_DATA); } } } + FpgaDisableTracing(); + switch_off(); DbpString("Sniff statistics:"); Dbprintf(" ExpectTagAnswer: %d, TagIsActive: %d, ReaderIsActive: %d", expect_tag_answer, tag_is_active, reader_is_active); - Dbprintf(" DecodeTag State: %d", DecodeTag.state); - Dbprintf(" DecodeTag byteCnt: %d", DecodeTag.len); - Dbprintf(" DecodeTag posCount: %d", DecodeTag.posCount); - Dbprintf(" DecodeReader State: %d", DecodeReader.state); - Dbprintf(" DecodeReader byteCnt: %d", DecodeReader.byteCount); - Dbprintf(" DecodeReader posCount: %d", DecodeReader.posCount); + Dbprintf(" DecodeTag State: %d", dtag->state); + Dbprintf(" DecodeTag byteCnt: %d", dtag->len); + Dbprintf(" DecodeTag posCount: %d", dtag->posCount); + Dbprintf(" DecodeReader State: %d", dreader->state); + Dbprintf(" DecodeReader byteCnt: %d", dreader->byteCount); + Dbprintf(" DecodeReader posCount: %d", dreader->posCount); Dbprintf(" Trace length: %d", BigBuf_get_traceLen()); - Dbprintf(" Max behindBy: %d", max_behindBy); +// Dbprintf(" Max behindBy: %d", max_behindBy); } // Initialize Proxmark3 as ISO15693 reader @@ -1386,7 +1475,7 @@ int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t tosend_t *ts = get_tosend(); TransmitTo15693Tag(ts->buf, ts->max, &start_time); uint32_t end_time = start_time + 32 * ((8 * ts->max) - 4); // substract the 4 padding bits after EOF - LogTrace(send, sendlen, (start_time * 4), (end_time * 4), NULL, true); + LogTrace_ISO15693(send, sendlen, (start_time * 4), (end_time * 4), NULL, true); int res = 0; if (recv != NULL) { @@ -1401,7 +1490,7 @@ int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, ui tosend_t *ts = get_tosend(); TransmitTo15693Tag(ts->buf, ts->max, &start_time); uint32_t end_time = start_time + 32 * (8 * ts->max - 4); // substract the 4 padding bits after EOF - LogTrace(NULL, 0, (start_time * 4), (end_time * 4), NULL, true); + LogTrace_ISO15693(NULL, 0, (start_time * 4), (end_time * 4), NULL, true); int res = 0; if (recv != NULL) { @@ -1639,7 +1728,7 @@ void SimTagIso15693(uint8_t *uid) { bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH); uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM; TransmitTo15693Reader(ts->buf, ts->max, &response_time, 0, slow); - LogTrace(resp_inv, CMD_INV_RESP, response_time * 32, (response_time * 32) + (ts->max * 32 * 64), NULL, false); + LogTrace_ISO15693(resp_inv, CMD_INV_RESP, response_time * 32, (response_time * 32) + (ts->max * 32 * 64), NULL, false); chip_state = SELECTED; } diff --git a/armsrc/iso15693.h b/armsrc/iso15693.h index fcca9491c..d650ab0ef 100644 --- a/armsrc/iso15693.h +++ b/armsrc/iso15693.h @@ -27,7 +27,7 @@ void Iso15693InitReader(void); void Iso15693InitTag(void); void CodeIso15693AsReader(uint8_t *cmd, int n); void CodeIso15693AsTag(uint8_t *cmd, size_t len); - + void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, uint32_t slot_time, bool slow); int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time); void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time); From ca14b92dd560e53b208b0f483595457ee006d7d5 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 21 Jul 2020 10:53:19 +0200 Subject: [PATCH 095/139] iclass info - some cards configured to take all available mem. ie no AA2 area. --- client/src/cmdhficlass.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 29c5eee98..14a796743 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -512,7 +512,10 @@ static void mem_app_config(const picopass_hdr *hdr) { PrintAndLogEx(INFO, "------ " _CYAN_("Memory") " ------"); PrintAndLogEx(INFO, " %u KBits/%u App Areas (%u bytes)", kb, app_areas, app2_limit * 8); PrintAndLogEx(INFO, " AA1 blocks %u { 0x06 - 0x%02X (06 - %02d) }", app1_limit , app1_limit + 5, app1_limit + 5); - PrintAndLogEx(INFO, " AA2 blocks %u { 0x%02X - 0x%02X (%02d - %02d) }", app2_limit - app1_limit, app1_limit + 5 + 1, app2_limit, app1_limit + 5 + 1, app2_limit); + if (app1_limit <= app2_limit) + PrintAndLogEx(INFO, " AA2 blocks %u { 0x%02X - 0x%02X (%02d - %02d) }", app2_limit - app1_limit, app1_limit + 5 + 1, app2_limit, app1_limit + 5 + 1, app2_limit); + else + PrintAndLogEx(INFO, " AA1 is configured to take all available space"); PrintAndLogEx(INFO, "------ " _CYAN_("KeyAccess") " ------"); PrintAndLogEx(INFO, " Kd = Debit key (AA1), Kc = Credit key (AA2)"); From 1f2c03ce8a76a9ef12f93f6a07829a77766b57d7 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 21 Jul 2020 13:00:52 +0200 Subject: [PATCH 096/139] iclass rdbl textual --- client/src/cmdhficlass.c | 62 +++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 14a796743..916ddd303 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -377,6 +377,14 @@ static inline uint32_t leadingzeros(uint64_t a) { #else return 0; #endif +} +static inline uint32_t countones(uint64_t a) { +#if defined __GNUC__ + return __builtin_popcountll(a); +#else + return 0; +#endif + } // iclass card descriptors @@ -1943,8 +1951,10 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo return PM3_ESOFT; } + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, " block %02X : " _GREEN_("%s"), blockno, sprint_hex(result->blockdata, sizeof(result->blockdata))); - + PrintAndLogEx(NORMAL, ""); + if (memcmp(result->blockdata, empty, 8) == 0) return PM3_SUCCESS; @@ -1962,14 +1972,19 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo break; } case 7: { - PrintAndLogEx(INFO, "-----------------------------------------------------------------"); + PrintAndLogEx(INFO, "----------------------------- " _CYAN_("cardhelper") " -----------------------------"); uint8_t dec_data[8]; + uint64_t a = bytes_to_num(result->blockdata, 8); - if (leadingzeros(a) < 12) { - PrintAndLogEx(INFO, "data looks encrypted, false positive is possible"); + bool starts = (leadingzeros(a) < 12); + bool ones = (countones(a) > 16 && countones(a) < 48); + + if (starts && ones) { + PrintAndLogEx(INFO, "data looks encrypted, False Positives " _YELLOW_("ARE") " possible"); Decrypt(result->blockdata, dec_data); PrintAndLogEx(SUCCESS, "decrypted : " _GREEN_("%s"), sprint_hex(dec_data, sizeof(dec_data))); } else { + memcpy(dec_data, result->blockdata, sizeof(dec_data)); PrintAndLogEx(INFO, "data looks unencrypted, trying to decode"); } @@ -1990,14 +2005,14 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo i &= 0x3C; PrintAndLogEx(SUCCESS, " bin : %s", binstr + i); - PrintAndLogEx(INFO, "-----------------------------------------------------------------"); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "------------------------------ " _CYAN_("wiegand") " -------------------------------"); wiegand_message_t packed = initialize_message_object(top, mid, bot); HIDTryUnpack(&packed, true); } else { PrintAndLogEx(INFO, "no credential found"); } - - PrintAndLogEx(INFO, "-----------------------------------------------------------------"); + PrintAndLogEx(INFO, "----------------------------------------------------------------------"); break; } } @@ -2008,8 +2023,8 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { uint8_t blockno = 0; uint8_t keyType = 0x88; //debit key uint8_t KEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t keyNbr = 0; - uint8_t dataLen = 0; + uint8_t key_idx = 0; + uint8_t key_len = 0; char tempStr[50] = {0}; bool got_blockno = false; bool elite = false; @@ -2029,7 +2044,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { cmdp += 2; break; case 'c': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("CREDIT")); + PrintAndLogEx(SUCCESS, "Using " _YELLOW_("KC credit")); keyType = 0x18; cmdp++; break; @@ -2040,20 +2055,19 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { break; case 'k': auth = true; - dataLen = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); - if (dataLen == 16) { - errors = param_gethex(tempStr, 0, KEY, dataLen); - } else if (dataLen == 1) { - keyNbr = param_get8(Cmd, cmdp + 1); - if (keyNbr < ICLASS_KEYS_MAX) { - PrintAndLogEx(SUCCESS, "Using key[%d] %s", keyNbr, sprint_hex(iClass_Key_Table[keyNbr], 8)); - memcpy(KEY, iClass_Key_Table[keyNbr], 8); + key_len = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); + if (key_len == 16) { + errors = param_gethex(tempStr, 0, KEY, key_len); + } else if (key_len == 1) { + key_idx = param_get8(Cmd, cmdp + 1); + if (key_idx < ICLASS_KEYS_MAX) { + memcpy(KEY, iClass_Key_Table[key_idx], 8); } else { - PrintAndLogEx(WARNING, "\nERROR: Credit KeyNbr is invalid\n"); + PrintAndLogEx(WARNING, "\nERROR: key index is invalid\n"); errors = true; } } else { - PrintAndLogEx(WARNING, "\nERROR: Credit Key is incorrect length\n"); + PrintAndLogEx(WARNING, "\nERROR: incorrect key length\n"); errors = true; } cmdp += 2; @@ -2078,6 +2092,14 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { if (errors) return usage_hf_iclass_readblock(); + if (verbose) { + if (key_len == 1) + PrintAndLogEx(SUCCESS, "Using key[%d] %s", key_idx, sprint_hex(KEY, 8)); + else + PrintAndLogEx(SUCCESS, "Using key %s", sprint_hex(KEY, 8)); + } + + if (auth == false) PrintAndLogEx(WARNING, "warning: no authentication used with read, only a few specific blocks can be read accurately without authentication."); From a85fd925b71169b657d3cfbc8df09e58a374c56d Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 21 Jul 2020 13:01:19 +0200 Subject: [PATCH 097/139] colors to wiegand decode --- client/src/wiegand_formats.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/src/wiegand_formats.c b/client/src/wiegand_formats.c index 7c25cd7ff..e04fc29f0 100644 --- a/client/src/wiegand_formats.c +++ b/client/src/wiegand_formats.c @@ -671,21 +671,21 @@ static void HIDDisplayUnpackedCard(wiegand_card_t *card, const cardformat_t form PrintAndLogEx(SUCCESS, " Parity: %s",card->ParityValid ? "Valid" : "Invalid"); */ - char s[80] = {0}; + char s[110] = {0}; if (format.Fields.hasFacilityCode) - snprintf(s, sizeof(s), "FC: %u", card->FacilityCode); + snprintf(s, sizeof(s), "FC: " _GREEN_("%u"), card->FacilityCode); if (format.Fields.hasCardNumber) - snprintf(s + strlen(s), sizeof(s) - strlen(s), " CN: %" PRIu64, card->CardNumber); + snprintf(s + strlen(s), sizeof(s) - strlen(s), " CN: " _GREEN_("%"PRIu64), card->CardNumber); if (format.Fields.hasIssueLevel) - snprintf(s + strlen(s), sizeof(s) - strlen(s), " Issue %u", card->IssueLevel); + snprintf(s + strlen(s), sizeof(s) - strlen(s), " Issue " _GREEN_("%u"), card->IssueLevel); if (format.Fields.hasOEMCode) - snprintf(s + strlen(s), sizeof(s) - strlen(s), " OEM: %u", card->OEM); + snprintf(s + strlen(s), sizeof(s) - strlen(s), " OEM: " _GREEN_("%u"), card->OEM); if (format.Fields.hasParity) - snprintf(s + strlen(s), sizeof(s) - strlen(s), " parity: %s", card->ParityValid ? "valid" : "invalid"); + snprintf(s + strlen(s), sizeof(s) - strlen(s), " parity: %s", card->ParityValid ? _GREEN_("valid") : _RED_("invalid")); PrintAndLogEx(SUCCESS, "[%s] - %s; %s", format.Name, format.Descrp, s); } From 06c02d3b404f9a6bd46968b997ed16bf1d045338 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 21 Jul 2020 14:09:05 +0200 Subject: [PATCH 098/139] added iclass esave, textual --- client/src/cmdhficlass.c | 199 +++++++++++++++++++++++++++++++-------- 1 file changed, 159 insertions(+), 40 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 916ddd303..6a99c0db7 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -51,8 +51,8 @@ static uint8_t iClass_Key_Table[ICLASS_KEYS_MAX][8] = { }; static int usage_hf_iclass_sim(void) { - PrintAndLogEx(NORMAL, "Simulate a iclass legacy/standard tag\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass sim [h]