Arduino encryption not the same as python

Hello! I am trying to encrypt a message on the Arduino using ChaCha20 encryption algorithm but when i am decrypting the received message on the computer using Python it does not resemble the same message. For some reason they are both different when encrypting and decrypting the same plaintext. The same thing applies when using AES. Is this a know issue or does anyone know a workaround to it ?

/* ----------- ENCRYPT DATA ----------- */
void encrypt(ChaChaPoly *cipher, byte key[], byte iv[], byte plaintext[], byte ciphertext[], byte tag[]) {
  cipher->clear();
  cipher->setKey(key, sizeof(key));
  cipher->setIV(iv, sizeof(iv));
  cipher->encrypt(ciphertext, plaintext, sizeof(plaintext));
  cipher->computeTag(tag, sizeof(tag));
}


/* ----------- PRINT UNDERSTANDABLE CODE FOR PYTHON ----------- */
void printFinal(byte data1[], int len1, byte data2[], int len2, byte data3[]){
  Serial.print("[");
  printHex(data1, len1);
  Serial.print("|");
  printHex(data2, len2);
  Serial.print("|");
  printHex(data3, sizeof(data3));
  Serial.print("]");
}

unsigned long uid = getID();
  unsigned long timenow = now();
  String ToSend = uid + "|" + timenow;
  byte text[32];
  
  for(int i=0; i< 32; i++)
       text[i] = ToSend[i];
  byte ciphertext[sizeof(text)];

  encrypt(&cha, key, iv, text, ciphertext, tag);
  printFinal(tag, 16, iv, 12, ciphertext);

So the both sides don't use the same algorithm (p.e. CBC) or one implementation is wrong.

As you don't link to the used libraries and don't provide complete code it's up to you to find the actual reason.

Try the example key, nonce, counter, and plaintext from here:
https://tools.ietf.org/html/rfc7539

For a test vector, we will use the following inputs to the ChaCha20
 block function:

 o Key = 00:01:02:03:04:05:06:07:08:09:0a:0b:0c:0d:0e:0f:10:11:12:13:
 14:15:16:17:18:19:1a:1b:1c:1d:1e:1f.

 o Nonce = (00:00:00:00:00:00:00:4a:00:00:00:00).

 o Initial Counter = 1.

 We use the following for the plaintext. It was chosen to be long
 enough to require more than one block, but not so long that it would
 make this example cumbersome (so, less than 3 blocks):

 Plaintext Sunscreen:
 000 4c 61 64 69 65 73 20 61 6e 64 20 47 65 6e 74 6c Ladies and Gentl
 016 65 6d 65 6e 20 6f 66 20 74 68 65 20 63 6c 61 73 emen of the clas
 032 73 20 6f 66 20 27 39 39 3a 20 49 66 20 49 20 63 s of '99: If I c
 048 6f 75 6c 64 20 6f 66 66 65 72 20 79 6f 75 20 6f ould offer you o
 064 6e 6c 79 20 6f 6e 65 20 74 69 70 20 66 6f 72 20 nly one tip for
 080 74 68 65 20 66 75 74 75 72 65 2c 20 73 75 6e 73 the future, suns
 096 63 72 65 65 6e 20 77 6f 75 6c 64 20 62 65 20 69 creen would be i
 112 74 2e t.

"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."
Ciphertext Sunscreen:
  000  6e 2e 35 9a 25 68 f9 80 41 ba 07 28 dd 0d 69 81  n.5.%h..A..(..i.
  016  e9 7e 7a ec 1d 43 60 c2 0a 27 af cc fd 9f ae 0b  .~z..C`..'......
  032  f9 1b 65 c5 52 47 33 ab 8f 59 3d ab cd 62 b3 57  ..e.RG3..Y=..b.W
  048  16 39 d6 24 e6 51 52 ab 8f 53 0c 35 9f 08 61 d8  .9.$.QR..S.5..a.
  064  07 ca 0d bf 50 0d 6a 61 56 a3 8e 08 8a 22 b6 5e  ....P.jaV....".^
  080  52 bc 51 4d 16 cc f8 06 81 8c e9 1a b7 79 37 36  R.QM.........y76
  096  5a f9 0b bf 74 a3 5b e6 b4 0b 8e ed f2 78 5e 42  Z...t.[......x^B
  112  87 4d

If you get the right ciphertext then your encryption works and the problem is on the decryption side (or in the data transmission).

  cipher->setKey(key, sizeof(key));

That's a mistake right there. Since 'key' is a function argument, the size will be 2 (the size of the pointer passed to the function). Your function will either need to KNOW the size and use that or be passed the size from a caller that knows the size.

Thank you for the quick response, the library we are using is Arduino Cryptography Library: ChaChaPoly Class Reference on the Arduino and ChaCha20 and XChaCha20 — PyCryptodome 3.15.0 documentation for the Python.

I do not have the code for decryption at the moment but i will post it as fast as i can here.

I am creating a key on the Arduino using Curve25519, share it with the python then use SHA256 over both the keys. Up to this point both keys are the same on both Arduino and Python. After creating an Initialization Vector which is directly shared with the python code. When encrypting with the same key and IV i get different results on the Arduino and the Python. Therefore decryption of the ciphertext is not doable.

johnwasser:

  cipher->setKey(key, sizeof(key));

That's a mistake right there. Since 'key' is a function argument, the size will be 2 (the size of the pointer passed to the function). Your function will either need to KNOW the size and use that or be passed the size from a caller that knows the size.

I have noticed that as well and is now hardcoded with the length of the key. Still the error persists.

AGorgan:
I have noticed that as well and is now hardcoded with the length of the key. Still the error persists.

Did you fix the other three places where the same mistake was made?

  cipher->setIV(iv, sizeof(iv));
  cipher->encrypt(ciphertext, plaintext, sizeof(plaintext));
  cipher->computeTag(tag, sizeof(tag));

Did you try to encrypt the example and check the result?

We changed every sizeof from the code and we run the following example (the one you pointed us to):

#include <Crypto.h>
#include <ChaChaPoly.h>

ChaChaPoly ccp;

void setup(){
  Serial.begin(9600);
}

void loop() { 
  byte key[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f};
  byte iv[] = {0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00};
  byte plaintext[114];
  String("Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.").getBytes(plaintext, 114);
  byte ciphertext[114];
  byte tag[16];

  encrypt(&ccp, key, iv, plaintext, ciphertext, tag);
  
  Serial.write(ciphertext, 114);

  delay(2000);  
}

void encrypt(ChaChaPoly *cipher, byte key[], byte iv[], byte plaintext[], byte ciphertext[], byte tag[]) {
  cipher->clear();
  cipher->setKey(key, 32);
  cipher->setIV(iv, 12);
  cipher->encrypt(ciphertext, plaintext, 114);
  cipher->computeTag(tag, 16);
}

But the ciphertext is not correct. After using other ciphers from the said library we come up to the conclusion that maybe the whole library is broken as no cypher from that library worked. Does anyone know a good reliable library to import ChaCha20 from for Arduino ?

The encryption works fine for me on an Arduino UNO. I put in the test data and got the expected results.

Note that the 'counter' is a 4 or 8 byte LITTLE-ENDIAN value.

#include <ChaCha.h>


ChaCha  CC20(20);


const byte CC20Key[32] =
{
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
  0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
};


const byte CC20Nonce[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00};


const byte CC20Counter[4] = {0x01, 0x00, 0x00, 0x00};  // Little-endian 4 or 8 byte integer


const byte Plaintext[] = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
const int PlaintextSize = (sizeof Plaintext) - 1; // Leave out the terminating null


byte Ciphertext[PlaintextSize];


/*  Expected ciphertext
  00: 6E 2E 35 9A 25 68 F9 80 41 BA 7 28 DD D 69 81
  16: E9 7E 7A EC 1D 43 60 C2 A 27 AF CC FD 9F AE B
  32: F9 1B 65 C5 52 47 33 AB 8F 59 3D AB CD 62 B3 57
  48: 16 39 D6 24 E6 51 52 AB 8F 53 C 35 9F 8 61 D8
  64: 7 CA D BF 50 D 6A 61 56 A3 8E 8 8A 22 B6 5E
  80: 52 BC 51 4D 16 CC F8 6 81 8C E9 1A B7 79 37 36
  96: 5A F9 B BF 74 A3 5B E6 B4 B 8E ED F2 78 5E 42
  112: 87 4D
*/

void setup()
{
  Serial.begin(115200);
  while (!Serial);


  CC20.clear();
  CC20.setKey(CC20Key, 32);
  CC20.setIV(CC20Nonce, 12);
  CC20.setCounter(CC20Counter, 4);
  CC20.encrypt(Ciphertext, Plaintext, PlaintextSize);


  Serial.print("00: ");
  for (int i = 0; i < PlaintextSize; i++)
  {
    Serial.print(Ciphertext[i], HEX);
    if ((i % 16) == 15)
    {
      Serial.println();
      Serial.print(i+1);
      Serial.print(": ");
    }
    else
      Serial.print(' ');
  }
  Serial.println();
}


void loop() {}

AGorgan:
After using other ciphers from the said library we come up to the conclusion that maybe the whole library is broken as no cypher from that library worked.

It's a poor craftsman who blames his tools.

we come up to the conclusion that maybe the whole library is broken as no cypher from that library worked.

Much more likely, you are using the library incorrectly.

Looks like you had it right except for one small typo:

byte iv[] = {0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00};

That should be:

byte iv[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00};