Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot delete Java Card applet

I am quite new to Java Card but after some reading, my first Applet is working quite well.....until today. I refactored a bit and inserted an OwnerPIN object to my Applet. And now I can write the Applet once, but the second time, the deletion does not work anymore. Here is my output:

Java Card 2.2.2 Class File Converter, Version 1.3
Copyright 2005 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms.
mode_201
gemXpressoPro
enable_timer
establish_context
command time: 16 ms
card_connect
command time: 187 ms
select -AID A000000018434D00
command time: 78 ms
open_sc -security 1 -keyind 0 -keyver 0 -key 47454d5850524553534f53414d504c45 -keyDerivation visa2
command time: 328 ms
delete -AID  0102030405060708090000
delete() returns 0x80206985 (6985: Command not allowed - Conditions of use not satisfied.)
command time: 31 ms
delete -AID  01020304050607080900
delete() returns 0x80206985 (6985: Command not allowed - Conditions of use not satisfied.)
command time: 47 ms
get_status -element e0


List of applets (AID state privileges)
a000000018434d00 1 9e
a0000000620001 1 0
a0000000620002 1 0
a0000000620003 1 0
a0000000620101 1 0
a000000062010101 1 0
a0000000620102 1 0
a0000000620201 1 0
a0000000620209 1 0
a0000000620202 1 0
a000000018100109 1 0
a00000001810010b 1 0
a00000001810010a 1 0
a0000000030000 1 0
a000000018100106 1 0
a000000018100201 1 0
a000000018100101 1 0
a00000015100 1 0
a000000018100108 1 0
a0000000181001ff 1 0
a000000018100501 1 0
a000000018100502 1 0
a000000018100401 1 0
5365637572697479 1 0
a0000000035350 1 0
01020304050607080900 1 0
0102030405060708090000 7 0
command time: 187 ms
install -file mycap.cap -sdAID A000000018434D00 -nvCodeLimit 4096 -instParam 2265
install_for_load() returns 0x80206985 (6985: Command not allowed - Conditions of use not satisfied.)

update As requested, parts of my code:

public class MyApplet extends Applet
{

   private static final byte MY_CLA = (byte)0xB0;

  ...
    private final static byte VERIFY_INS = (byte)0x40;
 ...
    private final static byte NEED_VERIFICATION_INS = (byte)0x47;
    private final static byte GET_INSTALL_PARAMS_INS = (byte)0x50;
    private final static byte GET_REMAINING_PIN_TRIES_INS = (byte)0x60;

    private final static byte PIN_TRY_LIMIT = (byte)0x03;
    private final static byte MAX_PIN_SIZE = (byte)0x08;

    // signal that the PIN verification failed
    private final static short SW_VERIFICATION_FAILED = 0x6300;
    private final static short SW_PIN_VERIFICATION_REQUIRED = 0x6301;

   ...
    private static byte[] testdata;
    private OwnerPIN m_pin;
    private byte[] m_array;
    private short m_offset;
    private byte m_length;

    private MyApplet(byte[] bArray, short bOffset, byte bLength)
    {
       ...
        m_pin = new OwnerPIN(PIN_TRY_LIMIT, MAX_PIN_SIZE);
        // m_array = bArray;
        // m_offset = bOffset;
        // m_length = bLength;
        register();
    }

    public boolean select()
    {
        if (m_pin.getTriesRemaining() == 0)
        {
            return false;
        }
        return true;
    }

    public void deselect()
    {
        m_pin.resetAndUnblock();
    }

    private void adminRest()
    {
        m_pin.resetAndUnblock();
        return;
    }


    private void getInstallParams(APDU apdu)
    {
        try
        {
            byte[] buffer = apdu.getBuffer();
            // inform the JCRE that the applet has data to return
            short le = apdu.setOutgoing();
            // set the actual number of the outgoing data bytes
            apdu.setOutgoingLength(((short)testdata.length));
            apdu.sendBytesLong(testdata, (short)0, (short)testdata.length);

        }
        catch (APDUException e)
        {
            // TODO Auto-generated catch block
        }
        catch (TransactionException e)
        {
            // TODO Auto-generated catch block
        }
        catch (ArrayIndexOutOfBoundsException e)
        {
            // TODO Auto-generated catch block
        }
        catch (NullPointerException e)
        {
            // TODO Auto-generated catch block
        }
    }

    /**
     * Get number of remaining pin tries
     * 
     * @param apdu
     */
    private void getPinTriesRemaining(APDU apdu)
    {
        try
        {
            byte[] buffer = apdu.getBuffer();
            // inform the JCRE that the applet has data to return
            apdu.setOutgoing();
            // set the actual number of the outgoing data bytes
            apdu.setOutgoingLength((byte)2);

            // write the PinTriesRemaining into the APDU buffer at the offset 0
            Util.setShort(buffer, (short)0, m_pin.getTriesRemaining());

            // send the 2-byte balance at the offset
            // 0 in the apdu buffer
            apdu.sendBytes((short)0, (short)2);
        }
        catch (APDUException e)
        {
            // TODO Auto-generated catch block
        }
        catch (TransactionException e)
        {
            // TODO Auto-generated catch block
        }
        catch (ArrayIndexOutOfBoundsException e)
        {
            // TODO Auto-generated catch block
        }
        catch (NullPointerException e)
        {
            // TODO Auto-generated catch block
        }

    } // end of getPinTriesRemaining method

    /**
     * Verification method to verify the PIN
     * 
     * @param apdu
     */
    private void verify(APDU apdu)
    {

        byte[] buffer = apdu.getBuffer();

        // receive the PIN data for validation.
        byte byteRead = (byte)(apdu.setIncomingAndReceive());

        // check pin
        // the PIN data is read into the APDU buffer
        // starting at the offset ISO7816.OFFSET_CDATA
        // the PIN data length = byteRead
        if (!m_pin.check(buffer, ISO7816.OFFSET_CDATA, byteRead))
        {
            ISOException.throwIt(SW_VERIFICATION_FAILED);
        }

    } // end of verify method

    /**
     * Installs the applet.
     *
     * @param bArray array with installation parameters.
     * @param bOffset offset into array.
     * @param bLength the length of the parameters.
     */
    public static void install(byte[] bArray, short bOffset, byte bLength)
    {
        testdata = new byte[bLength];
        Util.arrayCopy(bArray, (short)0, testdata, (short)0, bLength);
        new MyApplet(bArray, bOffset, bLength);
    }


    /**
     * Processes an incoming APDU.
     *
     * @param apdu the APDU.
     */
    public void process(APDU apdu)
    {

        byte l_transferredBuffer[] = apdu.getBuffer();
        // Get the CLA; mask out the logical-channel info.
        l_transferredBuffer[ISO7816.OFFSET_CLA] = (byte)(l_transferredBuffer[ISO7816.OFFSET_CLA] & (byte)0xFC);
        if ((l_transferredBuffer[ISO7816.OFFSET_CLA] == 0) && (l_transferredBuffer[ISO7816.OFFSET_INS] == (byte)(0xA4)))
        {
            ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
        }
        if (l_transferredBuffer[ISO7816.OFFSET_CLA] != MY_CLA)
        {
            ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
        }

        byte l_ins = l_transferredBuffer[ISO7816.OFFSET_INS];

      ...
        else if (l_ins == VERIFY_INS)
        {
            verify(apdu);
        }
        ...
        else if (l_ins == NEED_VERIFICATION_INS)
        {
            isNeedVerification(apdu, l_transferredBuffer);
        }
        else if (l_ins == GET_INSTALL_PARAMS_INS)
        {
            getInstallParams(apdu);
        }
        else if (l_ins == GET_REMAINING_PIN_TRIES_INS)
        {
            getPinTriesRemaining(apdu);
        }
        else
        {
            ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
        }
    }

    private void isNeedVerification(APDU apdu, byte[] buffer)
    {
        // The structure for the response request is
        // ---------------------
        // | response |
        // | 1 byte |
        // ---------------------
        getRequest(apdu, buffer);
        byte[] l_needVerificationResponse = new byte[1];
        boolean l_validated = m_pin.isValidated();
        if (l_validated)
        {
            l_needVerificationResponse[0] = 1;
        }
        else
        {
            l_needVerificationResponse[0] = 0;
        }
        apdu.setOutgoing();
        apdu.setOutgoingLength((short)l_needVerificationResponse.length);
        apdu.sendBytesLong(l_needVerificationResponse, (short)0, (short)l_needVerificationResponse.length);
    }

    private byte[] getRequest(APDU apdu, byte[] transferredBuffer)
    {
        short l_setIncomingAndReceive = apdu.setIncomingAndReceive();
        byte[] request = new byte[l_setIncomingAndReceive];
        for (short i = 0; i < request.length; i++)
        {
            short l_j = (short)(ISO7816.OFFSET_CDATA + i);
            request[i] = transferredBuffer[l_j];
        }
        return request;
    }
... 
}

Is my card blocked? I can see that my AID runs in state 7...what does that mean. Is it connected to my OwnerPIN object?

Thanks for your help! Jan

like image 334
Jan Engler Avatar asked Jan 07 '23 12:01

Jan Engler


1 Answers

The problematic line is this one:

private static byte[] testdata;

Remember, static references to persistent objects are EVIL AND DANGEROUS. They are always there, even after the Applet instance is uninstalled and garbage collected. That is why the referenced object (byte array in your case) is never garbage collected, the persistent memory is never released and thus the deletion command fails.

If you can avoid using static references, do so.

If you cannot avoid it, use AppletEvent interface:

public final class MyApplet extends Applet implements AppletEvent {

    private static byte[] testdata;
    ...

    //This method is called in the moment of uninstallation
    public final void uninstall() {
        testData = null; //release the reference --> testData can be GC
    }
    ...

}
like image 196
vojta Avatar answered Jan 17 '23 18:01

vojta