package jc;

import javacard.framework.*;
import javacardx.framework.*;

public class ID extends Applet{

	final static byte	SELECT	= (byte)0xA4;	//az ISO 7816-4 SELECT utasítás INS bájtja
	final static byte	VERIFY	= (byte)0x20;	//az ISO 7816-4 VERIFY utasítás INS bájtja
	final static byte	CHANGE_PIN = (byte) 0x10; //a ChangeUserPin utasítás INS bájtja
    final static short  ID_FID  = (short) 0x9102; //az idFile FID-je
    final static short	DATA_FID = (short) 0x9104; //a dataFile FID-je
    final static short	DATA_LENGTH = (short)32;	//the length of the dataFile
    final static short	ID_LENGTH =  8;			//length of Card ID
    final static byte   ISO_CLA = (byte)0x00;	//az ISO 7816-4 fájlkezelő utasítások CLA bájtja
    final static byte	ID_CLA	= (byte)0xAA;	//az ID applet CLA byte-ja
   	final static byte 	START	=	0;			//nullás offszet buffer-műveletekhez
	final static byte  	MASTER_PIN	= (byte)0x81;	//a Master PIN-t jelző P2 bájt 
	final static byte  	USER_PIN		= (byte)0x82;	//a User PIN-t jelző P2 bájt  
   	final static short 	SW_PIN_FAILED
		= (short)0x69C0;//SW az "érvénytelen PIN" esemény jelzésére
						//a státusz utolsó négybites egysége a még hátralevő
						//próbálkozások számával helyettesítődik.

    FileSystem      fs;
	TransparentFile	idFile;
	TransparentFile dataFile;
  	boolean justSelected;
  	private static OwnerPIN userPIN;
  	//csak ProxyPIN-t biztonságos megosztani más appletekkel,
  	//ugyanis annak nincs beállító, csak ellenőrző metódusa
  	public  static ProxyPIN p_userPIN;
  	private static OwnerPIN masterPIN;
  	public 	static ProxyPIN p_masterPIN; 
	final static byte[] idBuf  = {(byte)0x00,(byte)0x01,(byte)0x02,(byte)0x03,(byte)0x04,(byte)0x05,(byte)0x06,(byte)0x07};
	final static byte[] pinBuf = {(byte)0x01,(byte)0x01,(byte)0x01,(byte)0x02,(byte)0x02,(byte)0x02,(byte)0x03,(byte)0x03};
    private ID(){
    	//itt kell lefoglalni az összes szükséges memóriát
    	//így nem fogyhat el a memória futás közben

 		//az ISO fájlrendszer létrehozása
        fs = new FileSystem((byte)2);
        idFile = new TransparentFile(ID_FID,idBuf);
        fs.addChildFile(idFile);
   		idFile.setSecurity(File.ACCESS_READ,File.ALLOW_ANY); //mindenki olvashatja
		idFile.setSecurity(File.ACCESS_WRITE,File.ALLOW_NONE); //senki sem írhatja
        dataFile = new TransparentFile(DATA_FID,DATA_LENGTH);
        fs.addChildFile(dataFile);
		dataFile.setSecurity(File.ACCESS_READ,File.ALLOW_AUTH1);//User PIN megadása után olvasható
		dataFile.setSecurity(File.ACCESS_WRITE,File.ALLOW_AUTH2);//Master PIN megadása után olvasható		
		userPIN = new OwnerPIN((byte)5, (byte)8);
		masterPIN = new OwnerPIN((byte)1, (byte)8);
		masterPIN.updateAndUnblock(pinBuf,START,(byte)pinBuf.length);
		userPIN.updateAndUnblock(pinBuf,START,(byte)pinBuf.length);
		p_userPIN = new ProxyPIN(userPIN);
		p_masterPIN = new ProxyPIN(masterPIN);
		System.share(p_userPIN);
		System.share(p_masterPIN);
	}//ID()

    public static void install( APDU apdu ){
    	ID id = new ID();
    	id.register();
    }
    public boolean select(){
    	justSelected = true;
    	masterPIN.reset();
    	userPIN.reset();
    	fs.reset();
        return true;
    }

    public void process(APDU apdu){
        byte[] buffer = apdu.getBuffer();
        if (buffer[ISO.OFFSET_CLA] == ID_CLA) {
	        if (buffer[ISO.OFFSET_INS]==CHANGE_PIN)	
				processChangePIN(apdu);
			else ISOException.throwIt(ISO.SW_INS_NOT_SUPPORTED);
        }
        else if (buffer[ISO.OFFSET_CLA] == ISO_CLA) {
        	if (buffer[ISO.OFFSET_INS] == VERIFY){
				processVerifyPIN(apdu);
			}else
        	if ((buffer[ISO.OFFSET_INS] == SELECT) & justSelected) {
        	// most választották ki az appletünket
				processSelected();
			}else
		    //Minden más ISO-kompatibilis utasítást a FileSystem osztály kezel le
			if (!fs.process(apdu)) ISOException.throwIt(ISO.SW_INS_NOT_SUPPORTED);
		}
		else ISOException.throwIt(ISO.SW_CLA_NOT_SUPPORTED);
	}

	void processSelected(){
		justSelected = false;
	}

	void processVerifyPIN(APDU apdu)
	{
		byte[] buffer = apdu.getBuffer();
		byte pinLength = buffer[ISO.OFFSET_LC];
		byte triesRemaining	= (byte)0;
		apdu.setIncomingAndReceive();    //beolvassuk az adatokat
		byte pinType = buffer[ISO.OFFSET_P2];
		switch (pinType) {
			case MASTER_PIN: 
				if (!masterPIN.check(buffer, ISO.OFFSET_CDATA, pinLength)){
					triesRemaining = masterPIN.getTriesRemaining();
				}
				else {
					fs.setAuthFlag((byte)2, true);
					return;
				}
				break;
			case USER_PIN: 
				if (!userPIN.check(buffer, ISO.OFFSET_CDATA, pinLength)){
					triesRemaining = userPIN.getTriesRemaining();
				}
				else {
					fs.setAuthFlag((byte)1, true);
					return;
				}
				break;
			default: ISOException.throwIt(ISO.SW_INCORRECT_P1P2);
		}
		//a visszatérési kód utolsó négybites egysége a még hátralevő
		//próbálkozások számát adja meg.
		ISOException.throwIt((short)(SW_PIN_FAILED + triesRemaining));		   
	}

	void processChangePIN(APDU apdu){
		byte[] buffer = apdu.getBuffer();
		if (buffer[ISO.OFFSET_P1] != 0)
			ISOException.throwIt(ISO.SW_INCORRECT_P1P2);
		if (buffer[ISO.OFFSET_P2] != 0)
			ISOException.throwIt(ISO.SW_INCORRECT_P1P2);
		byte pinLength = buffer[ISO.OFFSET_LC];
		apdu.setIncomingAndReceive();    //beolvassuk az adatokat
		if (!masterPIN.isValidated())
			ISOException.throwIt(ISO.SW_CONDITIONS_NOT_SATISFIED);
		userPIN.updateAndUnblock(buffer,ISO.OFFSET_CDATA,pinLength); 	
	}

}//ID