johnnyb / ntag424-java

A Java library for the NXP NTAG 424 DNA chip
MIT License
21 stars 3 forks source link

Writing to File 03 (CommMode FULL) is failing in AES only #2

Open johnnyb opened 7 months ago

johnnyb commented 7 months ago

Writing to File 03 (CommMode FULL) is failing in AES Encryption but running on LRP Encryption.

[Submitted via email by AndroidCrypto]

AndroidCrypto commented 5 months ago

Dear Jon, In the last days I played a little bit to find the reason for this issue. I set the file settings of file 03h ("Data File") to Communication Mode PLAIN - writing and reading is working. Then I changed the Comm Mode to MAC and reading and writing is working as expected.

Then I changed the Comm Mode to FULL and run the same tests - reading is working (with a hickup, see below) but writing is failing with status result "917E" (Length Error, Command size not allowed).

But there seems to be something strange: I read the file after the failure (needed to run the authentication again) and most parts of my data was written !

To have a clean file, when the file was in MAC Comm Mode I cleared it by writing 128 00h values. This is the data in Comm Mode FULL.

readFile 3:  length: 144 data: 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000
writeFile 3: length: 128 data: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f 
             Invalid status result: 917E
readFile 3:  length: 144 data: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f0000000000000000000000000000000080000000000000000000000000000000

It seems that 112 bytes were written to the file instead of 128 bytes.

Now I changed the number of bytes I wrote to the file - now I used 114 bytes (so NOT a multiple of 16):

writeFile 3: length: 114 data: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f7071
             Operation SUCCESS: 9100
readFile 3:  length: 128 data: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f70718000000000000000000000000000             

The write operation is working as expected. As the error is in AES only the source is obviously in the AESEncryptionMode.java class. I'm for sure it starts in "public byte[] encryptData(byte[] message)" at line 71:

byte[] result = cipher.doFinal(Crypto.padMessageToBlocksize(message, BLOCKSIZE_BYTES));

As the writing of data with a NON multiple of 16 bytes length is working the error will be in the padding sequence:

public final class Crypto {
    public static byte[] padMessageToBlocksize(byte[] message, int blocksize) {
        int remainder = message.length % blocksize;
        int complete_blocks = message.length / blocksize;
        if(remainder == 0) {
            return message;
        }

        byte[] result = new byte[(complete_blocks + 1)*blocksize];
        System.arraycopy(message, 0, result, 0, message.length);
        result[message.length] = (byte)0x80;
        return result;
    }

This would explain the tag's error code "wrong command length".

As mentioned before there is a hick up when reading a file in Comm Mode FULL: the "readData" method is returning data that seems to include the padding or something "empty" as 16 additional bytes are returned (144 bytes are returned on a 128 byte long file).

I'm sure you are now been able to fix the problem, thanks a lot in advance.

Kind regards, Michael

AndroidCrypto commented 5 months ago

Hi Jon, I read the documentation "NTAG 424 DNA NT4H2421Gx.pdf page 24, 9.1.4. Encryption" which says:

Padding is applied according to Padding Method 2 of ISO/IEC 9797-1 [7], i.e. by adding always 80h followed, if required, by zero bytes until a string with a length of a multiple of 16 byte is obtained. Note that if the plain data is a multiple of 16 bytes already, an additional padding block is added. The only exception is during the authentication itself (AuthenticateEV2First and AuthenticateEV2NonFirst), where no padding is applied at all.

Using this modification let the writeData method run without any error:

public static byte[] padMessageToBlocksize(byte[] message, int blocksize) {
    int remainder = message.length % blocksize;
    int complete_blocks = message.length / blocksize;
    if(remainder == 0) {
        // add a complete padding block to message
        // see NTAG 424 DNA NT4H2421Gx.pdf page 24:
        // Padding is applied according to Padding Method 2 of ISO/IEC 9797-1 [7], i.e. by adding always 80h followed, 
        // if required, by zero bytes until a string with a length of a multiple of 16 byte is obtained. 
        // Note that if the plain data is a multiple of 16 bytes already, an additional padding block is added. 
        // The only exception is during the authentication itself (AuthenticateEV2First and AuthenticateEV2NonFirst), 
        // where no padding is applied at all.
        byte[] result = new byte[message.length + blocksize];
        System.arraycopy(message, 0, result, 0, message.length);
        result[message.length] = (byte)0x80;
        return result;
        //return message;
    }
    byte[] result = new byte[(complete_blocks + 1)*blocksize];
    System.arraycopy(message, 0, result, 0, message.length);
    result[message.length] = (byte)0x80;
    return result;
}

Just one "error" needs to get fixed - readData in Comm Mode FULL is returning too large data.

Greetings, Michael