Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Self-Lock a Javacard Applet

My question is whether it is possible to lock an applet from within the code of the applet itself as a countermeasure to detected manipulations from within the code.

The obvious choice is to use GPSystem.lockCard(); and it works, however I wonder if it is possible to only lock the applet. Also I can lock the applet itself from an authenticated session of the associated security domain. But is it possible from the applet code itself. It seems, given the GPSystem.setCardContentState(); method used with GPSystem.APPLICATION_LOCKED, so I also tested that but it does not work.

Rereading the description of the GP Card specification 2.2 PDF:

The OPEN shall reject any transition request from the Life Cycle State LOCKED;

In my Eclipse, the JavaDoc says:

The OPEN shall reject any transition request to the Life Cycle State LOCKED

What's going on here?

like image 246
Paul Bastian Avatar asked May 10 '16 16:05

Paul Bastian


5 Answers

It's interesting to see how this mechanism evolved from GlobalPlatform Card specification 2.1.1 to 2.2.1 (still the same in 2.3):

  1. In GP 2.1.1, only the card issuer (off-card entity) or the OPEN (as a result of "exceptions") is allowed to initiate locking of an application:

    The Card Issuer has a mechanism to disable the continued execution status of an on-card Application. This mechanism may be invoked from within the OPEN based on exceptions handled by the OPEN or from the use of externally invoked commands. The Card Issuer is the only entity that may initiate the locking of an Application.

    The method GPSystem.setCardContentState() is clearly defined to allow only state changes to application specific life-cycle states (values between 0x07 and 0x7F with the lowest 3 bits set). Since the constant for APPLICATION_LOCKED in later specifications is 0x80 setting this state is not allowed. This is also made clear in the notes to this method:

    • The OPEN shall reject any transition request to the Life Cycle States INSTALLED or LOCKED.

    Consequently, trying to set the application state to locked from within the application itself must fail on a card implementing GP 2.1.1.

    UPDATE (2016-05-20): I tested this on a few NXP JCOP cards (that are claimed to comply to GP 2.1.1) and setting values that have the upper bit set or any of the lower 3 bits cleared indeed fails.

  2. This changed in GP 2.2. Now, an application is allowed to lock itself:

    The card has a mechanism to disable and subsequently re-enable the continued execution status of an on-card Application. This mechanism may be invoked from within the OPEN based on exceptions handled by the OPEN or from the use of externally invoked commands. An Application with Global Lock privilege, the Application itself or a directly or indirectly associated Security Domain are the only entities that may initiate the locking of an Application.

    The GP Card specification does not require an application to hold any specific permission to lock itself.

    Unfortunately, the API specification for the method GPSystem.setCardContentState() is still not quite clear. First, the description of the method still states that only values between 0x07 and 0x7F with the lowest 3 bits set must be allowed:

    This method sets the Application specific Life Cycle State of the current applet context. Application specific Life Cycle States range from 0x07 to 0x7F as long as the 3 low order bits are set.

    Second, there are deviating notes in the API documentation thats part of appendix A of the GP Card specification 2.2 document and the JavaDoc in the API export files. While the notes in the specification were changed to:

    • The OPEN shall reject any transition request to the Life Cycle State INSTALLED;
    • The OPEN shall reject any transition request from the Life Cycle State LOCKED;

    The notes in the API export files (GPSystem.java and JavaDoc) remained the same as in GP 2.1.1.

    Consequently, if this method was implemented according to the specification, it should still reject setting the application life-cycle state to APPLICATION_LOCKED.

    UPDATE (2016-05-20): I tested this on a NXP J3D081 (JCOP v2.4.2 R2) card (that is claimed to comply to GP 2.2). Setting values that have the upper bit set or any of the lower 3 bits cleared, unfortunately, fails.

    However, there is also the method GPRegistryEntry.setState(). The documentation of this method states that:

    • A transition request to Life Cycle state other than APPLICATION_LOCKED and APPLICATION_UNLOCKED shall be accepted only if the invoking Application corresponds to this GPRegistryEntry;
    • An Application shall be able to lock and shall not be able to unlock itself;

    Thus, it would be interesting to see if the following worked on the same card where using setCardContentState() failed:

    GPSystem.getRegistryEntry(null).setState(GPSystem.APPLICATION_LOCKED);
    

    UPDATE (2016-05-20): I tested this on a NXP J3D081 (JCOP v2.4.2 R2) card (that is claimed to comply to GP 2.2). Unfortunately, this fails as well. Btw. it does not seem to make a difference if null or JCSystem.getAID() is used as parameter for getRegistryEntry().

    UPDATE (2016-06-14): According to Paul Bastian, an NXP representative has confirmed that applications cannot set themselves to locked state on JCOP v2.4.x cards.

    UPDATE (2016-06-06): I tested this on a Infineon SLE97CNFX card (that is claimed to comply to GP 2.2.1) and it worked. I could successfully set the state to locked by using APPLICATION_LOCKED (0x80). The state is then set to previous_state | 0x80. Trying to set other state values that have the upper bit set (e.g. 0x8F) does not work (just as I expected).

  3. In GP 2.2.1, the documentation of the method GPSystem.setCardContentState() was changed (again). The change note clearly indicates that the method was updated to now allow an application to lock itself (export file version 1.5. maps to GP 2.2.1):

    • export file version 1.5: this method now allows the application associated with the current applet context to lock itself.

    The method definition was changed to:

    This method allows the application associated with the current applet context to change its state to an application specific life cycle state or to lock itself. An application cannot unlock itself using this method.

    The value range for the state parameter passed to the method now explicitly includes the value of APPLICATION_LOCKED:

    bState - an application specific life cycle state (0x07 to 0x7F with 3 low order bits set), or APPLICATION_LOCKED (0x80).

    Consequently, cards implementing GP 2.2.1 or higher should eventually allow applications to change their own life-cycle state to locked using the method GPSystem.setCardContentState().

    UPDATE (2016-06-06): I tested this on a Infineon SLE97CNFX card (that is claimed to comply to GP 2.2.1 (or is it 2.3?)) and it worked. I could successfully set the state to locked by using APPLICATION_LOCKED (0x80). The state is then set to previous_state | 0x80. Trying to set other state values that have the upper bit set (e.g. 0x8F) does not work (just as I expected).

An alternative solution

What you could do to overcome your problem without being able to set the application life-cycle to state APPLICATION_LOCKED, is to use application-specific life-cycle states:

public class LockableApplet extends Applet {

    [... applet installation / instantiation code ...]

    private static final byte APPLICATION_STATE_UNLOCKED = (byte)0x07;
    private static final byte APPLICATION_STATE_LOCKED = (byte)0x7F;

    public boolean select() {
        if (GPSystem.getCardContentState() == APPLICATION_STATE_LOCKED) {
            return false;
        }

        return true;
    }

    public void process(APDU apdu) {
        if (selectingApplet()) {
            return;
        }

        if (GPSystem.getCardContentState() == APPLICATION_STATE_LOCKED) {
            ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
        }

        [... applet logic code ...]

    }   
}

Upon detecting a problem that should cause your application to be locked, you could lock the applet with the following call:

GPSystem.setCardContentState(APPLICATION_STATE_LOCKED);

You could later unlock the application again using a SET STATUS command through the security domain.

like image 55
Michael Roland Avatar answered Nov 23 '22 16:11

Michael Roland


(Buyer beware: It seems this way simply does not work -- see comments)

(In context of GlobalPlatform Card Specification 2.2.1)

You must obey the Application Life Cycle State rules depicted in figure 5-2 (the arrow marked '5' applies here).

The correct way should be:

GPSystem.setCardContentState((byte)(GPSystem.getCardContentState() | GPSystem.APPLICATION_LOCKED));

or

GPSystem.getRegistryEntry(JCSystem.getAID()).setState((byte)(GPSystem.getCardCo‌​ntentState() | GPSystem.APPLICATION_LOCKED))

The 0x80 life cycle state is invalid for an application. See table 11-4 ( at least the b1 and b2 bits must be set, the b3 bit probably as well).

EDIT>

(I confess to write this answer based solely on the remembrance of fact that OPEN keeps the original state from which the entity was locked)

I am quite curious about this so I did some tests using the following applet (excerpt):

public void process(APDU apdu) {
    byte[] buffer = apdu.getBuffer();

    if(selectingApplet()) {
        return;
    }

    short claIns = Util.getShort(buffer, ISO7816.OFFSET_CLA);
    switch(claIns) {
        case (short) 0x8007:
            buffer[0]=GPSystem.getCardContentState();
            if(buffer[0]==buffer[ISO7816.OFFSET_P1]) {
                if(GPSystem.setCardContentState(buffer[ISO7816.OFFSET_P2])) {
                    buffer[1]=0x01;
                } else {
                    buffer[1]=0x00;
                }
            } else {
                buffer[1]=(byte)0xFF;
            }
            buffer[2]=GPSystem.getCardContentState();
            apdu.setOutgoingAndSend((short)0, (short)3);
            return;
        default: {
            ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
            return;
        }
    }
}

And the following APDUs:

8007070F03 // To test transition into Application Specific State
80070F8F03 // To test my theory
80070F8003 // To test the GPSystem.APPLICATION_LOCKED constant directly

The results for my set of cards (Gemalto, Morpho, JCOP -- unfortunately all of them are GP 2.1.1) are in line with Michael Roland's great answer and GP specs -- the application's attempt to block itself is refused.

Received response APDUs for all GP 2.1.1 cards:

8007070F03 -> 07010F9000 // Succeeded in transition from `07` to `0F`
80070F8F03 -> 0F000F9000 // Failed transition from `0F` to `8F`
80070F8003 -> 0F000F9000 // Failed transition from `0F` to `80`

Just a note: This tool is quite useful to determine the implemented GP version as it parses the Card Recognition Data.

like image 31
vlp Avatar answered Nov 23 '22 15:11

vlp


Yes. It is common and intended operation of a GlobalPlatform application. You must install your application with the right privilege (gp -install -privs CardLock) and the code to do it is:

GPSystem.setCardContentState(GPSystem.APPLICATION_LOCKED);

You can later unlock the application with

gp -unlock-applet <aid>
like image 21
Martin Paljak Avatar answered Nov 23 '22 16:11

Martin Paljak


There is no compliance to GP core spec per se. A product is GP compliant to a GP configuration. GP configuration are not free of charge. JCOP 2.4.x products are compliant to GP 2.2.x 'Mapping Guidelines of Existing GP 2.1.1 Implementation on v2.2.1' configuration. As the name suggests, this configuration is for backward compatibility mapping. Basically JCOP 2.4.x products are GP 2.1.1 compliant products only (with a couple features from GP 2.2.x). Global Lock privilege is optional for applets.

like image 42
lexdabear Avatar answered Nov 23 '22 14:11

lexdabear


Yes, it is quite simple: use a private static boolean flag and check it in the beginning of the process(APDU apdu) method:

public class MiniApplet extends Applet {

    public static void install(byte[] bArray, short bOffset, byte bLength) {
        new MiniApplet();
    }

    protected MiniApplet() {
        register();
    }

    private static final short SW_APPLET_IS_LOCKED = (short) 0x9199; //any error SW
    private static boolean appletLocked = false; //static -> faster access, this flag is checked each call! "private" modifier is VERY important!

    public void process(APDU apdu) {
        if (selectingApplet()) {
            return; //it is a good practice not to throw any exceptions on SELECT command
        }

        if (appletLocked) { 
            ISOException.throwIt(SW_APPLET_IS_LOCKED);
        }

        if (attackDetected()) { //implement your attack detection
            appletLocked = true;
        }
    }   
}
like image 37
vojta Avatar answered Nov 23 '22 16:11

vojta