Anyone ever bumped into 6881 using Android IsoDep?

I’m working on having an Android app interact with a very basic Hello World applet. Obviously those are very isolated snippets, but my minimum issue is one that strikes me as quite odd.

From GlobalPlatformPro, I can send direct APDUs to select and message the applet fine (apologies for my sophomoric AID); I get back a hex encoded Hello:).

> gp.exe -d -a "00 A4 04 00 06 A00042069421" -a "01 40 00 00 00"

[...boilerplate snipped...]
A>> T=1 (4+0006) 00A40400 06 A00042069421
A<< (0000+2) (15ms) 9000
A>> T=1 (4+0000) 01400000 00
A<< (0007+2) (5ms) 48656C6C6F3A29 9000
[...boilerplate snipped...]

Hex works, bing bang boom. My custom INS 0x40 gives me 48656C6C6F3A29 which is my hello string and a nice 9000.

However, when I send the same thing using my Android app, I’m getting back a 6881 indicating a wrong logical channel…? I’m not using logical channels (I mean I suppose I am, but should be getting the default/0 since I don’t specify, right?), so I’m super confused.

Relevant parts of my code are

        val select: ByteArray = byteArrayOf(
            0x00, 0xA4.toByte(), 0x04, 0x00, 0x06, 0xA0.toByte(), 0x00, 0x42, 0x06, 0x94.toByte(), 0x21

        var select_result: ByteArray = tag.transceive(select)
        runOnUiThread {textView.append("\nSelect applet got back " + select_result.toHexString())}

        val hallo_fetch: ByteArray = byteArrayOf(
            0x01, 0x40.toByte(), 0x00, 0x00, 0x00

        var hallo_result: ByteArray = tag.transceive(hallo_fetch)
        runOnUiThread {textView.append("\nInteract got back " + hallo_result.size + " bytes:" + hallo_result.toHexString())}

Selection returns 9000 but the interact, same bytes as GP, always gives me 6881 and nothing further.

I know obviously there are a lot of variables and I’m not asking for a pairing session on this at all, but just wondering if there’s anything obvious I’m doing or if 6881 is a code smell that indicates I might be making a common mistake or something of the sort.

The best thing I can think of is that the comm channel is getting closed/reset somehow in between selection and interaction and that selection does some logical channel management that I’m not aware of, but the applet is default so on the CLI so I still get good results without doing a select at all.

I do dev for my day job but have never so much as touched Android dev, but I suspect this might be a finer point of card interaction rather than a code bug that I’m missing.

Thanks for any thoughts!

Why using .toByte() on only some hex values in the bytearray?

Kotlin byte arrays are signed, apparently, so you need to explicit recast values higher than 128 so they are not interpreted as negative. I’ve since learned that there’s a neater way to do this by declaring an unsigned byte array and then converting the array en masse. I’ve never done kotlin before and have only done a small amount of Java, so I might have really terrible instincts about all this and there may be a beautiful way to make it happen that I’m missing.

It’s gross code, but I’m just focused on getting my MVP working and then iterate from there. I’m like a dog when I code – I need frequent positive reinforcement (minimal functioning code) to keep me engaged :grin:


1 Like

You cannot just use any instruction class (CLA) you want to. See ISO7816-4 section 5 smart card standard basic organizations - CardWerk software for smart cards , 5.4.1 Class byte - Table 8 – Coding and meaning of CLA. As you can see, a CLA of 0x01 is a request to open logical channel #1, something which is not supported when you are not in an authenticated GlobalPlatform session (hence why it worked in the GPP CLI, which does implicit authentication). I recommend using only the inter-industry CLA 0x80 (or 0x80 | 0x10 = 0x90 if you are sending a message with chained fragmentation) for normal communication.

Your command should be: 80 40 00 00 00 (CLA = 0x80, INS = 0x40(thats what you have in your JC code switch case), P1/P2 = 0x00, 0x00, 0x00command bytes, indeterminate response).

I also recommend adding e.g. 0xFF to the command to set the maximum amount of bytes expected to be replied to prevent buffer overruns.

Ah ha! That’s very helpful; thanks so much for your eagle eyes. I’ll give that a shot tonight.

Not used to banging my shins on so much protocol stuff :upside_down_face: I appreciate you coming to my rescue so often @StarGate01


1 Like