corrupting arrays while using RC522 and DES Library

Hi All,

I am trying to write a Mifare Ultralight C encryption sketch. Basically everything is working, but while running the sketch the arrays seem to get corrupted and the sketch might even crash and restart.

I use the RC522 board and an Uno to read the Mifare Ultralight C Tags. The RC522 board is modified (L1 and L2 replaced) in order to detect an read the Ultralight C.
With commands over the serial monitor I am able to dump the data or to authenticate via an 3DES encrypted key.

the command for encryption used in the serial monitor is:

auth 49454D4B41455242214E4143554F5946

The perfect output looks like this (running the code on a Mega2560):

Firmware Version: 0x92 = v2.0
Read/Write Mifare Ultrlight C
Mifare Ultralight compatible PICC found
command: auth
data: 49454D4B41455242214E4143554F5946
IV: 00 00 00 00 00 00 00 00 
  enc(RndB): 92 37 31 3C 6D 3E 9D C2 
  RndB: A1 B5 EC 99 5E 37 2C B7 
  RndA: D4 6A AD E2 D8 5A 48 A0 
  rRndB: B5 EC 99 5E 37 2C B7 A1 
  RndA+RndB’: D4 6A AD E2 D8 5A 48 A0 B5 EC 99 5E 37 2C B7 A1 
new IV: 92 37 31 3C 6D 3E 9D C2 
0xAF + enk(RndA+RndB’): AF 33 DD F7 8F 32 2F 43 C9 0D C1 68 27 79 F3 DA CF 
dRndA': 6A AD E2 D8 5A 48 A0 D4 
RndA': 6A AD E2 D8 5A 48 A0 D4 
Keys are correct :-)

When trying to run this code on an Uno / Nano the result is this:

Firmware Version: 0x92 = v2.0
Read/Write Mifare Ultrlight C
Mifare Ultralight compatible PICC found
command: auth
data: 49454D4B41455242214E4143554F5946
IV: 00 00 00 00 00 00 00 00 
  enc(RndB): 31Firmware Version: 0x92 = v2.0
Read/Write Mifare Ultrlight C
Mifare Ultralight compatible PICC found

when repeating the command I get somthing like

command: auth
data: 49454D4B41455242214E414355⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮auth
IV: 00 00 00 00 00 00 00 00 
  enc(RndB): 4Firmware Version: 0x92 = v2.0
Read/Write Mifare Ultrlight C
Mifare Ultralight compatible PICC found

When printing the content of enc(RndB) the code crashes and restarts.
The intersting thing is that the code is running differently on a Mega2560 and Uno. Furthermore the output for the authentication depents on what routine I define to read the data from the tag.

To read the data from the tag, I tried to use the given MFRC522 command

mfrc522.PICC_DumpMifareUltralightToSerial();

When using this option, the authentication works.
What puzzles me is that after encrypted authentication this command seems to be corrupted as well, as the counter, limiting this function to only 16 pages in the MFRC522 library does not work anymore and the pages just keep counting up to thousends.

So I wrote my own dump-routine whitch totally messes up the authentification part of the program.

if(!strcmp(Scmd,"dump")){                                  // dump data
  DumpDataUltralight();                                      // this messes up the Authentification
  //mfrc522.PICC_DumpMifareUltralightToSerial();    // this messes the 16 page counter of the routine
 }

I tried my best to define and initialize the arrays required for encryption.
There are no warnings (setting to: "All")
Memory usage is OK

14926 Bytes (48%) of 30720 Bytes.
globale variables use 995 Bytes (48%) of 2048 Bytes

Full sketch is attached.

Where do I mess up the arrays/the code?

Arduino 1.8.13 on Windows
MFRC522 version 1.4.7
DES.h from Github

Many thanks in advance.

Ultralight_C_03.ino (14.9 KB)

The obvious difference is that the Mega2560 has more memory than the Uno. I'd guess that one or both of those libraries uses dynamic memory [aka malloc()] so the initial memory report may not be that relevant. One or both of those libraries may also use String objects which get dynamically created and can consume memory as well.

Thanks, that was something I was considering too. Based on your hint, I checked the DES.cpp and the MFRC522.cpp files for malloc(). Nothing found.

Then I used the <MemoryUsage.h> for further investigation.
After the setup I got the following usage:

Firmware Version: 0x92 = v2.0
Read/Write Mifare Ultrlight C
Data start:256
Heap start:1119
Heap end:1119
Stack start:2280
Stack end:2303
Stack size:23

As far as I understood, that doesn't look too bad.
When dumping the tag content I got exactly the same usage.

Right after sending the "auth 49454D4B41455242214E4143554F5946" command, prior to calling the authentication function by the if-statment the system crashes

Data start:256
Heap start:1119
Heap end:1119
Stack start:2(⸮
⸮⸮

... and a lot more garbage.

Any further ideas on how to investigate?

... update

I found another option to evaluate the free memory in the forum Arduino Playground - AvailableMemory

I used Serial.println(freeRam()); and the problems were gone. The code runs stable BUT, I need to add this in the setup including the Serial.println around it. It can be right in the beginning of the setup().
Indicates me about 1141 free bytes.

Then I added some additional code, and the code crashes again. Even without calling the new code.

Note:
I added thewhile(!Serial.available());before doing the Serial.read() to make sure the imput was received correctly (did not help solving the problem)

Adding a single line of code and your program suddenly works is a clear indication of memory issues. Running out of memory, writing outside boundaries of arrays and so on.

// Edit
Can you provide links to the libraries that you use?

Hi sterretje,

thanks for looking at it.

I am using the following library to do the 3DES encryption: GitHub - Octoate/ArduinoDES: DES and Triples DES encryption and decryption for the Arduino microcontroller platform

Using this library as provided on Github it generates some warnings (like writing 25 bytes in a 24 byte array) at

sprintf((char *)key,"000000000000000000000000\0");

I replaced that by

memcpy(key,"000000000000000000000000",24);

unfortunately that did not solve the problem.

The MRFC522 library is the standard library GitHub - miguelbalboa/rfid: Arduino RFID Library for MFRC522 in version 1.4.7

If you are writing 24 '0' into a 24 byte array with memcpy() you no longer have a string since there is no trailing '\0' byte. If you try to print this 'key' out, it will stop when it hits a zero in memory (where ever that is)

it generates some warnings (like writing 25 bytes in a 24 byte array)

That would be a major problem, and as pointed out above, your "fix" will not work as expected.

Increase the array size.

Thanks for the comments.

The variable key is defined in the DES.h as byte key[24];/**< holds the "key" for the encryption */
The variable "key" is never used for printing purpose inside the library nor in the sketch I created. So the methode of filling initial values to "key" in the DES.cpp was just a bit sloppy in my opinion, especially as the key is used as a hex value, so filling them with ASCII '0' makes no sense at all.

Increasing the variable key to 25 just creates another warning:

warning: embedded '\0' in format [-Wformat-contains-nul]
  sprintf((char *)key,"000000000000000000000000\0");

All variants produce the the given problem that there seems to be a pointer running wild.

The library seems to have a major problem with using sprintf() to copy hex data into the key array. I'm surprised there are no issues posted on github about that, since it would be exceeding the array bounds multiple times. The library has not been updated in six years, but the author appears to still be active so you might want to post an issue on github.

The compiler is also generating several warning messages because the key is passed as a void* and then sprintf is being used to copy that into the library's key array using sprintf.

/home/pi/Arduino/libraries/ArduinoDES-master/DES.cpp: In member function 'void DES::init(const void*, long long unsigned int)':
/home/pi/Arduino/libraries/ArduinoDES-master/DES.cpp:193:32: warning: format '%s' expects argument of type 'char*', but argument 3 has type 'const void*' [-Wformat=]
  sprintf((char *)key,"%s",m_key);
                                ^

I would try modifying the code with the following:

        //sprintf((char *)key,"000000000000000000000000\0");
        for (byte i = 0; i < sizeof(key)/sizeof(key[0]); i++) {
                key[i] = 0x00;
        }

Also replace the multiple instances of copying m_key using sprintf:

 //sprintf((char *)key,"%s",m_key);
 memcpy(key, m_key, sizeof(key));

Hi Dvid,
Thanks, I will follow up on that one. I don't like the way sprintf is used either.

It seems there might be an other problem with my sketch too. To print the results of the encryption etc. I wrote a dumInfo routine:

void dumpInfo(String str, byte *ar, int len){
  Serial.print(str);
  for(int i=0;i<len;i++){
    if(ar[i]<0x10)
      Serial.print("0");
    Serial.print(ar[i], HEX);
    Serial.print(" ");
  }
  Serial.println("");
}

I then used to call this routine with:

dumpInfo("  RndA+RndB’: ",message,16);    // print the message prior to encryption

No errors or warning were given during compiling.

As I was suspecting something went wrong with the pointers, I started to folow up on all pointer adresses of each variable. What I found out is that the adress of "str" is 0x08E4 and does not change for each call (independent how long the message was). I don't know if the max string lenth is reserved, but I was afraid that this might lead to overwrting some other variables nearby or going beond 0x08FF. I changed the dumpInfo() to print the array only. The string message is printed via Serial.printl(F(...)). Right now it seems to be running stable.

You would not want to pass a string literal as a String, use const char* instead, or use F() when you call the function and declare it as __FlashStringHelper* in the function header.

Using F() freed up some ram, which would help if you were low on memory.

Hi all,

I am absolutely puzzled by the behaviour of the Arduino. I was assuming the sketch was running smoothly now, still I did not like, that the DES decryption was somehow messing up the input given to decrypt.

So I investigated a bit and I was confronted with an absolutely strange behaviour.
I looked up the pointers of all variables used around the decryption.

  • authBuffer 2096-2119 (24 Bytes)
  • message 2120-2143 (24 Bytes)
  • RndA 2144-2151 (8 Bytes)
  • RndB 2152-2160 (8 Bytes)

This means that message, RndA and RndB are neighbours.
All variables are initilized with 0x00

In the sketch authBuffer will get some input from the RF522 module. This content is copied to "message" (shifted by one byte). Everything looks fine.
But after using

des.tdesCbcDecipher(message,RndB);

the content of "message" is absolutly messed up

AuthBuffer: 2096
AF 38 9C 0D 9C C8 BE BC BB 04 D9 00 00 00 00 00 00 00 00 00 00 00 00 00
message: 2120
38 9C 0D 9C C8 BE BC BB 04 D9 00 00 00 00 00 00 00 00 00 00 00 00 00 00
function -> des.tdesCbcDecipher(message,RndB);
message: 2120
00 B8 00 00 53 00 F5 B8 00 FF B8 00 00 00 B8 00 B8 B8 B8 00 B8 27 08 80

But when I look into the neighbour RndA, everything is nicely 0x00!
So I treid to investigate from which adress the bytes get corrupted by increasing the size of RndA. Here the strange behaviour starts:

  1. When I increase the size of RndA well within the area where "message" has been, RndA is still nicely 0x00, but the corrupted bytes within "message" stay at their exact location. That means, prior to shifting the location, 2124 was 0x53, but after shifting it is still 0x53, although the startpoint of "message" shifted.
  2. because RndA and "message" are neighbours, I tried to look into the neighbours of RndA by usging
  Serial.print(F("RndA: "));
  Serial.println((long)RndA);
  dumpInfo(&RndA[0]-8,24);

So I start 8 byte before RndA and look 8 byte after RndA.
What is absolutely weird, is that the memory location using the pointer of "message" provides a different result compared to using the pointer of RndA. Means, location 2143 reads 0x80, using "message" as pointer but 0x00, using RndA-1 as pointer.

message: 2120
00 B8 00 00 53 00 F5 B8 00 FF B8 00 00 00 B8 00 B8 B8 B8 00 B8 27 08 80 
RndA: 2144
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 42 88 B2 A6 9A 05 E9 1C 
RndB: 2152
42 88 B2 A6 9A 05 E9 1C

How can this happen?
Any hints?

I think you have your arguments reveresed. If you look at the DES example

des.tdesCbcDecipher(cyphertext,plaintext_p);

The first argument is what gets encrypted (message in your case), the second argument is the plain text that does not change (RndB in your case).

For encryption, your hint would be correct, for decryption the "plaintext" is the decrypted stuff. The encryption / decryption itself is working fine, except that depending on the length of the sketch, the arduino hangs, restarts or start to print weird thing.

By chance, the current version of the sketch seems to be currupting the pointer of "message".

It is somehow either the pointer to "message" or the Serial.print of this pointer doing strange things.
And it is only the exact pointer to "message". When dumping "message+1" the correct content is given:

message: 2120
DD D1 58 D4 CF 1A 47 F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

do all the decoding....

message: 2120
00 B8 00 00 53 00 2F B8 00 FF B8 00 00 00 B8 00 B8 B8 B8 00 B8 27 08 80 
message+1: 2121
D1 58 D4 CF 1A 47 F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

the current sketch is attached.

Ultralight_C_04.ino (19.5 KB)

It seems incredibly odd that back to back calls to dumpInfo() give different results for message v. message+1. It should just be the same data, shifted by 1 byte.

Looking at your code, it seems pretty convoluted. I had a tough time following what is supposed to happen. I would try to reduce this down to a very basic program that does 1 test. Get rid of the Serial input portion. Reduce your number of variables - there are many globals yet some functions have local copies, etc.
Make is a streamline of
. wait for chip present
. print chip info
. do encryption
. print results
. end

If that fails, it should be easier to see exactly where. If it doesn't fail, start building the code back up

Thanks for taking your time looking into the code.

It seems incredibly odd that back to back calls to dumpInfo() give different results for message v. message+1. It should just be the same data, shifted by 1 byte.

This behaviour led me to the assumption, that it is not the pointer itself having a problem, but the program code being called when accessing the the pointer "message"

But you are right, the code was pretty messy, and to be honest, I tried so many things that it is difficult to say anymore where the problems startet.

So I followed your sugestion and condensed the sketch to absolute basics (see attached Ultrlight_C_simple). I mostly deleted stuf, but did not change anything about the decryption / encryption.

It does not work!

Firmware Version: 0x92 = v2.0
Read/Write Mifare Ultrlight C
Mifare Ultralight compatible PICC found
Auth failed failed: A MIFARE PICC responded with NAK.

This means, that the Card is replying to the first encrypted authentication request, but does not accept the encrypted reply from the sketch.
Previously I checked all the encryption and decryption stuff based on examples provided by NPX, which is all done correctly.

So, to investigate, why it does not work, again I needed some data about the variables. In order to do so (see attached Ultrlight_C_simple_Serial), I inserted a small loop like:

  Serial.print(F("message: "));
  for(i=0;i<16;i++){
    if(message[i]<10)
      Serial.print(F("0"));
    Serial.print(message[i],HEX);
    Serial.print(F(" "));
  }
  Serial.println("");

again the result is more than puzzling:

Read/Write Mifare Ultrlight C
Mifare Ultralight compatible PICC found
message: 41 60 84 CB 7F 6C DA 80 

... do the decryption stuff

message: 41 60 84 CB 7F 6C DA 80
RndB: D8 BA DB 36 E0 31 E3 14 
RndA: 23 DB DE 3E AC 3E 90 6B 
message: 23 DB DE 3E AC 3E 90 6B BA DB 36 E0 31 E3 E3 D8 

....do the encryption stuff

AuthBuffer: AF F8 C4 D0 BE 04 3A 72 CF 3B 7B CC 5E DF 73 C2 EA 70 50 F8 35 C 75 00 41 60 84 CB 7F 6C DA 80 CE B9 C3 F1 08 DF 19 59 88 B5 75 FF 27 8F 9E (.....)

Although the loop for printing "AuthBuffer" is limited by "i<19" it keeps on running. So my impression is that the programm code itself gets corrupted as the expression

for(i=0;i<19;i++){

always seems to be true although "i" is counting up.

At the same time, the problem in the previous version of the code, that "message" is corrupted after decryption does not exist anmore.

As said, I have the impression that at some point the (program) code is being modified by a rouge pointer. But where and by what part of the code?

Ultralight_C_simple.ino (9.05 KB)

Ultralight_C_simple_Serial.ino (10.4 KB)