JC101-6C: Specifying the APDU’s

In our previous posts, we have written a class that is able to manage login information as password entries. This time, we will look at our application’s user interface. Well, user interface may be a bit overstated, since we will simply be defining a way to exchange information between the card and a terminal to which the card is connected.

The first step is to define a protocol, i.e., to define the syntax and semantics of the messages that will be exchanged between the card and the terminal. After that, we will implement this protocol on the card side, by defining an applet that is able to process these messages. But this will be in another post

The protocol

The first thing to do is to define the operations that will need to be performed. We will need the following basic operations:

  • Add a password entry. Allocates and initializes a new password entry.
  • Retrieve a password entry. Retrieves the password entry that corresponds to a given identifier.
  • Delete a password entry. Deletes the password entry that corresponds to a given identifier.
  • List identifiers. Lists the identifiers that match a given expression.

We then need to define how these operations will work. For this, we will describe their nominal behavior (when everything goes well), but we also need to define all the erroneous behaviors that we can think off. This sounds trivial, but it is very often overlooked by Java Card developers, and the consequences are usually quite bad. They can go from simple behavioral issues to critical security issues, depending on the erroneous situation that goes undetected and/or that is not properly processed. With our first basic operations, we will need the following errors:

  • Invalid data. The data is not valid; for instance, one of the elements is too long.
  • Duplicate identifier. An attempt is made to add a password entry with an identifier that already exists.
  • Identifier not found. A search or delete operation mentions an identifier that is not associated to any password entry.
  • Out of memory. The allocation of the password entry failed.

In addition, we will also need the usual transport-level errors. APDU’s are a low-level protocol, so the application needs to deal with a host of transport-level errors, indicating that the data does not have the expected length, that a command is not supported, that an option is not supported, etc. Our application will generate all these errors.

Global definitions

We can now start the definition of the protocol, by first defining the generic part of the messages and the corresponding error statuses.

Command APDU

The command APDU includes the following elements:

  • CLA. Identifies a category of instructions, as well as some transport-level options, such as secure messaging and logical channel information.
  • INS. Identifies a given instruction; we will use one per command.
  • P1 and P2. Describes the options of a command. For most instructions, it will be set to 00 00.
  • Lc. The length of the incoming data (between 0 and 255).
  • Incoming data. The incoming data (between 0 and 255 bytes).
  • Le. The expected length of the outgoing data.

A typical command (with incoming data) includes all the elements with the grey background (the Le element is exchanged just before sending the outgoing data). These elements are stores in the APDU buffer.

Response APDU

The response APDU includes the following elements:

  • Lr.
  • The actual length of the outgoing data.

  • Outgoing data. The outgoing data (between 0 and 256 bytes)
  • SW1 and SW2.

Only the outgoing data itself is usually (but not always) stored in the APDU buffer. The Lr value is defined in the negotiation of the setOutgoing API, and the status word (SW1-SW2) is sent as a parameter to a thrown ISOException. The generic status words will be as follows:

  • 6E00. Invalid class byte.
  • 6D00. Instruction not supported.
  • 6A86. Invalid parameters (P1,P2).
  • 6984. Invalid data.

The precise semantics of the two last statuses will be refined for each command.

Add a password entry

This command is used to define a new password entry.

CLA 00, the default value
INS 30, the specific instruction byte
P1-P2 00 00, since no options are required
Incoming Data A TLV entry that describes three required elements, in the mandatory order (identifier, user name, password)

The incoming data will be structured as a sequence of TLV (tag-length-value) records, using a one-byte tag and a one-byte length. We will use proprietary tags, with values F1 for identifiers, F2 for user names, and F3 for passwords. For instance, the encoding for a login/password “bob/pass” and identifier “Home” will be as follows, with the tags in bold and the length in italics:

F1 04 48 6F 6D 65 F2 03 62 6F 62 F3 04 70 61 73 73

The processing to be performed for this command is as follows:

  • Check the command parameters (P1-P2).
    1. If P1-P2 is not 00 00, send back status word 6A86
    2. If Lc is not at least 9, send back status word 6984
  • Receive the incoming data.
  • Check the validity of the data elements.
    1. If the first tag is not F1, send back status word 6984
    2. If the first length is smaller than 1 or greater than the maximum size for an identifier, send back status word 6984
    3. If Lc is not greater than the first length plus 8, send back status word 6984
    4. If the second tag is not F2, send back status word 6984
    5. If the second length is smaller than 1 or greater than the maximum size for a user name, send back status word 6984
    6. If Lc is not greater than the first length plus the second length plus 7, send back status word 6984
    7. If the third tag is not F3, send back status word 6984
    8. If the second length is smaller than 1 or greater than the maximum size for a password, send back status word 6984
    9. If Lc is not equal to the first length plus the second length plus the third length plus 6, send back status word 6984
  • Search the identifier in the current base.
    1. If the identifier can be found, send back status word 6F01
  • Allocate a new password entry.
    1. If the allocation fails, send back status word 6F02
  • Initialize the new password entry.
    1. Make sure that this initialization is atomic with the allocation in order to avoid memory leaks

This definition is a bit paranoid, especially, in its definition of the format checks. However, I can’t stress enough that, like any other server-side code, Java Card code must very carefully check the format of all incoming data. Otherwise, all kinds of unexpected errors may happen, which may in turn lead to all kinds of behavior/security issues if these errors are not handled properly. It seems like a good principle to catch problems as early as possible.

On this particular example, it is possible to remove some the two first checks on the length of the incoming data. But this is only possible because (1) the Java Card platform guarantees that the APDU buffer is reset to all 00‘s between two APDU’s, and (2) the sum of the maximum allowed lengths for the various elements does not exceed 256. And even then, I am not that comfortable about guaranteeing that I did not forget any hypothesis. This is why my suggestion is indeed to implement the full format checks.

The last part consists in defining the possible responses to the message. This is quite simple, since there is no outgoing data (the standard answers are not mentioned here):

Lr Out data SW Outcome
00 None 9000 The command succeeded
00 None 6984 Bad data TLV format
00 None 6A8A Duplicate identifier
00 None 6A84 Out of space

Note that we have been using status words that are defined in the ISO7816 standard. This is not mandatory, but it can be an advantage to use status words that have meanings that are close to our intention. For instance, here, we have used the status words related to files.

Retrieve a password entry

This command is used to retrieve a password entry from the table.

CLA 00, the default value
INS 32, the specific instruction byte
P1-P2 00 00, since no options are required
Request Data A TLV entry that describes the requested identifier

The incoming data will be structured as a TLV record, using a one-byte tag and a one-byte length. We will use a proprietary tag, with value F1 for identifiers. For instance, a command requesting the login and password with identifier “Home” will be as follows:

00 32 00 00 F1 04 48 6F 6D 65

The processing to be performed for this command is as follows:

  • Check the command parameters (P1-P2).
    1. If P1-P2 is not 00 00, send back status word 6A86
    2. If Lc is not at least 3, send back status word 6984
  • Receive the incoming data.
  • Check the validity of the data elements.
    1. If the first tag is not F1, send back status word 6984
    2. If Lc is not equal to the first length plus 2, send back status word 6984
  • Search the identifier in the current base.
    1. If the identifier cannot be found, send back status word 6A82
  • Copy the password entry into the APDU buffer.
    1. This operation should not lead to any error

The last part consists in defining the possible responses to the message. This is a bit more complex than for the first command, since there is outgoing data:

Lr Out data SW Outcome
nn A TLV with user name and password 9000 The command succeeded
00 None 6984 Bad data TLV format
00 None 6A82 Identifier not found

The outgoing data will be structured as a TLV record, using a one-byte tag and a one-byte length. We will use proprietary tags, with values F2 for user names, and F3 for passwords. For instance, the encoding for a login/password “bob/pass” will be as follows, with the tags in bold and the length in italics:

F2 03 62 6F 62 F3 04 70 61 73 73

Delete a password entry

This command is used to delete a password entry from the table.

CLA 00, the default value
INS 34, the specific instruction byte
P1-P2 00 00, since no options are required
Id Data A TLV entry that describes the identifier of the entry to delete

The incoming data will be structured as a TLV record, using a one-byte tag and a one-byte length. We will use a proprietary tag, with value F1 for identifiers. For instance, a command requesting the deletion of the entry with identifier “Home” will be as follows:

00 34 00 00 F1 04 48 6F 6D 65

The processing to be performed for this command is as follows:

  • Check the command parameters (P1-P2).
    1. If P1-P2 is not 00 00, send back status word 6A86
    2. If Lc is not at least 3, send back status word 6984
  • Receive the incoming data.
  • Check the validity of the data elements.
    1. If the first tag is not F1, send back status word 6984
    2. If Lc is not equal to the first length plus 2, send back status word 6984
  • Search the identifier in the current base.
    1. If the identifier cannot be found, send back status word 6A82
  • Delete the password entry.
    1. Reset the data and move it into the list of recycled entries

The last part consists in defining the possible responses to the message. This one is simple, since there is no outgoing data:

Lr Out data SW Outcome
00 None 9000 The command succeeded
00 None 6984 Bad data TLV format
00 None 6A82 Identifier not found

List identifiers

The last command is used to list all the identifiers currently defined in the database. It is a very simple command, with no incoming data:

CLA 00, the default value
INS 36, the specific instruction byte
P1 00, since no options are required
P2 00 for a new request, and 01 to get the rest of a result

The processing associated to this command

  • Check the command parameters (P1-P2).
    1. If P1 is not 00, send back status word 6A86
    2. If P2 is 01 and there is no result data on hold, send back status word 6A86
    3. If P2 is not 00, send back status word 6A86
  • If the database is empty, send back status word 6A82
  • Compute the global size of the return data (sum of all identifier lengths + 2 times the number of identifiers for the TLV meta-data
  • Return the outgoing data
    1. If the length is under 256, copy in the APDU buffer and send in a single go.
    2. Otherwise, send in chunks of around 255 bytes with a 6310 status word, and wait for more commands requesting the rest of the data.

The last part consists in defining the possible responses to the message. This is a bit more complex than for the first command, since there is outgoing data:

Lr Out data SW Outcome
nn A list of identifier TLV records 9000 The command succeeded
00 None 6A82 Database is empty

The outgoing data will be structured as a list of TLV records, using a one-byte tag and a one-byte length. We will use a proprietary tag, with values F1 for identifiers. For instance, the encoding for two identifiers “Home” and “Office” will be as follows, with the tags in bold and the length in italics:

F1 04 48 6F 6D 65 F1 06 4F 66 66 69 63 65

Wrap-up

I have tried in this post to define a consistent application specification. Of course, this specification is incomplete, since it is purely functional and it does not include any security feature. Nevertheless, this is quite a long post, even though it corresponds to four very simple commands. The intention is here to show that writing the specification for a card application is both difficult and tedious, and that it takes a lot of time and effort. We will keep proving this in the following posts, because I am quite convinced that some fixes in this spec will be required when implementing it.

For a good example of how to define commands, it is interesting to look at a complex example. One of the best publicly available specifications is undoubtedly the GlobalPlatform Card Specification. Version 2.2 of this spec is very precise and complete (and very big). It is also interesting because it has evolved over the years, and it has become more precise in every version. Definitely an example to follow.

4 Comments

  • lexdabear wrote:

    Good to see to see the process of a JC Applet interface definition from the beginning to the end.

    Some remarks:
    – I do not understand the reasoning behind Lr in the response. The application must set the offset and length of the APDU buffer in the APDU.sendBytes() anyway. Besides having always a data field (to stay consistent: no data means Lr = 0) the data field length is reduced by one byte.
    – You mention that this is purely functional, without security features, but .. I think it should always be forbidden to retrieve secret data like keys, or in this example the password itself.
    – Do you think it’s a good design that an applet provides information about it’s capabilities in the response to the SELECT APDU? For example the file control information could include the commands supported and the methods behind it.
    – I would add that the application developer has to be aware of status words which might conflict with the ones already defined in ISO 7816 or JCRE (e.g. T=0 protocol handling).

  • At the ISO7816-4 layer, Lr is for me part of the response. Of course, depending on the protocol, it will be transmitted in various ways (like SW1 and SW2). This is also why I did not put it in gray (it does not have to be in the APDU buffer).

    About security, don’t worry, it will come later. I never said that this spec was the final one…

    About design, I am trying to avoid getting in big smart card debates. I hesitated a while before writing this post, because it is not Java enough for me. Now, I would admit that it is a good idea to return information on SELECT, but this goes beyond what I want to do in this tutorial.

    Finally, about status words, I am not sure what you mean. I have reused status words defined in the ISO7816-4 spec, on top of the T=0 protocol, and I don’t see any conflict.

  • Yalcin Akdogan wrote:

    Excellent tutorial. Thank you very much.

    just one correction !
    On TLV: length of F1 and F2 are changed.

    F1 03 48 6F 6D 65 F2 04 62 6F 62 F3 04 70 61 73 73

    correct TLV:
    F1 04 48 6F 6D 65 F2 03 62 6F 62 F3 04 70 61 73 73

    Best regards.

  • Dammit! 10 years in the smart card industry, and I still can’t write a TLV correctly … I did fix it, though.

Leave a Reply

Your email is never shared.Required fields are marked *