Безопасная связь между Arduino и PHP с использованием RC4 и base64

Я пытаюсь сделать слегка защищенную связь между Arduino и PHP. Я не могу использовать SSL из-за отсутствия питания на Arduino. Поэтому я хотел использовать RC4 для шифрования данных из PHP, а также получать в Arduino и расшифровывать. также зашифровать с Arduino и отправить на PHP.

Проблема в том, что зашифрованные данные, отправленные с PHP, не совпадают в Arduino.

На PHP я получаю HesshwkfFk8Q в base64, а в Arduino я получаю nZcwrlpZEr0V в base64. разные результаты, когда они должны быть равны.

Я думаю, что у меня неправильная реализация Arduino RC4. Я использую этот https://github.com/adamvr/arduino-base64

Это код:

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 ответа

У меня была та же проблема, и я могу подтвердить, что ваша функция RC4 в Arduino неправильная, вы можете использовать это вместо:

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];
    }

Это текущая реализация, которую я использую

#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;
};
;

После множества безуспешных попыток я получил следующие три кода:

  • Код Arduino реализовал RC4 (адаптированный из этого кода C++ ) и кодировал зашифрованный результат в Base64.
  • Код PHP, который декодирует base64 и расшифровывает RC4
  • HTML для тестирования PHP (и в конечном итоге это будет индекс веб-сервера ESP32)

На момент написания я использую ядро ​​Arduino для ESP32 версии 2.0.0, учтите, что если вы не используете эту версию Base64.h, может быть несовместимость (подробнее здесь )

Код Arduino

      #include "base64.h"
#include "rc4.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

using namespace std;

void rc4_setup( struct rc4_state *s, unsigned char *key,  int length )
{
    int i, j, k, *m, a;

    s->x = 0;
    s->y = 0;
    m = s->m;

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

    j = k = 0;

    for( i = 0; i < 256; i++ )
    {
        a = m[i];
        j = (unsigned char) ( j + a + key[k] );
        m[i] = m[j]; m[j] = a;
        if( ++k >= length ) k = 0;
    }
} 

void rc4_crypt( struct rc4_state *s, unsigned char *data, int length )
{ 
    int i, x, y, *m, a, b;

    x = s->x;
    y = s->y;
    m = s->m;

    for( i = 0; i < length; i++ )
     {
        x = (unsigned char) ( x + 1 ); a = m[x];
        y = (unsigned char) ( y + a );
        m[x] = b = m[y];
        m[y] = a;
        data[i] ^= m[(unsigned char) ( a + b )];
    }

    s->x = x;
    s->y = y;
}

int main()
{
    char key[128] = {"Hello"};
    char data[512] = {"secretMsg"};
    struct rc4_state *s;
    s=(struct rc4_state *) malloc (sizeof(struct rc4_state));
    
    printf("key : %s\n", key);
    printf("raw : %s\n", data);
    rc4_setup(s, (unsigned char *)key, strlen(key));  
    /*
    int i;
    for (i = 0; i < 256; i++) {
        printf("%d ", s->m[i]);
    }
    printf("\n");
    */
    rc4_setup(s, (unsigned char *)key, strlen(key));  
    rc4_crypt(s, (unsigned char *)data, strlen(data));
    printf("encrypt  : %s\n",data);

    base64 b;

    String encoded = b.encode((unsigned char *)data, strlen(data));
    
    Serial.println(encoded);
    
    rc4_setup(s, (unsigned char *)key, strlen(key));  
    rc4_crypt(s, (unsigned char *)data, strlen(data));
    printf("decrypt  : %s\n",data);
    return 0;
}

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

void loop(){
  }

КОД PHP

      <?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-КОД ДЛЯ ТЕСТИРОВАНИЯ и, в конечном итоге, код, который будет размещен на веб-сервере ESP32 (подробнее здесь )

      <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_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>
Другие вопросы по тегам