0

Please excuse any glaring errors in my code, I'm new to both C and python.

I'm trying to send an encrypted message from my arduino uno via a DHT22 sensor and an rf module using the radiohead ask and AESlib libraries to a raspberry pi with a usb RTL-SDR using the rtl_433 library and a python script.

I think (but I could very well be wrong) I have the Arduino side working properly and it feels as though I'm pretty close on the python side, however I don't seem to be decrypting the message properly.

An example of the string before being encrypted might be:

"55.55,24.99,0001"

Here is the code on the Arduino side:

#include <AESLib.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include "DHT.h"
#include <RH_ASK.h>
#include <Base64.h>

#define DHTPIN 2

#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
DHT dht(DHTPIN, DHTTYPE);
RH_ASK driver;

bool takeSensorReading = true;
float h;
float t;
String str_humid;
String str_temp;
String str_out;
uint8_t key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31};
char data[32];
char endChar[1] = "Ʊ";

void setup() {
  driver.init();
  dht.begin();
  Serial.begin(9600);
}

void loop() {
  delay(5000);

  int messagelength = sizeof(str_out);
  byte plain[messagelength];

  h = dht.readHumidity();
  // Read temperature as Celsius
  t = dht.readTemperature();

  // Convert Humidity to string
  str_humid = String(h, 2);

  // Convert Temperature to string
  str_temp = String(t, 2);

  // Combine Humidity and Temperature
  str_out = str_humid + "," + str_temp + ",0001";

  str_out.toCharArray(data, 32);
  aes256_enc_single(key, data);

  /*aes256_dec_single(key, data);*/
  Serial.println(data);
  int encryptedStringLength = sizeof(data);

  int encodedLength = Base64.encodedLength(encryptedStringLength);
  char encodedString[encodedLength];
  Base64.encode(encodedString, data, encryptedStringLength);
  Serial.print("Encoded string is:\t");
  Serial.println(encodedString);

  driver.send(strcat( (uint8_t *)encodedString, endChar ), 33);
  driver.waitPacketSent(); 

  takeSensorReading = !takeSensorReading;
}

and here is the python code:

import subprocess
import json
import base64
from Crypto.Cipher import AES

BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[0:-ord(s[-1])]

def print_no_newline(string):
    import sys
    sys.stdout.write(string)
    sys.stdout.flush()

def decrypt(enc):
    private_key = str(bytearray([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]))
    iv = enc[:16]
    cipher = AES.new(private_key, AES.MODE_CBC, iv)
    return unpad(cipher.decrypt(enc[16:]))

p = subprocess.Popen(['rtl_433', '-f', '433720000', '-F', 'json'],stdout=subprocess.PIPE,bufsize=1)
# rtl_433 -f 433720000 -F json
try:
    for line in iter(p.stdout.readline, ""):
        lineStripped = str(line).strip()
        try:
            jsondata = json.loads(lineStripped)
            time = jsondata["time"]
            payload = jsondata["payload"]
            if len(payload) == 33:
                payloadstring = ''
                for a in range(len(payload)):
                    payloadstring += chr(payload[a])
                payloadstring += "AAAAAAAAAA="
                print payloadstring
                encrypted = base64.b64decode(payloadstring)
                print encrypted
                decrypted = decrypt(encrypted)
                print decrypted
        except Exception as err:
            print "oh no, something went wrong"
            print("error: {0}".format(err))
except KeyboardInterrupt:
    p.kill()

p.wait()
p.stdout.close()

Here is an example of the erroneous output of the decrypt function:

"@g�އ�6S���25zD"

Any help you could give me in decoding the message from the Arduino in the python script would be really appreciated.

Thanks

Richard

*EDIT

I thought if it helps I could provide a bit more context around the transmission stages that the message goes through:

After the initial data string is constructed from the temperature and humidity sensor eg "55.55,24.99,0001" that 16 byte message is added to a 32 byte char array and encrypted using the aes128_enc_single method. The encrypted message is then base63 encoded The encrypted char array is then combined with an extra unencrypted character "Ʊ" (which I thought I could use to crudely validate the message) and sent via the radiohead ask send method:

driver.send(strcat( (uint8_t *)encodedString, endChar ), 33);

On the pyhton/reciever end I run a sub process rtl_433 -f 433720000 -F json

and listen for the output line by line. A example of the output of the rtl_433 command would look something like this: {"time": "2019-03-05 00:00:00", "model": "RadioHead ASK", "len": 33, "to": 255, "from": 255, "id": 0, "flags": 0, "payload": [111, 110, 109, 57, 120, 86, 52, 79, 121, 78, 71, 52, 48, 122, 117, 88, 67, 52, 77, 118, 85, 81, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65], "mic": "CRC"}

rtl_433 converts the message into an array of ASCII or Unicode characters.

I have then tried to convert the characters in the recieved array back into standard characters: payloadstring += chr(payload[a]) added padding to the message: payloadstring += "AAAAAAAAAA=" then base64 decoded the result: encrypted = base64.b64decode(payloadstring) and then finally decrypt that: decrypted = decrypt(encrypted)

Hopefully that info might be helpful in flagging up where I have gone wrong.

  • payloadstring += "AAAAAAAAAA=" that looks rather arbitrary – Jaromanda X Mar 4 at 0:32
  • First error: int messagelength = sizeof(str_out); no that is not the length of the String. It is the number of bytes for a String instance. The correct way to retrieve the string length is to use the member function, int messagelength = str_out.length();arduino.cc/en/Tutorial/StringLength – Mikael Patel Apr 4 at 18:44
1

By inspection only:

You are using an Initialization Vector and CBC mode in your Python code, but no IV and single block encryption in your Arduino code.

I think if you use MODE_ECB in your Python it will ignore the IV and match the Arduino code.

From a look at the AESLib github it appears your key should be exactly 16 bytes - yours is 32. I haven't looked at the code to see whether it will just ignore the rest, but the Python code definitely won't, so you should probably do what it says and use 16, and adjust your Python code to match.

Since the above wasn't enough to get it working, I would suggest divide-and-conquer: Use an online AES-128 encyrptor/decryptor such as aesencryption.net or encode-decode.com or infoencrypt.com to validate and cross-check your Arduino encryption and Python decryption functions. With a bit of luck you can at least fix them one-by-one, against a known-good reference. Perhaps with a bit of fiddling you may be able to work out where it's going wrong.

I presume the humidity value may not change much, and I have a feeling that sending several messages with the same start can substantially weaken the encryption but I'm no crypto expert and perhaps it's still adequate for your purposes.

  • Hi Mark, Thanks very much for taking the time to help out. I've had a go at implementing your suggestions by changing the uint8_t key to have only 16 values and called aes128_enc_single instead of aes_256_enc_single. On the python side I removed the iv, reduced the private_key length to 16 and changed the AES mode to AES.MODE_ECB. Unfortunatley I'm still not able to decrypt the message correctly. I have edited my original post to include some context around the stages the message goes through in transit, I hope that helps shed some light into where I am going wrong. Thanks again – richardtriestodoarduino Mar 5 at 15:22
  • Hey, thanks very much for the links ... encode-decode.com did throw up something interesting. If I use the string HelloIamamessage and the secret abcdefghijklmnop and set up the corresponding values in the Arduino script uint8_t string_key[] = {97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112}; and String test_message = "HelloIamamessage"; I get the following output from the site: k7yooB6Mj6bgxJ2ywaZj0rRDy5XLQ8VcZMRo2jfU2Ok= and the following from the Arduino IDE: k7yooB6Mj6bgxJ2ywaZj0gAAAAAAAAAAAAAAAAAAAAA= – richardtriestodoarduino Mar 5 at 21:11
  • The output starts the same but then diverges. Could this be a padding issue (or lack of it on the Arduino side) and if so would you have any idea how to solve this? – richardtriestodoarduino Mar 5 at 21:13
  • I might have worked out what's going wrong. Change your message to "HelloIamamessag" (i.e. 15 chars) and see if it matches. If so, I'll edit the answer and explain why. – Mark Smith Mar 6 at 10:01
  • Hey Mark, thanks for taking another look. I changed the message to HelloIamamessag unfortunately I still can't get the encrypted string to match that on encode-decode. Perhaps I have made a simple error. Here is a pastebin with my latest attempt (If this ever works I'll edit the question and post the working code there). The Arduino IDE is returning: 0P3iW9FSrni+evrhrWaiMQ== and encode-decode is returning: bs3Uzmhox9ARIPliQRxawQ== Both using the string HelloIamamessag – richardtriestodoarduino Mar 6 at 18:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.