How to Self-Lock a Javacard Applet
Asked Answered
B

5

10

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?

Budworth answered 10/5, 2016 at 16:18 Comment(3)
Did you try to contact card vendor?Corvine
Yes, no feedback yet. Will post here if they're is an updateBudworth
Update finally received from NXP. It is not possible with JCOP 2.4.x cards and Global Platform API. See Michael Roland's extensive answer for information and also the best valid workaround! Thanks everyone who participated in solving this issue!Budworth
P
9

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.

Pricefixing answered 11/5, 2016 at 7:24 Comment(8)
Thank you very much for your detailed answer, Michael! My card is currently using GP 2.2 and i tried your suggestion using GPSystem.getRegistryEntry(null).setState(GPSystem.APPLICATION_LOCKED);. Unfortunately, it does not work. As the card is capable of using GP 2.2.1 I will try to integrate the new API+export file into the IDE.Budworth
@PaulBastian That's interesting. Are you sue that the card implements GP 2.2.1? I've updated my answer to include another locking mechanism that should probably work (not tested though).Pricefixing
I'm using JCOP 2.4.2.R3 and the user documentation says it supports GP 2.2.1. Your suggestion seems to be legit workaround, I think that should work. However, I will test with GP 2.2.1 to be more standard compliant once I have a JCOP tools version that supports the new API(have to wait for NXP though...)Budworth
@PaulBastian Just an idea: would it help to give the applet the 'Global Lock' privilege for the GPSystem.getRegistryEntry(null).setState(...) call?Corvine
@Corvine No it didnt help. The 'Global lock' privilege is for locking the whole card. It works and might be a good substitution for the applet lockingBudworth
@PaulBastian For locking the whole card there is 'Card Lock'. The 'Global Lock' is a different one, citing "Application may lock or unlock any Application"...(never used that one thought)Corvine
@PaulBastian Since I finally got access to some cards that seem to implement at least GP 2.2.1, I added some more test results. See the updates in my answer.Pricefixing
Thanks a lot for your help and effort Michael! I got the reply from NXP now, that it is not possible with JCOP2.4.x. But your workaround solution is easy and successful!Budworth
C
5

(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.

Corvine answered 11/5, 2016 at 16:53 Comment(3)
That's an interesting interpretation. Did you verify that this works on an actual card? (@PaulBastian ?)Pricefixing
This interpretation of Table 11-4 seems really to make sense, because only that way can be ensured to transition back into the previous state. I tested this under my JCOP GP 2.2 card and it fails. Transitions to 0x0F work for sure, I always use that state to signalize the applet is fully personalized. I will keep you updated once i have the possibility to use GP 2.2.1Budworth
@PaulBastian Maybe GPSystem.getRegistryEntry(JCSystem.getAID()).setState((byte)(GPSystem.getCardContentState() | GPSystem.APPLICATION_LOCKED)) is the way to go. And it is in line with the setState() method documentation mentioning application locking itself...by the way, I can't find any reference that getRegistryEntry() argument can be nullCorvine
M
3

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>
Morman answered 10/5, 2016 at 22:42 Comment(4)
Yes I used the privilege as well in jcop shell with install --cm-lock. Still does not work.Budworth
To clarify: locking and unlocking from the Card Manager does work and the applet has the privilegeBudworth
(AFAIK) the CardLock privilege is for GPSystem.lockCard() call.Corvine
Yes I think this might be true. The Card locking mechanism also works, so I might use this one instead of application lockingBudworth
P
3

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.

Phosphoric answered 14/6, 2016 at 11:40 Comment(2)
Does this mean GPSystem.setCardContentState(APPLICATION_STATE_LOCKED); in non-effective on any JCOP 2.4.x card and it is not possible to lock the applet from within the code with this command?Budworth
Correct, since JCOP 2.4.x does not support Global Lock for applications and GP 2.1.1 does not allow self-locking of applications, it is not possible to GP lock an application using GP API.Phosphoric
B
2

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;
        }
    }   
}
Boardman answered 10/5, 2016 at 20:29 Comment(4)
+1 - Moving it to the select method and throwing an exception in the line of appletLocked toggling, is more efficient, I guess. Anyway, the OP can't unlock the applet using this method.Quickie
Exactly! I want to have the possibility to unlock the applet via global platformBudworth
@Quickie Well, he can't, but unlocking with asymmetricly signed command can be easily implemented. I always prefer solutions I can easily customize - GP locking is great, but... you could need the locked applet answering some APDUs one day and you won't be able to do this with GP locking. Moreover, as Michael says in his answer, you are restricted to certain GP versions.Boardman
Beware that this answer it correct in a logical sense, but relying on a single boolean is tricky when it comes to protection against fault injection techniques.Acreage

© 2022 - 2024 — McMap. All rights reserved.