Secure communication between arduino and PHP using RC4 and base64

4.2k views Asked by At

I'm trying to make a mildly secure communication between Arduino and PHP. I can not use SSL for lack of power on the Arduino. So I wanted to use RC4 to encrypt data from PHP, and receive into the Arduino and decrypt. also encrypt from the Arduino and send to PHP.

The problem is that the encrypted data sent from PHP, do not coincide in the Arduino.

On PHP I get HesshwkfFk8Q in base64 and in the Arduino i get nZcwrlpZEr0V in base64. different results when they should be equal.

I think I have a wrong Arduino RC4 implementation. I'm using this https://github.com/adamvr/arduino-base64

This is the code:

Arduino

#include <Base64.h>

unsigned char S[256];
char has[512];

#define S_SWAP(a,b) do { int t = S[a]; S[a] = S[b]; S[b] = t; } while(0)

void rc4(char *key, char *data){
     int i,j;

     for (i=0;i<256;i++){
         S[i] = i;
     }

     j = 0;
     for (i=0;i<256;i++){
         j = (j+S[i]+key[i%strlen(key)]) %256;
         S_SWAP(S[i],S[j]);
     }

     i = j = 0;
     for (int k=0;k<strlen(data);k++){
         i = (i+1) %256;
         j = (j+S[i]) %256;
         S_SWAP(S[i],S[j]);
         has[k] = data[k]^S[(S[i]+S[j]) %256];
     }
     has[strlen(data)+1] = '\0';

}

void setup() {
  Serial.begin(9600);
  char key[] = "Hello";
  char sdata[] = "secretMsg";

  rc4(key,sdata);
  Serial.print("Encrypted : ");

  char out[100];
  base64_encode(out,has,strlen(has));
  Serial.println(out);

  char out2[100];
  base64_decode(out2,out,strlen(out));

  rc4(key,out2);
  Serial.print("Decrypted : ");

  Serial.println(has);

}

void loop(){

}

PHP

  <?php  
    $key = 'Hello';  
    $msg = 'secretMsg';  
    $encrypted = rc4_crypt($key, $msg);  

    echo 'encrypted b64: ', base64_encode($encrypted) ,'<br>';
    echo "decrip: " , rc4_decrypt($key,rc4_crypt($key, $msg));
    exit;


    function rc4_crypt($key,$msg) {  
          $td = mcrypt_module_open('arcfour', '' , 'stream', '');  
    mcrypt_generic_init($td, $key, null);  
    $encrypted = mcrypt_generic($td, $msg);  
    mcrypt_generic_deinit($td);  
    mcrypt_module_close($td);  
    return $encrypted;         
    }  

    function rc4_decrypt($key,$msg) {  
    return rc4_crypt($key,$msg);  
    }  
  ?>  
3

There are 3 answers

0
Pietro On

After a lot of trials without success I ended up with the following three codes:

  • Arduino code implemented RC4 (adapted from this C code) and encoding the encrypted result in Base64
  • PHP code that decodes base64 and decrypts RC4
  • HTML to test the PHP (and eventually it will be the index of ESP32 Webserver). ATTENTION! sending Base64 in the Url can be dangerous becasue the characters '+' '/' '=' can create problems, so make sure you read this post Base64 in url and the charachters that can be problematic are replaced by '.' '_' '-'

At time of writing I am using Arduino core for ESP32 version 2.0.0 consider that if you are not using this version of Base64.h there could be incompatibility (more details here)

Arduino Code

#include <stdio.h>
#include <string.h>
#include "base64.h"

typedef unsigned long ULONG;

void rc4_init(unsigned char *s, unsigned char *key, unsigned long Len) 
{
    int i =0, j = 0;
    char k[256] = {0};
    unsigned char tmp = 0;
    for (i=0;i<256;i++) {
        s[i] = i;
        k[i] = key[i%Len];
    }
    for (i=0; i<256; i++) {
        j=(j+s[i]+k[i])%256;
        tmp = s[i];
        s[i] = s[j]; 
        s[j] = tmp;
    }
 }

void rc4_crypt(unsigned char *s, unsigned char *Data, unsigned long Len) 
{
    int i = 0, j = 0, t = 0;
    unsigned long k = 0;
    unsigned char tmp;
    for(k=0;k<Len;k++) {
        i=(i+1)%256;
        j=(j+s[i])%256;
        tmp = s[i];
        s[i] = s[j]; 
        s[j] = tmp;
        t=(s[i]+s[j])%256;
        Data[k] ^= s[t];
     }
} 

int main()
{ 
    unsigned char s[256] = {0}; //S-box
    char key[256] = "Hello";
    char pData[512] = "secretMsg";
    ULONG len = strlen(pData);
    printf("key : %s\n", key);
    printf("raw : %s\n", pData);
    
    rc4_init(s,(unsigned char *)key,strlen(key)); 
    rc4_crypt(s,(unsigned char *)pData,len);
    printf("encrypt  : %s\n", pData);

    base64 b;
    
    String encoded = b.encode((unsigned char *)pData, strlen(pData));
        
    Serial.println(encoded);
 
    rc4_init(s,(unsigned char *)key, strlen(key)); 
    rc4_crypt(s,(unsigned char *)pData,len);
    printf("decrypt  : %s\n",pData);

    
    
    return 0;
}


void setup(){
  Serial.begin(115200);
  main();
}

void loop(){
}

PHP CODE

<?php

function rc4_crypt($key,$msg) {  
          $td = mcrypt_module_open('arcfour', '' , 'stream', '');  
    mcrypt_generic_init($td, $key, null);  
    $encrypted = mcrypt_generic($td, $msg);  
    mcrypt_generic_deinit($td);  
    mcrypt_module_close($td);  
    return $encrypted;         
    }  

    function rc4_decrypt($key,$msg) {  
    return rc4_crypt($key,$msg);  
    }  


if(isset($_POST['base'])) {
    $key = 'Hello';

    $msg_revealed = $_POST['base']; 
    rc4_decrypt($key, base64_decode($msg_revealed))  //decode form base64 and decrypt

    $msg = 'secretMsg';     //for testing
    $encrypted = rc4_crypt($key, $msg);  

    echo 'encrypted b64: ', base64_encode($encrypted) ,'<br>';
    echo "decrip: " , rc4_decrypt($key,rc4_crypt($key, $msg));
    exit;
}

?>

HTML CODE FOR TESTING and eventually the code that will be hosted on ESP32 webserver (more info here)

<html >
   <head>
      <title>test Base64</title>
      <meta http-equiv='content-type' content='text/html;charset=utf-8' />
      <meta name='generator' content='Geany 1.36' />
   </head>
   <body>
      <form action='https://www.yourwebsite.com/base64.php' method='post' id='downloadForm'>
        <input hidden name='base' value='PASS_THE_URL_BASE64_ENCRYPTED_STRING_HERE'>
        </form>
      <div style='text-align: center;'><button type='button' class='btn btn-danger' onclick='sendBaseForm()'>TEST</button></div>
   </body>
   <script>
   
   function sendBaseForm(){     
           document.getElementById('downloadForm').submit();       
   }
         
    </script>
</html>
1
Daniele On

I had the same problem and I can confirm you that your RC4 function in Arduino is wrong, you can use this instead:

unsigned char S[256];
unsigned int i, j;    

void swap(unsigned char *s, unsigned int i, unsigned int j) {
        unsigned char temp = s[i];
        s[i] = s[j];
        s[j] = temp;
    }

    /* KSA */
    void rc4_init(unsigned char *key, unsigned int key_length) {
        for (i = 0; i < 256; i++)
            S[i] = i;

        for (i = j = 0; i < 256; i++) {
            j = (j + key[i % key_length] + S[i]) & 255;
            swap(S, i, j);
        }

        i = j = 0;
    }

    /* PRGA */
    unsigned char rc4_output() {
        i = (i + 1) & 255;
        j = (j + S[i]) & 255;

        swap(S, i, j);

        return S[(S[i] + S[j]) & 255];
    }
0
Michael Paleo On

This is the current implementation that i'm using

#define SWAP(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
class RC4 
{
public:

    RC4 () 
    {
        memset(sbox,0,256);
        memset(key,0,256);
    }
    virtual ~RC4 ()
    {                           
        memset(sbox,0,256);
        memset(key,0,256);   
    }

    char *Encrypt(char *pszText,const char *pszKey) 
    {
        i=0, j=0,n = 0;
        ilen = (int)strlen(pszKey);

        for (m = 0;  m < 256; m++)
        {
            *(key + m)= *(pszKey + (m % ilen));
            *(sbox + m) = m;
        }
        for (m=0; m < 256; m++)
        {
            n = (n + *(sbox+m) + *(key + m)) &0xff;
            SWAP(*(sbox + m),*(sbox + n));
        }

        ilen = (int)strlen(pszText);
        for (m = 0; m < ilen; m++)
        {
            i = (i + 1) &0xff;
            j = (j + *(sbox + i)) &0xff;
            SWAP(*(sbox+i),*(sbox + j));

            k = *(sbox + ((*(sbox + i) + *(sbox + j)) &0xff ));
            if(k == *(pszText + m))       
                k = 0;
            *(pszText + m) ^=  k;
        }

        return pszText;
    }

    char *Decrypt(char *pszText,const char *pszKey)
    {
        return Encrypt(pszText,pszKey) ;
    }

private:
    unsigned char sbox[256];
    unsigned char key[256],k;
    int  m, n, i, j, ilen;
};
;