Newsgroups: alt.2600
Path: sundog.tiac.net!europa.eng.gtefsd.com!emory!swrinde!pipex!sunic!EU.net!uunet!iat.holonet.net!clafave
From: clafave@iat.holonet.net (Christopher R LaFave)
Subject: Re: Hacking aTM
Message-ID: <CsCvt5.Lut@iat.holonet.net>
Organization: HoloNet National Internet Access System: 510-704-1058/modem
References: <2v5ev9$53p@lucy.infi.net>
Date: Sun, 3 Jul 1994 08:34:16 GMT
Lines: 510

			 Magnetic stripe technology


Figures and listings are at the end.

  Because of their widespread use, most magnetic cards adhere to well-defined
standards that describe the physical and magnetic characteristics for a
magnetic stripe on a plastic card. These standards outline specifications for a
storage format and information interchange. This does not preclude other 
encoding techniques or additional data tracks for specific applications, but in
most cases it makes sense to adhere to at least the basic constraints. This
gives you the choice of using any commercially available magnetic encoders for
your application. The technique used for encoding magnetic cards is known as
"Two-Frequency, Coherent Phase Recording". Allowing for the representation of
single-channel, self-clocking serial data, this methodology is generally 
referred to as F/2F. Self-clocking is achieved by combining data and clock bits
together in a continuous, synchronous sequence. In this scheme, an intermediate
flux transition signifies a one bit and the absence of an intermediate flux
transition denotes a zero bit.
  Three data tracks defined for use on standard magnetic cards each possess
different bit densities and encoded character sets. The average bit density of
track 1 is 210 bits per inch (bpi). Track 1 characters are made up of six data
bits and an odd parity bit, encoded with the least-significant bit first and 
the parity bit last, yielding a 64-character set. Taking the number of bits per
inch and the number of bits per character, you can see track 1 has the capacity
to hold 79 characters.
  Track 2 has a bit density of 75 bpi, and track 3 uses 210 bpi. Both of these
tracks allow the representation of a numeric-only character set. The characters
for tracks 2 and 3 are encoded using a 4-bit binary-coded decimal subset with
odd parity and, like track 1, are encoded with the least significant bit first
and the parity bit last. The lower density of track 2 allows up to 40 numeric
characters, where 107 numerics can be squeezed onto track 3. The actual number
of usable characters will be fewer since you also have the Start Sentinel, End
Sentinel, and LRC characters.
  Though sometimes magnetic cards are moved past the read head mechanically,
most applications rely on manually moving the card, either through a slotted
reader or into an insertion-type reader. Typically the swipe rate is 5-20 inches
per second (ips), with 50 ips being the fastest most card readers can handle. Of
course, moving the card by hand will not only result in varying the absolute
card velocity but, will also introduce incremental speed changes as the card
accelerates and decelerates past the pickup. The F/2F scheme is very forgiving
of such speed fluctuations. 
  For all 3 tracks, the fundamental data format is similar and consists of the
following elements: First, leading zero bits are encoded to indicate the 
presence of an encoded magnetic card and provide synchronization pulses to the
read head electronics and ultimately to the controller. Next, the Start Sentinel
character is encoded which indicates the start of the actual data. The coded
data follows. Next, the End Sentinel terminates the data portion of the card
and followed by an LRC byte (used for error detection). The LRC is essentially
a horizontal parity calculated by an exclusive-OR of all the data bits from the
Start Sentinel to the End Sentinel (inclusive). Finally, trailing zeros follow
the LRC and fill out the remainder of the card.
			  ANATOMY OF A MAGNETIC CARD
  The magnetic tracks have inherent characteristics based on details such as 
code set and bit and character densities. International organizations such as
Mastercard and VISA impose additional constraits for their participating members
and standards exist for bank debit cards and ATM cards as well. These rules
specify the exact content and format of each data field in aech track as well as
the intended uses for the tracks. Naturally, for nonfinancial uses, it is not
necessary to comply with these standards. For dedicated uses such as access 
control, people tracking, and material tracking, adhering to the minimal 
standards is adequate.
  The most-often-used track is track 2, although it offers the lowest inform-
ation density of the three. It contains all the information that is normally
used for credit card transactions. When a customer name is required, track 1
must be used since it is the only track that permits alphanumeric data. Track 3
is specified for numeric-only data, but is unique. It is intended for change-
able data and consequently may not only be read but may be rewritten by the
transaction-handling equipment. A multitude of data fileds are contained in
these various tracks. Figure 1 shows a brief run-down of what is generally 
placed on tracks 1 and 2.
			      REAL CARD READERS
The recovery of magnetically encoded F/2F data can be accomplished directly
with the use of just about any microcontroller. There are no particular
difficulties in deciphering the raw F/2F data stream and many early magnetic
read heads contained nothing more than signal amplifiers and line drivers.
These are now artifacts  since all modern magnetic read heads contain integ-
rated F/2F bit recovery circuitry and interface with the host controller in a
standard fashion using three wires: CARD PRESENT, CLOCK, and DATA. The read
heads usually rely on a single chip to perform the linear signal conditioning,
sychronization, and recovery of individual bits from the data stream. The Mag-
Tek 21006505 IC is representative of this type of data recovery circuit and its
functionality is depicted in figure 2.
  Linear conditioning consists of raising the level of the magnetic pickup's
input signal, rejecting common-mode noise, conditioning and detecting the 
signal, and finally providing a digital output for susequent processing. The
enable/disable counters provide initialization for the recovery section. The
recovery section locks onto the data rate and recovers the individual data bits
from the data stream. The oscillator section provides the clocks for the recov-
ery section and for the enable/disable timers. Card present goes low after 8 or
9 flux reversals are seen from the magnetic pickup and will return high about
50 ms after the last flux reversal. The strobe line signals that data is valid
and is active low. The data pin indicates a one bit when it is low. Raw F/2F
data can also be picked directly off the chip.
  The data rate for a high-density track scanned at 50 ips comes to 10500 bits
per second (bps). This results in a transfer rate of 1500 characters per second
for the 7-bit elements used on track 1, and 2100 characters per second using
the 5-bit elements of track 3. I either case, this translates to a new bit 
arriving at the controller just under every 100 us (microsecond). Even the most
anemic controller should be able to keep up. With resonably good coding techni-
ques, there should be no problem handling the entire data sampling phase on an
interrupt-driven basis. The low-density (track 2) data flows at a more pedes-
trian 3750 bps, yielding 750 5-bit characters per second, or a new bit every
266 us. Since most dual-track read heads provide track 1 and track 2 data, this
indicates that handling both tracks simultaneously is feasible under interrupt
control. Keep in mind that 50 ips is a rather fast scan rate; 20-30 ips is
probably a more realistic limit.
			     MAGNETIC BIT STORMS
  When approaching a problem such as decoding magnetic cards, it pays to spend
some time looking at the overall picture before starting to write the code. At
first glance, it would seem that organizing the data into the prevailing element
size during the sampling interval would make decoding easier. This could be
easily done by ignoring all the leading zeros, with actual data storage comm-
encing with the first one bit. Of course, this approach assumes you're getting
good data. The fact that the data recovery is handled using well-proven hard-
ware makes this assumption valid.
  If all you need to do is decode the card in a forward direction, then going
about things as I just described makes sense and reduces the coding effort to a
trivial exercise. If you have to support reverse decoding then this is not the
optimal solution. Having considered the tradeoffs of being able to decode a 
magnetic card in both forward and reverse directions, I decided to structure the
program to work equally well in either direction at the cost of a slight 
increase in initial complexity. 
  The first step in decoding is to acquire the serial bit stream. This can be 
done using a dedicated sample loop or, with a little more work, using interrupt
processing. Since the idea is the same regardless of the details, I decided to
use a sample loop in my demonstration program (listing 1). The code simply
records the incoming data stream and deposits the data in a sample buffer a byte
at a time. Sampling begins when Card Present returns idle. Any incomplete byte
that has been partially assembled at the time when sampling terminates is simply
discarded. The abundance of leading and trailing zeros allows losing some bits
at either end causing any problems. 
  Once the sampling interval completes, control is transferred to the decoding
algorithm. Presented in listing 2, the track-1 decode algorithm consists of 
nothing more than some initialization and the essence of the decode logic.
Limiting the gyrations contained in the main body of this routine not only makes
the logic easy to follow, but permits the same code to handle the decoding in
either a forward or reverse direction.
  The initial entry point assumes a forward decode attempt and sets up the 
necessary flags, pointers, and counters before jumping into the main initial-
ization code. After initialization, the sample buffer is scanned for the first
one bit, at which time a 7-bit element is assembled. If the parity is correct
and the character code checks out to be a Start Sentinel, the code proceeds and
starts pulling successive data elements from the sample buffer. If the data 
element is not an End Sentinel, the character is translated to ASCII and stored
in the decode buffer. Should an End Sentinel be detected, the program extracts
the next character, which is assumed to be the LRC byte, and finally checks the
calculated LRC for a value of zero.
  The checks and balances included in the execution of this loop include things
such as parity, a cumulative LRC, and checking to make sure I haven't run out of
samples. If everything checks out, the program terminates and returns with the
DPTR pointing to the decoded data buffer and the character count contained in
ACC. Should a decode failure occur, a test is performed on the direction flag
and if this is an attempt at a forward decode, the routine jumps to the reverse
initialization entry point. The reverse entry is similar to the forward decode
entry but sets up the sample pointer to the end of the sample buffer and sets 
the direction flag to indicate a reverse operation.
  The routines contained in the intermediate layer are shown in listing 3. The
meaning and operation of these routines should be apparent. The key routine in
this section is GET_BIT, which picks off the next bit from the sample buffer,
essentially restoring the sequential nature of the initial magnetic bit stream.
FIND_START is used to synchronize with the first one bit. GET_CHAR first checks
to make sure it hasn't run out of samples, then assembles the next 7-bit data
element while doing a parity test and LRC calculation. Any problems encountered
here are sent back to the caller and are handled there. STORE_CHAR translates
and deposits the data character into its respective location in the decoded-data
buffer and increments the character counter.
  Listing 4 shows the low-level code. These routines perform the most rudiment-
ary functions and operate in accordance with the direction flag. INDEX_PTR 
either increments or decrements the sample pointer, POSITION_BIT likewise does
either a right or left shift and LOCATE_BIT returns the state of the least- or
most- significant bit of the accumulator.
			   GO AHEAD, TAKE A SWIPE
  Let me touch on a few additional points that may not be immediately apparent
before signing off. Storing the sampled data in a continuous stream makes the
sample routine work equally well with the various bit configurations used for
the different recording tracks. This would not be easily attained if you tried
to generate a particular element format during sample time. Furthermore, if you
look at the differences between the encoded character sets and the bit formats
for the different tracks, you will find that they differ in only a few areas.
With a few minor changes, such as the defined Start Sentinel, number of bits per
element, and character translation method, the decode routine I've shown could
easily be coerced to handle the decoding of any of the standard magnetic tracks.
  As a matter of fact, by recoding and redefining the hard-coded constants as
variables, these could be set up for the particular data track at execution
time before invoking the decode function. Doing so would not only save program
memory, but would also allow you to use a routine you were comfortable with.  

*******************************************************************************
							       FIGURE 1
_____________________________________________________________________
TRACK 1 |SS|FC|   PAN   |FS|   NAME   |FS|  ADDITIONAL DATA  |ES|LRC|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Notes: Track 1 is limited to 79 characters including SS, ES, and LRC.
Mastercard PAN is variable up to 16 characters maximum. VISA is 13 or 16
 characters, including mod-10 check digit. 
SS: Start sentinel (%)
FC: Format code 
FS: Field separator ({)
ES: End sentinel (?)
LRC: Longitudinal redundency check character
PAN (primary account number): 19 digits max.
NAME: 26 alphanumeric characters max.
ADDITIONAL DATA: Expiration date 4  
		 Restriction or type 3
		 Offset or PVN 5
______________________________________________________________________
TRACK 2 |SS|FC|      PAN      |FS|       ADDITIONAL DATA      |ES|LRC|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SS: Start sentinel (;) (hex B)
FS: Field separator (=) (hex D)
ES: End sentinel (?) (hex F)
LRC: Longitudinal redundency check character
PAN (primary account number): 19 digits max.
ADDITIONAL DATA: Expiration date 4  
		 Restriction or type 3
		 Offset or PVN 5

*****************************************************************************
				  FIGURE 2
HEAD SIGNAL   __/~\__   __/~\__   __/~\__   __/~\__   __/~\__   __/~\_______
		     `-`       `-`       `-`       `-`       `-`       
____ _______  __________                                                ____
CARD PRESENT            |______________________________________________|

____ ____     _____________________      ____           ____           _____
READ DATA                          |____|    |_________|    |_________|

____ ______   __________    __   __   __   __   __   __   __   __   __   __
READ STROBE             |__|  |_|  |_|  |_|  |_|  |_|  |_|  |_|  |_|  |_|  |

			|    0|   1|   0|   1|   1|   0|   1|   1|   0|   0|
			

							    ___ 
				HEAD 1-----X---X--------13 |MAG| 9--C2-R3
					   R1  C1          |TEK|        |
				HEAD 2-----X---X--------12 |   | 10-----X--X
							   |   |        |  |
					     X---X------11 |   |        |  R5
				    X-X      R2  C6        |   | 6------|--X
				    | |      X---X-------8 |   |        | 
+5VDC --------------X--R7--X--------X-|-----------------14 |   | 7-----C3
		    |      |+         |                    |   |       ____
		    R6     C5         |                    |   | 16----DATA
		    |      X----------|------------------5 |   |       ______
____ _______        |      |          X------------------2 |   | 15----STROBE
CARD PRESENT -------X------|-----------------------------1 |   |
			   |                  X----X-----3 |   |
			   |                  C4   R4----4 |   |
GND------------------------X------------------X            |___|

R=RESISTOR(Values weren't given)
C=CAPACITOR(Values weren't given)
X=CONNECTION BETWEEN WIRES
MAG TEK is the Mag-Tek 21006505

*****************************************************************************
LISTING 1-- Using several externally defined routines, a sample program to
	    read a stripe and store it in a buffer is very short.

	    PUBLIC READ_MAG1
	    EXTERN MS1_BUF (XDATA)      ;sample buffer
	    EXTERN MS1_LIM (NUMBER)     ;sample limit
	    EXTERN MD1_BUF (XDATA)      ;decode buffer
	    EXTERN CP (BIT)             ;card present bit
	    EXTERN M1_CLK (BIT)         ;clock bit
	    EXTERN M1_DQ (BIT)          ;data bit  

M1_SS       EQU    5                    ;start sentinel
M1_ES       EQU    1FH                  ;end sentinel
;
	    SEG    CODE
;Sample and decode magnetic track 1
; output: ACC contains character count.
;         DPTR points to data buffer
READ_MAG1   PROC             
	    CALL   MAG_SAMPLE
	    JZ     L?RM1
	    CALL   MAG1_DECODE
L?RM1:      RET
	    ENDPROC
;General-purpose magnetic sampling routine
; output: ACC contains sample count
MAG_SAMPLE  PROC
	    MOV    DPTR,#MS1_BUF
	    MOV    R1,#0                ;sample counter
L?MS1:      MOV    R0,#8                ;bit counter
L?MS2:      JB     CP,L?MS4
	    JB     M1_CLK,L?MS2
	    MOV    C,M1_DQ
L?MS3:      JB     CP,L?MS4            
	    JNB    M1_CLK,L?MS3
	    CPL    C
	    RRC    A
	    DJNZ   R0,L?MS2
	    MOVX   @DPTR,A
	    INC    DPTR
	    INC    R1                   ;sample counter
	    CJNE   R1,#MS1_LIM,L?MS1
L?MS4:      MOV    A,R1                 ;final sample count
	    RET
	    ENDPROC
	    
*******************************************************************************
LISTING 2-- Once the serial bit stream has been acquired, the decoding
	    algorithm takes over.

;Bidirectional magnetic track 1 decode routine
;input: sample count in ACC, output: ACC contains character count
;       DPTR points to decoded data buffer
;Reg. usage for this routine (includes subroutines) as follows:
;R5: Direction flag, 0=forward
;R4: Cumulative LRC register
;R3: Decoded character counter
;R2: Nondecrementing sample count
;R1: Decrementing sample count
;R0: Bit syncronizing counter
MAG1_DECODE PROC
	    ;1st pass, setup for forward decode attempt
	    MOV    R5,#0                ;indicate forward decode
	    MOV    DPTR,#MS1_BUF        ;point to start of buffer
	    MOV    R1,A                 ;sample counter
	    MOV    R2,A                 ;sample count
	    JMP    L?M1D1                               
L?M1D0:     ;2nd pass, setup for reverse decode attempt
	    MOV    R5,#1                ;indicate reverse decode
	    MOV    DPTR,#MS1_BUF
	    MOV    A,R2                 ;sample count
	    DEC    A
	    ADD    A,DPL
	    MOV    DPL,A
	    CLR    A
	    ADDC   A,DPH
	    MOV    DPH,A                ;point to end of buffer
	    MOV    R1,2                 ;sample counter
L?M1D1:                                 ;decode initialization
	    MOV    R3,#0                ;character counter
	    MOV    R4,#0                ;inital LRC
	    MOV    R0,#0                ;bit syncronizer
	    CALL   FIND_START           ;main decode loop
	    JNC    L?M1D5               ;start bit error
	    CALL   GET_CHAR             ;Start Sentinel
	    JB     ACC.7,L?M1D5         ;format error
	    CJNE   A,#M1_SS,L?M1D5      ;start setinel error
L?M1D2:                                 ;data byte or End Sentinel              
	    CALL   GET_CHAR
	    JB     ACC.7,L?M1D5         ;format error
	    CJNE   A,#M1_ES,L?M1D3      ;end sentinel not found
	    JMP    L?M1D4
L?M1D3:                 
	    CALL   STORE_CHAR           ;data character
	    JMP    L?M1D2
L?M1D4:                                 ;LRC
	    CALL   GET_CHAR             ;get LRC                        
	    JB     ACC.7,L?M1D5         ;format error
	    MOV    A,R4             
	    JNZ    L?M1D5               ;LRC error
	    MOV    DPTR,#MD1_BUF        ;good return
	    MOV    A,R3                 ;final character
	    RET
L?M1D5:     ;decode error, check if 1st pass
	    CJNE   R5,#1,L?M1D0         ;check direction
	    CLR    A                    ;bad return
	    RET
	    ENDPROC

*******************************************************************************
LISTING 3-- The intermediate layer of software is one level removed from the
	    nitty-gritty details.

;Get the next bit from the sample buffer
;output: C contains data bit
GET_BIT     PROC             
	    CJNE   R0,#0,L?GB1          ;bit synchronizer
	    MOV    R0,#8
	    PUSH   ACC
	    MOVX   A,@DPTR
	    CALL   INDEX_PTR
	    MOV    B,A
	    POP    ACC
	    DEC    R1                   ;sample counter
L?GB1:      XCH    A,B
	    CALL   POSITION_BIT
	    XCH    A,B
	    DEC    R0                   ;bit synchronizer
	    RET
	    ENDPROC
;
;Find the first '1' bit in the sample buffer
;output: C=1 if bit is found
FIND_START  PROC
L?FS1:
	    CJNE   R0,#0,L?FS2          ;bit synchronizer
	    MOV    R0,#8                
	    MOVX   A,@DPTR
	    CALL   INDEX_PTR
	    DJNZ   R1,L?FS2             ;sample counter
	    JMP    L?FS4                ;out of samples
L?FS2:      CALL   LOCATE_BIT           ;test for a '1' bit
	    JC     L?FS3          
	    CALL   POSITION_BIT                                         
	    DEC    R0                   ;bit synchronizer
	    JMP    L?FS1
L?FS3:                                  ;good return
	    MOV    B,A                  ;save copy in B
	    SETB   C
	    RET
L?FS4:      CLR    C                    ;bad return
	    RET
	    ENDPROC
;
;Get the next 7 bit element from the sample buffer
;output: ACC contains data element
;        error flag is ACC.7
GET_CHAR    PROC                                
	    MOV    A,R1                 ;sample counter
	    JZ     L?GC2                ;out of samples
	    MOV    R7,#7                ;bit counter
	    CLR    A
L?GC1:      CALL   GET_BIT              ;next bit
	    RRC    A                    
	    DJNZ   R7,L?GC1
	    RR         A
	    JNB    P,L?GC2              ;parity error
	    ANL    A,#3FH               ;discard parity
	    PUSH   ACC
	    XRL    A,R4                 ;calculate LRC
	    MOV    R4,A
	    POP    ACC                  ;good return
	    RET
L?GC2:      SETB   ACC.7                ;bad return
	    RET
	    ENDPROC
;Translate and store the data character
;input: ACC contains data character
STORE_CHAR  PROC
	    PUSH   DPL
	    PUSH   DPH 
	    PUSH   ACC
	    MOV    DPTR,#MD1_BUF
	    MOV    A,R3                 ;character counter
	    ADD    A,DPL
	    MOV    DPL,A
	    CLR    A
	    ADDC   A,DPH                ;generate displacement
	    MOV    DPH,A
	    POP    ACC
	    ADD    A,#' '               ;translate
	    MOVX   @DPTR,A              ;store
	    POP    DPH
	    POP    DPL
	    INC    R3                   ;character counter
	    RET
	    ENDPROC
	    
*******************************************************************************
LISTING 4-- The low-level routines get right down to the ground and take care
	    of the gory details.

;Index the sample pointer either forward or backward
INDEX_PTR          PROC
	    CJNE   R5,#0,L?IP1          ;check direction
	    INC    DPTR                 ;forward
	    RET
L?IP1:      PUSH   ACC                  ;backward
	    DEC    DPL
	    MOV    A,DPL
	    CJNE   A,#-1,L?IP2
	    DEC    DPH
L?IP2:      POP    ACC
	    RET
	    ENDPROC

;Position bit is in ACC into C in either a right or left shift
POSITION_BIT       PROC
	    CJNE   R5,#0,L?PB1          ;check direction
	    RRC    A                    ;forward
	    RET
L?PB1:      RLC    A
	    RET
	    ENDPROC
	    
;Locate a 1 bit, either msb or lsb
;output: C=1 if bit is a one
LOCATE_BIT         PROC
	    CJNE   R5,#0,L?LB1          ;check direction
	    MOV    C,ACC.0              ;forward
	    RET
L?LB1:      MOV    C,ACC.7              ;backward
	    RET
	    ENDPROC
	    
*******************************************************************************
Contact:
 Mag-Tek, Inc.
 20725 S. Annalee Ave.
 Carson, CA 90746
 (213) 631-8602
 fax: (213) 631-3956
*******************************************************************************




-- 
               _
  __ |   __   | ` __      __   clafave@holonet.net
 |   |   __| ~|~  __||  ||__|  Beaverton, Oregon USA
 |__ |_ |__|  |  |__| \/ |__.  GO BLAZERS!
