This discussion is archived
9 Replies Latest reply: Feb 15, 2013 9:43 AM by safarmer RSS

Query about apdu.receiveBytes()

DTF Newbie
Currently Being Moderated
Hi!

I saw in this thread: RSA implementation basics ... Shane(safarmer) post the following code:


short lc = (short) (a[ISO7816.OFFSET_LC] & 0xff);
short read = apdu.setIncomingAndReceive();
while(read < lc) {
read += apdu.receiveBytes(read);
}


But i did not understand what this code do. Even reading APDU class from Java Card API. Someone can explain me, please?

Cheers,

Davi
  • 1. Re: Query about apdu.receiveBytes()
    fgrieu Newbie
    Currently Being Moderated
    This comments on
    short lc = (short) (a[ISO7816.OFFSET_LC] & 0xff);
    short read = apdu.setIncomingAndReceive();
    while(read < lc) {
      read += apdu.receiveBytes(read);
    }
    which was suggested in RSA implementation basics ... with the caveat that it may not actually compile or run.



    The code clearly assume a Case 2 or Case 3 APDU with no use of extended header; and that <tt> a[ISO7816.OFFSET_LC] </tt>is <tt> apdu.getBuffer()[ISO7816.OFFSET_LC] </tt>.

    With this assumed, <tt> short lc = (short) (a[ISO7816.OFFSET_LC] & 0xff) </tt> is the canonical way to compute Nc (the positive number represented by the Lc byte present in the APDU, by the above assumptions), without using<tt> apdu.getIncomingLength() </tt> which would make the applet incompatible with Java Card 2.2.1 or earlier.

    <tt>short read = apdu.setIncomingAndReceive() </tt> is the canonical way to get at least the beginning of the incoming data in the APDU buffer. The variable<tt> read </tt>now holds how many bytes have to be been received (starting at offset ISO7816.OFFSET_LC+1 in the APDU buffer, if above assumptions have been correct).

    The author rightly assume that<tt> read </tt>might be less than<tt> lc </tt> (that is, Nc). One example of that would be a Java Card with a 160-byte buffer and Nc of 200 (which is perfectly legit, both under ISO/IEC 7816-3 T=1, and even T=0 with a physical TPDU buffer separate from the Java Card APDU buffer). More on if there are other cases later.

    The<tt> while(read &lt; lc) { read += apdu.receiveBytes(read); } </tt> loop simply appends, at the appropriate offset in the APDU buffer, whatever additional bytes<tt> receiveBytes </tt>returns, updating<tt> read </tt> as this process goes. When and if the loop terminates, all Nc incoming bytes bytes have been received in the APDU buffer.

    While that code is IMHO correct, I think there is no case where the while loop both terminates and is executed at least once. My reading of the specification of <tt> setIncomingAndReceive </tt>and<tt> receiveBytes </tt>is that the only case where these methods do not get all the data in the command APDU is when this data would not fit in the APDU buffer; at least this is how I understand "+This method gets as many bytes as will fit without buffer overflow in the APDU buffer following the header. _It gets all the incoming bytes if they fit_+ ". Thus, unless the APDU buffer magically expands, only one of two things should occur:
    - <tt> setIncomingAndReceive </tt>got all the data and the loop will be skipped.
    - <tt> setIncomingAndReceive </tt>did not get all the data because the buffer is too short,<tt> receiveBytes </tt>will either return nothing for the same reason, or throw an exception, and in either case the loop will never terminate.

    I would thus say that either:
    - the piece of code shown is a tad overly defensive (which does not harm);
    - or there are buggy implementations of Java Card around which make this necessary, and I want more information on that!

    I have never witnessed any case where that while loop would be necessary to get all the ingoing data, and as far as I can tell this is also the assumption under which all sample classic applets in the various JCDKs are written. Those sample applets that do get the ingoing data with <tt> receiveBytes </tt> do not append the received data in the APDU buffer, as performed in the above snippet.
  • 2. Re: Query about apdu.receiveBytes()
    safarmer Expert
    Currently Being Moderated
    Here is the annotated version
    // Get the Lc from a standard length APDU. In JC2.2.2 use the Apdu methods for Lc.
    short lc = (short) (a[ISO7816.OFFSET_LC] & 0xff);
    // Ask the OS to read the incoming APDU.
    short read = apdu.setIncomingAndReceive();
    // Check how much was read. The OS may not be able to read the entire APDU in one call.
    while(read < lc) {
        // Keep reading until the OS has read all of the data in the APDU buffer.
        read += apdu.receiveBytes(read);
    }
    - Shane
  • 3. Re: Query about apdu.receiveBytes()
    DTF Newbie
    Currently Being Moderated
    Shane and fgrieu, thank you!

    Cheers,

    Davi
  • 4. Re: Query about apdu.receiveBytes()
    fgrieu Newbie
    Currently Being Moderated
    Att. Shane/safarmer: As explained, I do not get the rationale of the while loop.

    I conclude it either is skipped, or does not terminate, on any conformant Java Card platform, because<tt> setIncomingAndReceive </tt>is supposed to get all the incoming bytes that fit. Is there a hole in my reasoning, or is there a non-conformant Java Card platform where this help?

    Note: again, I do not challenge that the code shown works.
  • 5. Re: Query about apdu.receiveBytes()
    Adriaan Explorer
    Currently Being Moderated
    The while loop is necessary because of different COS behaviour.

    If I recall correctly, under NXP JCOP (at least v2.2.1 and v2.4.1) the COS does not guarantee that all incoming bytes will be collected before making them available through setIncomingAndReceive(...). This tends to happen with mode T=CL and T=1.

    Anyway, it's easy to test: just send a C-APDU with 8B payload with T=0, T=1 and T=CL and see how much of it your applet receives without the while loop :)


    Adriaan
  • 6. Re: Query about apdu.receiveBytes()
    fgrieu Newbie
    Currently Being Moderated
    Adriaan, if I get you correctly, the following piece of the sample code from<tt> java_card_kit-2_2_2\samples\src\com\sun\javacard\samples\JavaPurse\JavaPurse.java </tt> running on the aforementioned Java Cards, could fail if the Smart Card reader uses protocol ISO 7816-3 T=1 or ISO 14443-4 (aka T=CL), and transmits the C-APDU (normaly 5+13+1 bytes) as two separate I-blocks of say 10 and 9 bytes, which is perfectly conformant?

    I that for real ?!? Despite assurance in the specification of<tt> setIncomingAndReceive </tt> that this should not occur as long as the APDU buffer is long enough to receive the ingoing data ("+..gets as many bytes as will fit without buffer overflow in the APDU buffer following the header. It gets all the incoming bytes if they fit.+"), and assurance in<tt> javacard.framework.APDU </tt>that the APDU buffer is at least 133 bytes bytes? And that sample code, and several others?

    I'm in the business of writing applets that must run on a wide variety of Java Card platforms, I'm fighting with the limitations of such and such platforms (last one met yesterday: the stack depth), but that one is new for me, and more than a little disturbing.
    /**
     * Handles Complete Transaction APDU.
     * @param apdu APDU object
     */
    private void processCompleteTransaction(APDU apdu) {
        if (!transientBools[TRANSACTION_INITIALIZED])
            ISOException.throwIt(SW_COMMAND_OUT_OF_SEQUENCE);
        byte[] buffer = apdu.getBuffer();
        if (buffer[ISO7816.OFFSET_LC] != LC_CT) // LC_CT is 13
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        if ((buffer[ISO7816.OFFSET_P1] != 0) || (buffer[ISO7816.OFFSET_P2] != 0))
            ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
        
        apdu.setIncomingAndReceive();    // get expected data
        
        //restore transaction data from transient
        short newTN = transientShorts[TN_IX];
        short amount = transientShorts[AMOUNT_IX];
        short newBalance = transientShorts[NEW_BALANCE_IX];
        //The signature verification could be here
        Util.arrayFillNonAtomic(byteArray8, (short)0, (short) byteArray8.length,(byte)0);
        boolean signatureOK = (0 == Util.arrayCompare(buffer,
            (short)ISO7816.OFFSET_CDATA, byteArray8, START, SIGNATURE_LENGTH));
  • 7. Re: Query about apdu.receiveBytes()
    safarmer Expert
    Currently Being Moderated
    The motivation for this was a Gemalto card that advertised a 127 (or 133) byte buffer at the ISO7816-3 T=CL layer to the terminal yet had a 261 byte APDU buffer. The worry was that a card os would pass on the 127 bytes first without doing internal buffering, but if I recall correctly (it was around the time of my first post on this forum) the card OS will do the buffering ahead of time. This is not to say that all card OS's will do this for you. If you get into the loop, it will terminate as long as the card OS passes all the data in the APDU buffer. Granted there are corner cases with the code given, but that was also why the disclaimer was given. If the Lc and the number of incoming bytes in the incoming APDU are correct, the loop may execute and if it does will terminate correctly. To allow for incorrect Lc values you could also check for STATE_FULL_INCOMING on the APDU.

    While I am not aware of any cases where the loop has been required it is not too much effort to make sure that on a card that is affected by this that the code will still work with very little overhead.

    - Shane
  • 8. Re: Query about apdu.receiveBytes()
    Adriaan Explorer
    Currently Being Moderated
    I just discovered I can't reproduce the alleged setIncomingAndReceive(...) behaviour of the NXP JCOP cards...

    So now I'm not sure if that while loop is really necessary :/

    Let's give NXP the benefit of the doubt and say their cards are fine, until proven otherwise :)
  • 9. Re: Query about apdu.receiveBytes()
    safarmer Expert
    Currently Being Moderated
    As mentioned, this was probably excessive paranoia based on the fact that the Gemalto cards I was using could have in theory returned less than all of the bytes. Personally I don't recall using this code in production anywhere (even when dealing with large chained APDUs. It may be different with extended length APDUs but I have only ever had a card that supports them on T=CL which did not require extended length APDUs.

    - Shane

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points