Android: Authenticating with NXP MiFare Ultralight C
Asked Answered
C

1

39

I have been trying for more than a week to make an Android phone authenticate with a Mifare Ultralight C. I have confirmed I can write to the tag (by writing to an unsecured memory page and then reading what I wrote). I can also write to the key pages (44-47) and have written 0x00 for all 16 key bytes.

When I try to authenticate, the following is an example of the data involved during one exchange - it is from a log written by my application. Can anyone tell me if I am doing something incorrect? I AM under non-disclosure and have access to the full data sheets. Note that the hexadecimal strings below are obviously human readable versions of the data being sent and received, which in the code consists of byte arrays.

Send authenticate command

Received rndB: 8A5735694D9D7542

Key: 00000000000000000000000000000000

IV: 0000000000000000

Decrypted rndB: EF340C62E1B866D4

rndB': 340C62E1B866D4EF

rndA: 6E262630E299F94F

rndA+rndB': 6E262630E299F94F340C62E1B866D4EF

Key: 00000000000000000000000000000000

IV: 8A5735694D9D7542

ek(RndA+rndB'): E36C6C46FAAC60BA45DDF5F5A0802C79

After sending 0xAF + E36C6C46FAAC60BA45DDF5F5A0802C79 I immediately lose the connection with the tag. I've gone through the data sheet and read every post I can find here. I have also looked at the libfreefare code and I honestly can't figure out what I'm doing wrong.

NXP technical support had been completely unresponsive.

Any ideas? I am at a loss.

Consistent answered 17/10, 2013 at 22:41 Comment(11)
I found the examples in NXP Application Note AN0945 very instructive and useful for debugging my own code.Magnetics
Great, thanks! I missed that because it's a DESFire document and I didn't pay attention to that folder in the DocStore. I just downloaded and it looks promising. Thanks again - I really appreciate your input.Consistent
NFCGuy - Have you (or has anyone you know of) successfully done an end-to-end authentication with an Ultralight-C using an Android device?Consistent
Yes, it can certainly be done. See for example this comment and this appMagnetics
Thanks, NFC Guy. I don't think that NXP app actually authenticates with the tag. It just reads memory that is unprotected. Their comment: "Please note that based on the contained security mechanisms of such applications no access to the actual contained data is provided by our application." I did run across that thread you linked and saw Miguel's closing comments. I sure wish there was a way to contact him. I'd love to know what I've been doing wrong.Consistent
I can confirm that the NXP app actually tries to perform authentication (with the all-zero key and the key used in sample cards: "BREAKMEIFYOUCAN!"). I just checked your calculations above and they are all correct! So my first guess is that the card you test with has a different key configured. The other option is that you single-step through the code, which may cause the Android device to do a presence check for the tag in between, interfering with the authentication protocol.Magnetics
Interesting. Thanks a TON for checking my data. Okay, I'm going to do some rearranging on my code and see if there is something that's causing the 'droid to do that presence check. Thanks again for checking my work - that gets a huge question mark out of the way and helps focus my effort going forward. I really appreciate your generosity with your time.Consistent
You're welcome. Not sure whether it matters (it shouldn't), but personally, I always use rndA = 0 (all zeroes) out of laziness.Magnetics
It's working now! Thanks, NFC guy for all your help - I really appreciate your generosity with your time.Consistent
@NFCguy You definitely should use a random rndA challenge/nonce -- otherwise it is possible to clone a card from eavesdropped authentication using an emulatorStereotypy
@NFCguy or OP, mind posting an answer to the question that explains the final solution? It might help future visitors to this question.Marlonmarlow
S
6

Below is an example java code to perform Ultralight-C authentication as described in MF0ICU2 / MIFARE Ultralight C - Contactless ticket IC document (chapter 7.5.5 -- 3DES Authentication, page 15):

public void authenticate(byte[] key) throws CardException {
    System.out.println("AUTHENTICATE");
    byte[] encRndB = transmitRaw(new byte[] { 0x1A, 0x00 });
    if((encRndB.length!=9)||(encRndB[0]!=AF)) {
        throw new RuntimeException("Invalid response!");
    }
    encRndB=Arrays.copyOfRange(encRndB, 1, 9);
    System.out.println(" - EncRndB: " + toHex(encRndB));
    byte[] rndB = desDecrypt(key, encRndB);
    System.out.println(" - RndB: " + toHex(rndB));
    byte[] rndBrot = rotateLeft(rndB);
    System.out.println(" - RndBrot: " + toHex(rndBrot));
    byte[] rndA = new byte[8];
    generateRandom(rndA);
    System.out.println(" - RndA: " + toHex(rndA));
    byte[] encRndArotPrime = transmitRaw(ArrayUtils.addAll(new byte[] {AF}, desEncrypt(key, ArrayUtils.addAll(rndA, rndBrot))));
    if((encRndArotPrime.length!=9)||(encRndArotPrime[0]!=0x00)) {
        throw new RuntimeException("Invalid response!");
    }
    encRndArotPrime=Arrays.copyOfRange(encRndArotPrime, 1, 9);
    System.out.println(" - EncRndArot': " + toHex(encRndArotPrime));
    byte[] rndArotPrime = desDecrypt(key, encRndArotPrime);
    System.out.println(" - RndArot': " + toHex(rndArotPrime));
    if(!Arrays.equals(rotateLeft(rndA), rndArotPrime)) {
        throw new RuntimeException("Card authentication failed");
    }
}

protected static SecureRandom rnd = new SecureRandom();
protected static void generateRandom(byte[] rndA) {
    rnd.nextBytes(rndA);
}

protected byte[] desEncrypt(byte[] key, byte[] data) {
    return performDes(Cipher.ENCRYPT_MODE, key, data);
}
protected byte[] desDecrypt(byte[] key, byte[] data) {
    return performDes(Cipher.DECRYPT_MODE, key, data);
}
private byte[] iv = new byte[8];
protected byte[] performDes(int opMode, byte[] key, byte[] data) {
    try {
        Cipher des = Cipher.getInstance("DESede/CBC/NoPadding");
        SecretKeyFactory desKeyFactory = SecretKeyFactory.getInstance("DESede");
        Key desKey = desKeyFactory.generateSecret(new DESedeKeySpec(ArrayUtils.addAll(key, Arrays.copyOf(key, 8))));
        des.init(opMode, desKey, new IvParameterSpec(iv));
        byte[] ret = des.doFinal(data);
        if(opMode==Cipher.ENCRYPT_MODE) {
            iv=Arrays.copyOfRange(ret, ret.length-8, ret.length);
        } else {
            iv=Arrays.copyOfRange(data, data.length-8, data.length);
        }
        return ret;
    } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidKeySpecException | IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException e) {
        throw new RuntimeException(e);
    }
}

protected static byte[] rotateLeft(byte[] in) {
    return ArrayUtils.add(Arrays.copyOfRange(in, 1, 8), in[0]);
}

Note: this code uses Apache Commons Lang.

Stereotypy answered 19/6, 2017 at 21:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.