Wrote an EAN13 barcode program

I decided to write a barcode generator in Java “for no apparent reason”, that can currently generator EAN13 barcodes. It’s available here in this post, but if you want the latest code then I suggest you check it out on github.

Github repo:
https://github.com/jra89/barcode

Usage:

java -jar barcode.jar numbers path barwidth barheight

Example:

java -jar barcode.jar 999990000020 /home/user/Desktop/EAN_IMG.png 2 60

Main.java

package barcode;

public class Main
{
	public static void main(String[] args)
	{
		String code;
		String path;
		int barWidth;
		int barHeight;

		if(args.length <= 1)
		{
			System.out.println("Not enough parameters");
		}
		
		try
		{
			code = args[0]; 
		}
		catch(NullPointerException e)
		{
			code = "9999900000207";
		}
		
		try
		{
			path = args[1];
		}
		catch(NullPointerException e)
		{
			path = "/tmp/barcode.png";
		}
		
		try
		{
			barWidth = Integer.parseInt(args[2]);
		}
		catch(NullPointerException e)
		{
			barWidth = 1;
		}
		
		try
		{
			barHeight = Integer.parseInt(args[3]);
		}
		catch(NullPointerException e)
		{
			barHeight = 100;
		}

		Ean13 ean = new Ean13(code, path, barWidth, barHeight);
		ean.createBarcodePNG();

	}
}

Ean13.java

package barcode;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class Ean13 
{
	/**
	 * -=Structure=-
	 * 
	 * Fist, First Six, Last Six
	 * LGR = 012
	 * 0 	LLLLLL 	RRRRRR
	 * 1 	LLGLGG 	RRRRRR
	 * 2 	LLGGLG 	RRRRRR
	 * 3 	LLGGGL 	RRRRRR
	 * 4 	LGLLGG 	RRRRRR
	 * 5 	LGGLLG 	RRRRRR
	 * 6 	LGGGLL 	RRRRRR
	 * 7 	LGLGLG 	RRRRRR
	 * 8 	LGLGGL 	RRRRRR
	 * 9 	LGGLGL 	RRRRRR
	 *
	 * -=Encodings=-
	 *
	 * Digit 	L-code 	G-code 	R-code
	 * 0 	0001101 	0100111 	1110010
	 * 1 	0011001 	0110011 	1100110
	 * 2 	0010011 	0011011 	1101100
	 * 3 	0111101 	0100001 	1000010
	 * 4 	0100011 	0011101 	1011100
	 * 5 	0110001 	0111001 	1001110
	 * 6 	0101111 	0000101 	1010000
	 * 7 	0111011 	0010001 	1000100
	 * 8 	0110111 	0001001 	1001000
	 * 9 	0001011 	0010111 	1110100
	**/
	
	private String code;
	private String path;
	private BufferedImage bi;
    private Graphics2D ig2;
    private int barPos;
    private int barPosBin;
    private int imgPixelPos;
    private int barWidth;
	private int barHeight;
	private int imgWidth;
	private int imgHeight;
    private int[][] barcodeBinary;
	
	private int[][] firstSix = {
			{0,0,0,0,0,0}, //LLLLLL
			{0,0,1,0,1,1}, //LLGLGG
			{0,0,1,1,0,1}, //LLGGLG
			{0,0,1,1,1,0}, //LLGGGL
			{0,1,0,0,1,1}, //LGLLGG
			{0,1,1,0,0,1}, //LGGLLG
			{0,1,1,1,0,0}, //LGGGLL
			{0,1,0,1,0,1}, //LGLGLG
			{0,1,0,1,1,0}, //LGLGGL
			{0,1,1,0,1,0}  //LGGLGL
	};
	
	private int[] lastSix = {2, 2, 2, 2, 2, 2};
	
	private int[][][] encodings = {
			{
				{0,0,0,1,1,0,1}, 
				{0,1,0,0,1,1,1}, 
				{1,1,1,0,0,1,0}
			},
			{
				{0,0,1,1,0,0,1},
				{0,1,1,0,0,1,1},
				{1,1,0,0,1,1,0}
			},
			{
				{0,0,1,0,0,1,1},
				{0,0,1,1,0,1,1}, 	
				{1,1,0,1,1,0,0}
			},
			{
				{0,1,1,1,1,0,1},
				{0,1,0,0,0,0,1},
				{1,0,0,0,0,1,0}
			},
			{
				{0,1,0,0,0,1,1},
				{0,0,1,1,1,0,1},
				{1,0,1,1,1,0,0}
			},
			{
				{0,1,1,0,0,0,1},
				{0,1,1,1,0,0,1},
				{1,0,0,1,1,1,0}
			},
			{
				{0,1,0,1,1,1,1},
				{0,0,0,0,1,0,1},
				{1,0,1,0,0,0,0}
			},
			{
				{0,1,1,1,0,1,1},
				{0,0,1,0,0,0,1},
				{1,0,0,0,1,0,0}
			},
			{
				{0,1,1,0,1,1,1},
				{0,0,0,1,0,0,1},
				{1,0,0,1,0,0,0}
			},
			{
				{0,0,0,1,0,1,1},
				{0,0,1,0,1,1,1},
				{1,1,1,0,1,0,0}
			}
	};
	
	Ean13(String code, String path,int barWidth, int barHeight)
	{
		this.code = code;
		this.path = path;
		this.barPos = 12;
		this.barPosBin = 7;
		this.imgPixelPos = 0;
		this.barcodeBinary = new int[barPos][barPosBin];
		this.barWidth = barWidth;
		this.barHeight = barHeight;
		this.imgWidth = ((12*7) + (2*9) + (2*3) + (1*5)) * this.barWidth;
		this.imgHeight = this.barHeight;
	}
    
	public void createBarcodePNG()
	{
		this.code += Integer.toString(calculateControlDigit(this.code));
		generateBinaryMap();
		generateBarcodePNG();
	}
	
	private void generateBinaryMap()
	{
		int first = Integer.parseInt(String.valueOf(this.code.charAt(0)));
				
		//i = 1, first digit is not welcome to the bar
		for(int i = 1; i < 13; ++i)
		{
			int current = Integer.parseInt(String.valueOf(this.code.charAt(i)));
			
			if(i < 7)
				this.barcodeBinary[i-1] = this.encodings[current][this.firstSix[first][i-1]];
			else
				this.barcodeBinary[i-1] = this.encodings[current][this.lastSix[i-7]];
		}
	}
	
	private void generateBarcodePNG()
	{
		try {
		      // TYPE_INT_ARGB specifies the image format: 8-bit RGBA packed
		      // into integer pixels
		      bi = new BufferedImage(this.imgWidth, this.imgHeight, BufferedImage.TYPE_INT_ARGB);
		      ig2 = bi.createGraphics();
		      
		      ig2.setPaint(Color.white);
		      ig2.fillRect ( 0, 0, bi.getWidth(), bi.getHeight() );

		      //Draw quiet zone
		      drawSpecial(0);
		      
		      //Draw lead
		      drawSpecial(1);
		      
		      //Draw first group
		      drawGroup(1);
		      
		      //Draw separator
		      drawSpecial(2);
		      
		      //Draw second group
		      drawGroup(2);
		      
		      //Draw lead
		      drawSpecial(1);
		      
		      //Draw quiet zone
		      drawSpecial(0);

		      ImageIO.write(bi, "PNG", new File(path));
		      
		    } catch (IOException ie) {
		      ie.printStackTrace();
		    }
	}
	
	private void drawGroup(int groupPart)
	{
		int i = 0, length = 0;
		if(groupPart == 1)
		{
			i = 0;
			length = (this.barcodeBinary.length/2);
		}
		else if(groupPart == 2)
		{
			i = 6;
			length = this.barcodeBinary.length;
		}
			
		for(; i < length; ++i)
		{
			for(int n = 0; n < this.barcodeBinary[i].length; ++n)
			{
				if(this.barcodeBinary[i][n] == 0)
				{
					ig2.setPaint(Color.white);
				    ig2.setStroke(new BasicStroke(this.barWidth));
				}
				else
				{
					ig2.setPaint(Color.black);
					ig2.setStroke(new BasicStroke(this.barWidth));
				}
				  		  
				int pos = this.imgPixelPos;
				ig2.drawLine(pos,0,pos,this.barHeight);
				this.imgPixelPos += this.barWidth;
			}
		}
	}
	
	private void drawSpecial(int type)
	{
		
		/*
		Special Symbol Pattern
		Quite Zone 	000000000
		Lead / Trailer 	101
		Separator 	01010
		 */
		
		int[] quiteZone = {0,0,0,0,0,0,0,0,0};
		int[] leadtrail = {1,0,1};
		int[] separator = {0,1,0,1,0};
		int binaryArrLength = 0;
		int[] arr;
		
		if(type == 0)
		{
			binaryArrLength = quiteZone.length;
			arr = quiteZone;
		}
		else if(type == 1)
		{
			binaryArrLength = leadtrail.length;
			arr = leadtrail;
		}
		else
		{
			binaryArrLength = separator.length;
			arr = separator;
		}

		for(int n = 0; n < binaryArrLength; ++n)
		{
			if(arr[n] == 0)
				ig2.setPaint(Color.white);
			else
				ig2.setPaint(Color.black);
			  		  
			int pos = this.imgPixelPos;
			ig2.drawLine(pos,0,pos,this.barHeight);
			this.imgPixelPos += this.barWidth;
		}
	}
	
	public int calculateControlDigit(String ean)
	{
		int sum = 0;
		for(int i = 0; i < 12; ++i)
		{
			int val = charToInt(ean.charAt(i));
			if((i+1)%2 == 0)
				sum += val*3;
			else
				sum += val*1;	
		}

		return (10 - (sum % 10));
	}
	
	private int charToInt(char c)
	{
		return Integer.parseInt(String.valueOf(c));
	}
}

Reverse vending machines, wrapping it up

Ok so I thought it would be a good idea to wrap things up around this project now. I’ve been doing a lot more work on it now, perfecting the scripts and so on. I even wrote my own barcode program in Java (for fun), and bought a receipt printer so that I could perfect my proof-of-concept receipt.

I also got a new reply from Tomra about my questions, and it seems that they have been forwarding my email around in their company, and then forgot to remove their subject line when finally sending me an answer. And thus the subject was something like “A lot of spam from the same customer” … cute. But the answer I got was the same as usual, as in “We have a solution that has been working for a long time now that we believe in, so we can’t tell you more about it. Thank you for your interest”. Of course I understand their view on this, as it is a very expensive piece of machinery and not something they will just openly expose everything about to random people like me. I haven’t been able to find a store with this security yet, although I got a receipt a few days ago that indicates something unusual, so if I get the time for it, I might actually check that out as well, just for fun, and to see if there actually is some security.

The kind of security that I can imagine that they have, is some sort of control number on the receipt that is stored in a database and then removed from the database when the receipt is scanned by the cashier (I have seen some receipts with an expiration date of 2 months and such, which would indicate something like that). Of course this security will hold pretty ok, except if the store has one of those self checkouts, where the customer scans all the receipts themselves, since then you can try to have a bunch of receipts with you and try every time you shop, and if you are lucky there will be a matching receipt in the database (of course this will be extremely hard since the control number and amount of money on the receipt will probably have to match, and thus making this pretty meaningless to attempt).

My solution is actually similar to theirs, with the control number and everything, except that I have two approaches.
The first method is to do it like they probably have, as in having a control number and then have the machine add it to a database that the cash machines check when scanning the receipt.
But instead of using an EAN code, the receipt would have a QR code (which can store a lot more data than an EAN code can) that contains PGP encrypted data. The machine would have the public key and the cash machine would have the private one, and the data stored would be the amount of money and a control number.

The second solution is one where the machine does not have any contact to a database or any kind of network at all.
The receipt would still have a QR code with almost the same encrypted data, but it would also have the date when it was created, so that when it is scanned, it is added to the database then, instead of being removed at the time of scanning, and if the date has passed the expiration, it will not be added and be counted as invalid, and if it’s valid, it will be added and then kept for the time it is valid, and then removed by the system. This way the machine wont have to be connected all the time.

Anyway enough about that, so I actually bought 2 printers, one being an Epson TM-T88IV, and the second one a Star TSP100 ECO. I had a lot of trouble with the Epson one at first, so I bought the Star one since I read some pretty good reviews about it, and then it turned out that I had configured the Epson wrongly (I discovered this when I was playing around with the Star one, so I guess it was worth it in a way).

I will fix the Epson one a bit, clean it, add a driver DVD, make a short manual and add a cable for it (didn’t get a cable when I bought it).
And when I finish fixing it, I will try to sell it for a bit more than what I got it for (it was in bad shape when I got it, but it’s mostly fixed now).

The Star one that you can see below, was in really neat condition and works very well, so I will keep it for future projects.

2013-08-20 18.20.41

So with this I printed out some example receipts first, to try the quality.

2013-08-20 18.29.31

And then with my new barcode program and the updates that I made to the scripts, I could finally print a very good looking and authentic “fake” receipt! 🙂
As you can see on the fake one to the left, the barcode is a bit smaller, and this is due to me being a little bit lazy now at the end (The barcode program isn’t 100% finished yet, but I will fix it more later so that it can make the barcode a bit wider, but I don’t have much time for it right now).

2013-08-20 18.20.12

I tried to scan the barcode on the fake one as well, just to make sure that it worked.

2013-08-20 18.17.57

As you can see, the real one to the right is a bit old and the color is about to go away, but the strength of the color would be about the same as the fake one.
Do note that the “ink” and paper of the fake one, is the same as the real one, so it would be very hard to tell them apart in the store.
And with just a little bit more work, you could make them exactly identical, but I think I have made my point now, so I wont go into that.

Hint: https://alcor.se/2013/08/20/wrote-an-ean13-barcode-program/

Using Java to connect with SSLSocket, trusting all certificates

So I wanted to make a little test client that would connect to a web server via SSL.
The problem is that in this experiment I don’t really care about the security, so I want the client to accept all certificates, even self signed ones and very old ones.
Credits to my good friend Maboroshi who helped me find the final solution that solved the last problem with the invalid certs.

So the first thing I did was to write the following.
This is a safe code that does NOT accept invalid certificates of any kind.

SAFE

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.*;
import java.net.Socket;

public class test {	
    public static void main(String[] arstring) {
        try {
            //Connect without SSL
            Socket clientSocket = new Socket("google.com", 80);
            DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
            BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

            outToServer.writeBytes("GET / HTTP/1.1nHost: google.comnn");
            System.out.println("No SSL - " + inFromServer.readLine());
            clientSocket.close();
        	
            //Connect with SSL
            SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
            SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("google.com", 443);

            DataOutputStream outToServerSSL = new DataOutputStream(sslsocket.getOutputStream());
            BufferedReader inFromServerSSL = new BufferedReader(new InputStreamReader(sslsocket.getInputStream()));

            outToServerSSL.writeBytes("GET / HTTP/1.1nHost: google.comnn");
            System.out.println("SSL - " + inFromServerSSL.readLine());
            
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
}

The problem with the above code for me, was that when connecting to a server with an invalid certificate, I got the following error.

Exception in thread “main” javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(Unknown Source)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)

Thus, that solution wont cut it!
I needed something else, so I tried to write some code that would accept all certs as well.
Please note that ALL of the code from here on is UNSAFE, and should NOT be used in a production environment.


Reference to solution

UNSAFE

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import java.io.*;
import java.net.Socket;
import javax.net.ssl.TrustManager;
import java.security.cert.X509Certificate;

public class ssl {
    public static void main(String[] arstring) {
    	
        try {
            //Connect without SSL
            Socket clientSocket = new Socket("google.com", 80);
            DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
            BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
 
            outToServer.writeBytes("GET / HTTP/1.1nHost: google.comnn");
            System.out.println("No SSL - " + inFromServer.readLine());
            clientSocket.close();
               
            //Connect with SSL
            // Create a trust manager that does not validate certificate chains
    		TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
    				public java.security.cert.X509Certificate[] getAcceptedIssuers() {
    					return null;
    				}
    				
    				@Override
    				public void checkClientTrusted(X509Certificate[] certs, String authType) {
    				}
    				
    				@Override
    				public void checkServerTrusted(X509Certificate[] certs, String authType) {
    				}
    			}
    		};

            // Install the all-trusting trust manager
    	    SSLContext sc = SSLContext.getInstance("SSL");
    	    sc.init(null, trustAllCerts, new java.security.SecureRandom());
            
            
            SSLSocketFactory sslsocketfactory = sc.getSocketFactory();
            SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("google.com", 443);
 
            DataOutputStream outToServerSSL = new DataOutputStream(sslsocket.getOutputStream());
            BufferedReader inFromServerSSL = new BufferedReader(new InputStreamReader(sslsocket.getInputStream()));
 
            outToServerSSL.writeBytes("GET / HTTP/1.1nHost: google.comnn");
            System.out.println("SSL - " + inFromServerSSL.readLine());
           
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
}

But, now this gives me a different error because this invalid certificate is broken/old!

javax.net.ssl.SSLProtocolException: no more data allowed for version 1 certificate
at sun.security.ssl.HandshakeMessage$CertificateMsg.(HandshakeMessage.java:431)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:153)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:609)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:545)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:963)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1208)
at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:674)
at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:119)
at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:133)
at java.io.DataOutputStream.writeBytes(DataOutputStream.java:276)
at ssl.main(ssl.java:93)
Caused by: java.security.cert.CertificateParsingException: no more data allowed for version 1 certificate
at sun.security.x509.X509CertInfo.parse(X509CertInfo.java:710)
at sun.security.x509.X509CertInfo.(X509CertInfo.java:169)
at sun.security.x509.X509CertImpl.parse(X509CertImpl.java:1751)
at sun.security.x509.X509CertImpl.(X509CertImpl.java:196)
at sun.security.provider.X509Factory.engineGenerateCertificate(X509Factory.java:107)
at java.security.cert.CertificateFactory.generateCertificate(CertificateFactory.java:322)
at sun.security.ssl.HandshakeMessage$CertificateMsg.(HandshakeMessage.java:429)
… 10 more

Ok, so this is where it got tricky to solve.
I googled around for some time but I couldn’t really find a solution (since usually this is not the kind of thing you want to do).

But then a friend of mine (Maboroshi), pointed me to this site

Which brought me to this final solution.
The only real change here was to use the BouncyCastle provider

Security.insertProviderAt(new BouncyCastleProvider(), 1);

Which can be found HERE

Another requirement that I came to think about, is that this change requires Java 6 since Java 7 doesn’t support MD2. There is a solution if you are running Java 7 though.

Just open this file

JDK_HOME/jre/lib/security/java.security

And comment out the following line

jdk.certpath.disabledAlgorithms=MD2

That should do the trick.
Solution was found HERE

UNSAFE

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import java.io.*;
import java.net.Socket;
import javax.net.ssl.TrustManager;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.cert.X509Certificate;
import java.security.Security;
 
public class ssl {
    public static void main(String[] arstring) {
    	Security.insertProviderAt(new BouncyCastleProvider(), 1);
    	
        try {
            //Connect without SSL

            Socket clientSocket = new Socket("google.com", 80);
            DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
            BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
 
            outToServer.writeBytes("GET / HTTP/1.1nHost: google.comnn");
            System.out.println("No SSL - " + inFromServer.readLine());
            clientSocket.close();
               
            //Connect with SSL
            
            // Create a trust manager that does not validate certificate chains
    		TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
    				@Override
					public java.security.cert.X509Certificate[] getAcceptedIssuers() {
    					return null;
    				}
    				
    				@Override
    				public void checkClientTrusted(X509Certificate[] certs, String authType) {
    				}
    				
    				@Override
    				public void checkServerTrusted(X509Certificate[] certs, String authType) {
    				}
    			}
    		};

    		// Install the all-trusting trust manager
    		SSLContext sc = SSLContext.getInstance("SSL");
    		sc.init(null, trustAllCerts, new java.security.SecureRandom());
            
            
            SSLSocketFactory sslsocketfactory = sc.getSocketFactory();
            SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("google.com", 443);
 
            DataOutputStream outToServerSSL = new DataOutputStream(sslsocket.getOutputStream());
            BufferedReader inFromServerSSL = new BufferedReader(new InputStreamReader(sslsocket.getInputStream()));
 
            outToServerSSL.writeBytes("GET / HTTP/1.1nHost: google.comnn");
            System.out.println("SSL - " + inFromServerSSL.readLine());
           
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
}

Now I got the output I wanted 🙂

No SSL – HTTP/1.1 200 OK
SSL – HTTP/1.1 200 OK

Confronting the store about the problem

This is a continuation of THIS post

I went down to one of the stores today that I used in my research, to tell them about my research, the problem and so on.
I asked for the store manager, and told him about it, and then I asked if he was okay with me testing if the receipt actually worked in the scanner at the checkout.

He seemed a bit skeptical at first, but eventually he told me to follow him to the checkout, and then he asked a cashier to take the receipt and use it as if it was a normal purchase (You can scan the receipt when you are scanning your products, and the machine will deduct the amount it says on the receipt).

The receipt worked, and a few SEK (Swedish crowns) was deducted from the price (Success o/).
After this I told them that they could keep the fake receipt to do with as they wished, like destroying it or something, but they decided to keep it intact and put up a warning sign in the store for the people who work there to keep a lookout for this kind of receipt (even though it would be close to impossible to tell them apart with a few adjustments and a proper receipt printer).

I also asked them if I could use them as a reference when contacting the company that makes these machines, which they approved of.
I didn’t get the approval on paper though, and I even forgot to take any pictures from the machine in the store when it had scanned the fake receipt, but you’ll just have to believe me 🙂

All in all this turned out pretty nicely, and I will make a few more changes to the receipt template and then generator script to make it a bit more complete, and then I will make a small proof-of-concept for my solution to the problem, and then send it to Tomra that makes the machines. After all that I will consider this little project done most likely, if nothing else comes up.

The next project in line after this one that I will be blogging about, is how to break the encryption on public transport cards that use the MIFARE Classic 1K standard (using already built tools, as the encryption was broken a few years ago), and then make copies of the card, as well as altering the data on them (like how much money they have on the card).

Bottom line here is, I’m happy they didn’t call the cops 🙂

girl-anime-happy

Continuation: https://alcor.se/2013/08/20/reverse-vending-machines-wrapping-it-up/

Having fun with reverse vending machines

I have a bad habit of breaking things on purpose, just to see what would happen if I do.
And in this case, I was recycling some bottles a few weeks ago, and I happened to take a quick look at the EAN code on the receipts that I got from the machine.

receipt1

From the pictures we can immediately come to two conclusions

1: The 7 digits (read from left to right) are probably the same on all the receipts from the same machine.

2: The third digit (read from right to left) is the amount of money on the receipt

Now, these receipts are usually given to the cashier.
When given to the cashier, she/he scans them and makes sure that they’re valid and then proceeds to give you the money or takes the money off from your purchase.
The cashier then draws a line over the EAN codes of the receipts and keeps them, so that it can’t be used again.

From that behavior, and from what we saw before on the receipts, we can draw another conclusion

3: The receipt is not unique, and could be scanned as many times as we want (We could make copies of it for example)

The receipts seem to vary a bit between machines/stores (gonna check if I can find some more info on that).
But for now I’ll focus on the Tomra T-83 HCpIII that this store has.

Tomra T-83 HCp website
Tomra T-83 HCp PDF

tomra-t-83hcp_p1

Now, if you create fake receipts with a normal printer, it would probably be discovered by the cashier, since the receipts use a special kind of paper and ink.
Although those printers + paper aren’t very hard to come by, so that wouldn’t be too hard to solve.

Epson TM-T88IV

tmt88iv

But, let’s put that aside and assume that you can’t go via the cashier (My goal here is not to create fake receipts and earn a bunch of money, so going via the cashier and getting cash would be pointless here). In the store where I am conducting these little experiments, there’s a “self checkout” area, where you can draw your membership card, scan everything yourself and then pay with a credit or debit card (They have random checks at times to make sure that people aren’t cheating). Next to the machine where you scan the EAN codes of the products that you are buying, there’s a small machine where you can put in different notes (with EAN codes) with offers on (buy 2 get 70% of on the third blabla etc), but you can also put in your receipts there and it will scan it, verify it and then draw the amount of your purchase.

A few things that should be noted here are;

4: The paper with the different offers and the recycling receipts, are on different kind of papers with different ink and quality, which makes me think that there aren’t many (if any) restrictions on the size and quality of the paper and EAN code, as long as it’s readable. Thus, even if I wanted to print it on normal A4 paper that I have in my printer at home, it would probably work.

5: The company that makes this EAN code reader is not the same one that makes the reverse vending machine for recycling the cans.

6: When the paper is inserted into the EAN code reader, if the EAN is valid, the machine keeps the paper. This further confirms point 3 that the code could probably be scanned several times if we wanted (which makes me think of a few fun things we could do with those “Weekly offers” papers that we get sometimes when shopping there, but that’s for another blog entry).

Now that we know all this, we need to do a few things!

* We need to analyze the receipts and figure out what the numbers are (to make templates later)

* We need to gather info on the machines that are used in this experiment (EAN barcode printers and scanners)

* Write a program that can generate these receipts from a template (Might as well do it while I’m at it)

So these are the two receipts that I have right now

Store: ICA Kvantum (Malmborgs Mobilia Malmö)
Machine: Tomra T-83 HCpIII
EAN Code: 9 999900 000702

Store: ICA Kvantum (Malmborgs Mobilia Malmö)
Machine: Tomra T-83 HCpIII
EAN Code: 9 999900 001006

These receipts use the EAN-13 standard, and since we already concluded that the first part of the code is the same on all these receipts,
we’ll just focus on the last part of the code, the 001006 and 000702.
According to the EAN-13 standard, the last digit is always the “check digit” or “checksum”, which is calculated like this:

References:
http://en.wikipedia.org/wiki/ISBN#ISBN-13_check_digit_calculation
http://en.wikipedia.org/wiki/EAN-13

9*1 + 9*3 + 9*1 + 9*3 + 9*1 + 0*3 + 0*1 + 0*3 + 0*1 + 0*3 + 7*1 + 0*3
=
9 + 27 + 9 + 27 + 9 + 7 = 88
=>
88/10 = 8,8
(take the rest)
10 - 8 = 2
The check digit is 2

So now that we know what the check digit is and how to calculate it, let’s focus on the rest of the numbers that we have left, which is the 001006 and 000702.
If we remove the check digit, we have this

00100
00070

And since we had one receipt on 10 SEK and one with 7 SEK, this one is pretty easy to guess, that on the first one, the “10” part is the 10 SEK, and on the second one the “7” is the 7 SEK.
So from this we can conclude that reading it from left to right, we have a digit for thousand, hundred, ten, ones and a decimal (Under 1 SEK is öre, but we don’t have that as a part of our currency any longer in Sweden, so I’ll just call it decimal for now).

So now that we know all this, we can actually create a fake receipt with just about any value we want (that fits in the EAN code of course).
To try this out and see how close to the original we can get without just putting a receipt in a machine and making copies, I have created a template in LaTex where one can just insert the right values in the template, and then convert it to a picture or PDF and print it to get the finished receipt.
The template can be seen below.

Template in LaTeX:

documentclass[12pt]{article}
usepackage{lmodern}
usepackage[utf8]{inputenc}
usepackage[paperwidth=57mm,paperheight=113mm,margin=3mm]{geometry}
%usepackage{geometry}
usepackage{graphicx}
usepackage{setspace}
usepackage{color}
%usepackage{ean13}
usepackage{soul}
geometry{verbose}
makeatletter

title{ICA Kvantum T-83HCp Receipt Template}
date{today}
makeatother

begin{document}
%begin{minipage}[t]{57mm}
centering{includegraphics{STORE_LOGO_IMG}}
pagecolor{white}
vspace{1mm}

textsf{textbf{Large TITLE_PART_1}}textsf{Large {} }
{Large {} {} {} }textsf{Large {} }textsf{textbf{Large TITLE_PART_2}}{Large }

%Large space between title and total
vspace{20mm}

%Total amount of money
centering
colorbox{black}{hspace{linewidth}hspace{HSPACEfboxsep}color{white}textbf{Large TOTAL_MONEY}{Large }}{Large par}

%Tiny space between total and ean
vspace{1mm}
centering
%EAN image
includegraphics{EAN_IMG}

%Space plus ean numbers under ean
begin{spacing}{0.5}
textsf{textbf{textsc{small EAN_NUM}}}{small par}
end{spacing}

%Space between ean and bottle/can amount
vspace{4mm}

%Amount of bottles/cans
textsf{textbf{footnotesize COUNT_BOTTLE flaskor/burkar áhfill{}AMOUNT_PER_BOTTLE}}{footnotesize par}
textsf{textbf{footnotesize rule[0.5ex]{1columnwidth}{1pt}}}{footnotesize par}

begin{spacing}{0.8}
textsf{textbf{footnotesize MODEL_NAME}}{footnotesize par}
textsf{textbf{footnotesize SERIAL_NUMBER}}{footnotesize par}
textsf{textbf{footnotesize TIME DATE}}
end{spacing}
%end{minipage}
end{document}

Generator script:

gen.sh (For safety reasons, the barcode script is not included)

#!/bin/bash

# This is a generator script for the ICA Malmborgs template
# I have left out some important parts, to protect the store in question
# Usage:
# bash gen.sh total_money

# Copy template to a temporary location
cp receipt.tex /tmp/receipt.tmp.tex

#Set path, bar width and height
path="EAN_IMG.png"
barwidth=1
barheight=60

# Set the numbers for the EAN
first=9
left=999900
right=$(bash right.sh $1)

# Decide the control digit
control=$(bash control.sh $first $left $right)
echo $control

# Set hspace (it needs to differe depending on the input value)
hspace=$(bash hspace.sh $1)

# Create the barcode here named EAN_IMG.png
# You will have to figure out for yourself how to do that though
# And write the barcode script yourself
# <insert barcode call here>

# Get time and date values
time_var=$(date +%H:%M)
date_var=$(date +%d-$(echo $(date +%b) | tr '[:lower:]' '[:upper:]')-%Y)

# Replace placeholders in template
sed -i 's/STORE_LOGO_IMG/STORE_LOGO_IMG.jpg/g' /tmp/receipt.tmp.tex
sed -i 's/TITLE_PART_1/Malmborgs/g' /tmp/receipt.tmp.tex
sed -i 's/TITLE_PART_2/Mobilia/g' /tmp/receipt.tmp.tex
sed -i "s/HSPACE/$hspace/g" /tmp/receipt.tmp.tex
sed -i "s/TOTAL_MONEY/$1.00/g" /tmp/receipt.tmp.tex
sed -i 's/EAN_IMG/EAN_IMG.png/g' /tmp/receipt.tmp.tex
sed -i "s/EAN_NUM/$first \hspace{1mm} \so{$left} \hspace{1mm} \so{$right$control}/g" /tmp/receipt.tmp.tex
sed -i 's/COUNT_BOTTLE/7/g' /tmp/receipt.tmp.tex
sed -i 's/AMOUNT_PER_BOTTLE/1.00/g' /tmp/receipt.tmp.tex
sed -i 's/MODEL_NAME/Tomra 83 HCp3/g' /tmp/receipt.tmp.tex
sed -i 's/SERIAL_NUMBER/606657-90199310-01456-00/g' /tmp/receipt.tmp.tex
sed -i "s/TIME/$time_var/g" /tmp/receipt.tmp.tex
sed -i "s/DATE/$date_var/g" /tmp/receipt.tmp.tex

# Create PDF and PNG
pdflatex /tmp/receipt.tmp.tex receipt.tmp.pdf
convert receipt.tmp.pdf receipt.png

# remove temp files
rm /tmp/receipt.tmp.tex
rm EAN_IMG.png

 

hspace.sh – This one sets the hspace value (template design related)

#!/bin/bash

val=$1
hspace="-12"

#There's probably a better way to do this
if  [[ ${#val} = 1 ]] ; then
   hspace="-12"
elif  [[ ${#val} = 2 ]] ; then
   hspace="-15"
elif [[ ${#val} = 3 ]] ; then
   hspace="-20"
elif [[ ${#val} = 4 ]] ; then
   hspace="-25"
fi

echo $hspace

 

control.sh – This one calculates the control digit

#!/bin/bash

var=$1$2$3
echo $var
vartwo=0

for i in {0..11}
do
    tmp=${var:$i:1}

    if [ $(($(($i+1))%2)) -eq 0 ] ; then
        vartwo=$(($vartwo+$(($tmp*3))))
    else
        vartwo=$(($vartwo+$(($tmp*1))))
    fi
done

vartwo=$(($vartwo%10))
vartwo=$((10-$vartwo))
echo $vartwo

 

right.sh – This one calculates the right part of the EAN number

#!/bin/bash

val=$1
right=0

#There's probably a better way to do this
if  [[ ${#val} = 1 ]] ; then
   right="000${val}0"
elif  [[ ${#val} = 2 ]] ; then
   right="00${val}0"
elif [[ ${#val} = 3 ]] ; then
   right="0${val}0"
elif [[ ${#val} = 4 ]] ; then
   right="${val}0"
fi

echo $right

 

Usage:
The easiest way to use it would be to simply run the gen.sh script. As it is right now the script will probably fail, since the pictures are missing, which I will not provide right now. But when it works, it will output a pdf and a png with the receipt. An easier way to generate them will be made together with a colleague of mine, although this will probably not be release to the public, and only presented to the company that I have targeted in this little research.

bash gen.sh sek

 

Requirements

The soul LaTeX package
The lmodern fonts for LaTeX

Result:

The EAN code has been made a bit smaller now so that it can’t be scanned in the stores.
receipt

This concludes my research for now on this topic.
I have a solution for this problem that I will write about in another post later.

Click HERE for part 2