Android Question [SOLVED] How to get more than 256 bytes from TagTechnology.RunAsync?

Sandman

Expert
Licensed User
Longtime User
I try to communicate with a smartcard using this code:
B4X:
TagTech.RunAsync("TT", "transceive", Array(Array As Byte(0x00, 0xCB, 0x3F, 0xFF, 0x05, 0x5c, 0x03, 0x5F, 0xC1, 0x01, 0x00)), 0)
Wait For TT_RunAsync (Flag As Int, Success As Boolean, Result As Object)

It works just fine, except for one crucial detail: I can only get 258 bytes. I'm guessing that means 256 bytes + 2 response status bytes, so in reality I can only get 256 bytes.

But the thing is that I am certain that the response should be ~1300 bytes. I can tell because in the first 256 bytes is a length indicator. The response is also in standard TLV encoding, so I've been able to verify using online tools that my length calculation is correct.

So I'm wondering, should I do something special to get more than 256 bytes? Am I supposed to somehow wrap the Wait For in a loop somehow and wait for more events, or how is this supposed to work? (I have added Log() after the Wait For, to see if it automatically gets more than one event, which doesn't happen.)
 

Sandman

Expert
Licensed User
Longtime User
Erel: Do you know if there's a limitation in your TagTechnology implementation causing only 256 bytes to be received?

(I tried looking at your Github page, but couldn't find code for NFC or TagTechnology.)


EDIT: No, this is not an issue with the B4A implementation. I get the same results with a desktop smartcard reader.
 
Last edited:
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
there was a bug related to the number of bytes in the response, allegedly fixed in android P.
there is way to query the maxresponse size. if it says 256, that's all you can receive. nowadays it should say 65535 or some such.
if the response begins with AF, a loop is possible. in fact, required if you need more that what would be returned in the first response.
although you're using transceive, your command looks like a typical apdu command. the last byte (so-called Le) indicates what you are expecting back. it looks like you have 0. 0 = 256 bytes max. to allow for the maximum (and assuming your device does not suffer from the bug), Le needs to be 2 or 3 bytes, depending on what you set Lc at. for this to work, extended apdu's need to be supported. since you already think the response should be 1000 bytes, that means a 1 byte Le won't work. you need to see if you can extend the command and include 2 bytes instead of 1 at the end. i'm guessing, each would be set to 0xff to allow for the max. the java card version on the card should be above 2.2.2.
 
Last edited:
Upvote 0

Sandman

Expert
Licensed User
Longtime User
Thanks for your answer, there's a lot to unpack in it! I'll just have to cut it up into pieces so I don't miss anything. :)

there was a bug related to the number of bytes in the response, allegedly fixed in android P.
Understood. In that case it should not affect me, because I'm testing this with Android 14.
However, I do wonder if you have a source? I do have users with Android P and I'd like to read more to better understand if this would affect them.

there is way to query the maxresponse size.
I'll see if I can find it, it's not obvious to me at the moment. However, it's a modern card so we can probably assume that it's ~64k.

if the response begins with AF, a loop is possible. in fact, required if you need more that what would be returned in the first response.
The response does not begin with AF, but out of curiousity: How would I do that look if that was the case? Would the Wait For just be called several times or would I have to do something more involved?

EDIT after reading more online: The response does not begin with AF, but if it did I think I should just keep sending 00 C0 00 00 XX until 61 XX indicates there is nothing more to receive. Is this correct?

although you're using transceive, your command looks like a typical apdu command.
Yes, it's an apdu command. My understanding was that I should use transceive, is that not correct?

the last byte (so-called Le) indicates what you are expecting back. it looks like you have 0. 0 = 256 bytes max. to allow for the maximum (and assuming your device does not suffer from the bug), Le needs to be 2 or 3 bytes, depending on what you set Lc at. for this to work, extended apdu's need to be supported. since you already think the response should be 1000 bytes, that means a 1 byte Le won't work. you need to see if you can extend the command and include 2 bytes instead of 1 at the end.
This is really very interesting. I started with doing this approach, that you mentioned:

B4X:
TagTech.RunAsync("TT", "transceive", Array(Array As Byte(0x00, 0xCB, 0x3F, 0xFF, 0x05, 0x5c, 0x03, 0x5F, 0xC1, 0x01, 0xFF, 0xFF)), 0)
Wait For TT_RunAsync (Flag As Int, Success As Boolean, Result As Object)
I removed the last 0x00 and added 0xFF 0xFF instead. However, this just gave me a response of 0x67 0x00

I read this Wikipedia page for more info about apdu:

It clearly states that a zero limits to 256 bytes so that was an error you helped correct - thank you! But there clearly is something else I'm not understanding here.

Both you and the Wikipedia page states that for Le to be two bytes, I need to adjust Lc. The only way I can see to adjust Lc is to change it from 0x05 to 0x00 0x00 0x05. As best I can understand, this would be an "extended Lc", which in turn would make it possible to have a two-byte. So I did that:

B4X:
TagTech.RunAsync("TT", "transceive", Array(Array As Byte(0x00, 0xCB, 0x3F, 0xFF, 0x00, 0x00, 0x05, 0x5c, 0x03, 0x5F, 0xC1, 0x01, 0x00, 0x00)), 0)
Wait For TT_RunAsync (Flag As Int, Success As Boolean, Result As Object)
This gave the response of 0x67 0x00, which means "Incorrect length or address range error".

Any ideas?
 
Last edited:
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
wrong length means the card does not support extended apdu's. depending on what Lc is, Le can be 2 bytes (but it's moot if the card doesn't accept extended apdu's). as for the bug, it's covered (at length) in a google source forum. people report their claim, and there's back and forth between users and google people assigned to the matter. i forget the link. it's unlikely you are experiencing it with android 14. i think you need to home in on extended apdu's. here's a start:
https://stackoverflow.com/questions...xtended-apdu-with-windows-smartcard-framework. but you need something like "apdu format". again, i don't have the particular link at hand relating to Le. i'll pass it along if i can find it.
 
Upvote 0

Sandman

Expert
Licensed User
Longtime User
Understood. I think I should do some more diagnostics to make sure I'm not trying to do something impossible.
  1. Verify Java Card version is above 2.2.2
  2. Investigate whether extended apdus are supported
If you know how to check this, I'd love help, otherwise I'll try to search the internet.

However, is it possible to get the data using max-256 chunks as it is right now? Using 00 C0 00 00 00? I just tried just now, and I seem to get more data even though the status is 61 00. That does seem strange to me, does it sound normal to you? (You obviously have a lot more experience than I do.)

Edit: I looped, and I seem to have received all data using this method. I'll report back once I've taken a better look at it.
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
EDIT after reading more online: The response does not begin with AF, but if it did I think I should just keep sending 00 C0 00 00 XX until 61 XX indicates there is nothing more to receive. Is this correct?
this would depend on the card's application. no harm in trying.

Yes, it's an apdu command. My understanding was that I should use transceive, is that not correct?
apdu is a format. cards may recognize it or their own proprietary command set or both. transceive is the sending protocol.


you need to find the card's documentation. if the card has its own set of commands, it may be possible to get longer responses back. you would still use transceive, but the format would follow the card's proprietary commands...

here for apdu format (and Lc, Le):
https://en.wikipedia.org/wiki/Smart_card_application_protocol_data_unit
 
Last edited:
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
check this: https://cardwerk.com/smart-card-standard-iso7816-4-section-7-transmission-interindustry-commands/

i never see command C0 with the types of cards i am interested in. but it seems to function the way 4F does: "more to come..." you should get a specific status byte when you've captured all the data (or maybe just a lack of a starting 61). i only glanced at the above link to see if it might be germane, but i noticed it mentions a dependency on the Lc byte(s) in the request. in any case, it seems like you're a few steps closer; it looks like a proper pairing for Lc and Le is what's standing in the way.

note:
61XXICommand successfully executed; ‘XX’ bytes of data are available and can be requested using GET RESPONSE.
second status byte gives you what's to come...
 
Last edited:
Upvote 0

Sandman

Expert
Licensed User
Longtime User
Yeah, I managed to read out the full response by doing the C0 command several times. Each time the status was 61 00, until it was something like 61 47. And when I read a last time, the status turned 90 00 - so that seems like a working solution. Much of the issues here stems from the fact that I didn't realize that 0x61 0x00 meant 0x61 256, I thought it actually meant zero. :-/

So I feel this thread can be considered solved. Perhaps the cards support extended APDUs somehow, but this works well enough so I'm not going to delve deeper into it for now. Thanks for helping out, I appreciate it a lot.
 
Upvote 0

Sandman

Expert
Licensed User
Longtime User
A small follow-up: Do you know what the established way is to identity the type of card? I mean, there are so many different cards, and they seem to behave in slightly different ways. Is there a common method that one can rely on to actually get the card type?
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
android tells you which "technologies" a discovered tag supports. that's basically your "type" of card.
if 1 such technology is ndef or ndef-unformatted, you're out of the woods. mifare comes next, then isodep.
these are all nfca (and maybe nfcb) cards with the widest support. any others (nfce or nfcf) get into proprietary
command sets. i would think that's the first thing you see when the tag is discovered. it's the first thing android
gives you when a tag is discovered. the os sends your app the information via intent.

if you know a tag is a mifare, you just use mifare tools to home in on the particular model and capabilities of
the tag. if the tag is isodep, you got your apdu's to see what the tag responds to. javacard tags may be more
proactive than regular smartcards.
 
Upvote 0

Sandman

Expert
Licensed User
Longtime User
Understood, thanks. I probably don't use the correct terms, sorry for that. And for whatever it's worth, I'm only working with isodep, ndef doesn't come into this for me, ever.

As for "type" I meant more like being able to differentiate between things like this (assuming they are all isodep):
- An ID card
- A passport
- A subway card
- A payment card (Mastercard or whatever)

Currently I'm selecting the application the first thing I do after connecting to the card, but my gut feeling is that I should do something else first to make sure I should even bother to select the application. (Not all cards have the same application, I imagine.)

Here's how I select the application, in case that's interesting:
B4X:
00 A4 04 00 09 A0 00 00 03 08 00 00 10 00 00
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
you compile a list of AID's and test against them. in the case of emv tags, there is a generic call
which will give you a given tag's application id (from there you consult a list of thousands of such id's,
available on the greater net for download.) passport seems to have its generic aid (i only get to see my passport),
mrtd's have a generic test. subway (or metro, as some call it) cards are likely to be mifare or sony
felica. they are most likely to be proprietary, given the limited usage of such cards. somebody will
have discovered the aid and shared the bounty. but unless you happen to be in madrid looking for discarded
subway cards in the trash, knowing their aid isn't of much use. in any case, transportation tags are usually
triggered by keys rather than aid's. a different story entirely.

these generic aid's go into your tool box. like truffles, you search them out and guard them.
i don't recognize the one you posted, so i'll look it up and, if applicable, add it to my private, personal
list, and thank you very much.
 
Upvote 0

Sandman

Expert
Licensed User
Longtime User
So it's not really possible to get the AID of the card, but rather you have your own, internal list that you test against the card until you find one that the card will accept?
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
there are some generic calls which will induce a card to give up its aid. but even if you have a tag's aid, you have to know how to communicate with that aid. it's like determining which language somebody speaks and then not being able to speak that language. and dealing with these aid's is like dealing with each successive version of android.

if you are dealing with a card that you have seen before, you will, of course, have already saved its aid.

if you have access to a given card's documentation (sometimes at great expense and nda signing), you will have its aid. (not all cards use an aid.)

there is no master key (or master aid) that opens all cards. emv comes closest, but each card issuer implements the aid as it sees fit. you need to look into a tag's documentation.

not applicable for your case, but the only lingua franca for nfc is ndef. if you have an ndef-compliant tag, it's ready to be programmed. otherwise, you have to research the command set. if the card is isodep-compliant, you need to see if there is a generic aid query for that type of card.
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
Understood, thanks. I probably don't use the correct terms, sorry for that. And for whatever it's worth, I'm only working with isodep, ndef doesn't come into this for me, ever.

As for "type" I meant more like being able to differentiate between things like this (assuming they are all isodep):
- An ID card
- A passport
- A subway card
- A payment card (Mastercard or whatever)

Currently I'm selecting the application the first thing I do after connecting to the card, but my gut feeling is that I should do something else first to make sure I should even bother to select the application. (Not all cards have the same application, I imagine.)

Here's how I select the application, in case that's interesting:
B4X:
00 A4 04 00 09 A0 00 00 03 08 00 00 10 00 00
that command seems to be a generic piv get aid call. that's the kind of thing you add to the toolbox. if that's the case, it makes life easier when testing to see what kind of card you're looking at. if it forces the card to give you the aid, then you just have to know how to handle that aid. not the same has having to step through a list of hundreds of possible aid's. it's the same for emv.

edit: look here https://www.icao.int/meetings/tag-mrtd/documents/tag-mrtd-17/tagmrtd17_wp015_c.pdf
section 3. even the people responsible for mrtd's talk about testing to see which kind of card you're looking at.
 
Last edited:
Upvote 0
Top