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);
}
?>
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];
}
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;
};
;
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>
Related
i'm trying to send multiple data to a webserver using Ethercard library but for any reason is is only sending one data at a time.
Here is the the code I'm using:
#include <enc28j60.h>
#include <EtherCard.h>
#include <net.h>
#define HTTP_HEADER_OFFSET 163
#define MAX_STRINGS 100
#define MAX_STRING_LENGTH 8
/* Setup for Ethernet Library */
static byte mymac[] = { 0x74, 0x69, 0x69, 0x2D, 0x30, 0x31 };
const char website[] PROGMEM = "www.arksecurity.net16.net";
const char device[] = "0004";
char test[MAX_STRINGS][MAX_STRING_LENGTH+1];
unsigned long timer;
unsigned long tempo = 5000;
String register[20];
unsigned int sentOK = 0;
char* localID[MAX_STRINGS];
int countRegister = 2;
//int countRegister = 1;
int countID;
byte Ethernet::buffer[700];
void setup()
{
Serial.begin(57600);
register[0] = "empty";
if (!ether.begin(sizeof Ethernet::buffer, mymac, 53))
{
Serial.println("Failed to access Ethernet controller");
while(1);
}
else
Serial.println("Ethernet controller initialized");
Serial.println();
if (!ether.dhcpSetup())
{
Serial.println("Failed to get configuration from DHCP");
while(1);
}
else
Serial.println("DHCP configuration done");
if (!ether.dnsLookup(website))
{
Serial.println("DNS failed");
while(1);
}
else
Serial.println("DNS resolution done");
ether.printIp("SRV IP:\t", ether.hisip);
Serial.println();
timer = millis() - tempo;
}
void loop()
{
char tempRegister[80];
ether.packetLoop(ether.packetReceive());
if (millis() - timer > tempo)
{
timer += tempo;
ether.browseUrl(PSTR("/DevicesQuery.php?device="), device , website, response_callback);
if(test[0][0] != 0)
{
int missing = 0;
Serial.println("Data Received");
for(int i = 0; i < countID ; i++)
{
for(int x = 0; x < 8 ; x++)
{
if (test[i][x] == '\0')
{
missing = 1;
}
}
}
if(missing)
{
Serial.println("Data Missing");
tempo = 5000;
}
else
{
for(int g; g < countID ; g++)
{
register[g] = String(test[g]);
}
tempo = 40000;
}
}
else{
Serial.println("Waiting for server response...");
tempo = 5000;
}
//register[0] = "?ID=1234";
//register[1] = "?ID=5678";
if(register[0] != "empty")
{
for(int i = 0; i < countRegister ; i++)
{
register[i].toCharArray(tempRegister, 80);
//delay(5000);
//Serial.println(tempRegister);
ether.browseUrl(PSTR("/log.php"), tempRegister , website, browser_callback);
//while(sentOK == 0);
//sentOK = 0;
}
countRegister = 0;
register[0] = "empty";
}
}
}
static void response_callback (byte status, word off, word len) {
for(int i=0;i<MAX_STRINGS;i++)
for(int j=0;j<=MAX_STRING_LENGTH;j++)
test[i][j] = 0;
int i_string = 0;
int i_char = 0;
int i_ethBuff = off + HTTP_HEADER_OFFSET;
char carat;
countID = 0;
for (i_ethBuff = off + HTTP_HEADER_OFFSET; (carat = (char)Ethernet::buffer[i_ethBuff]) != 0; i_ethBuff++)
{
if (carat == '\n')
{ // New line char = new string
if (i_string < MAX_STRINGS - 1){
i_string++;
i_char = 0;
}
else
break; // Limite de memória do Arduino
}
else
{
if (i_char < MAX_STRING_LENGTH)
{
test[i_string][i_char] = carat;
i_char++;
} // otherwise discard the char (max length of string reached)
}
}
}
static void browser_callback (byte status, word off, word len)
{
Serial.println("Data sent");
//sentOK = 1;
}
countRegister is a variable and when it equals 1, it works great but, if he equals 2,for example, he only sends the second data to the server and ignore the first one. Anyone knows why is happening?
PS.:As for right now, I'm forcing register[0],register[1] values as you can see in the code above,but the idea is that later this values come from an outsource.
The short of it is that browseurl works asynchronously. Have a look at
http://forum.jeelabs.net/node/1477.html
I need the default behaviour (i.e ENT_COMPAT | ENT_HTML401) of the PHP-function htmlspecialchars() in C, how is this done best (that means fastest) in C?
I don't need the input string, therefore an in-place solution is possible.
It is a really simple function, it just converts these characters:
'&' -> '&'
'"' -> '"'
'<' -> '<'
'>' -> '>'
What strategy would be fastest? Looping over each character individually and creating the output buffer byte-for-byte, overwriting the input string in-place or some other solution?
This code assumes that input and output are buffers and that input contains the input string. It also assumes that the output buffer is large enough to hold the output (if not output is truncated):
long i = 0;
long j = 0;
while (input[i])
{
if (input[i] == '<')
{
memcpy(&output[j], "<", 4);
j += 4;
} else if (input[i] == '>')
{
memcpy(&output[j], ">", 4);
j += 4;
} else if (input[i] == '"')
{
memcpy(&output[j], """, 6);
j += 6;
} else if (input[i] == '&')
{
memcpy(&output[j], "&", 5);
j += 5;
} else
{
output[j++] = input[i];
}
if (j > sizeof(output)-7)
{
break;
}
i++;
}
output[j] = 0;
In C, ugly code is often the fastest.
An in-place solution would only yield performance benefits if the characters to be exchanged would be very, very rare so that the whole string can be reordered (very expensive) on every character that is detected. With normal HTML-data, where these 4 characters will appear often, an in-place solution would be much slower.
You can build a solution that works in both cases:
Calculates the required space and return an allocated string when NULL is passed, otherwise fill the passed string:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static size_t encode_len(const char *str)
{
size_t len = 0;
while (*str) {
if (*str == '"' || *str == '&' || *str == '<' || *str == '>') {
len += 5;
} else {
len += 1;
}
str++;
}
return len;
}
static char *encode(const char *str, char *dest)
{
char *ptr;
if (dest == NULL) {
dest = malloc(encode_len(str) + 1);
if (dest == NULL) return NULL;
}
ptr = dest;
while (*str) {
switch (*str) {
case '"': memcpy(ptr, """, 5); ptr += 5; break;
case '&': memcpy(ptr, "&", 5); ptr += 5; break;
case '<': memcpy(ptr, "<", 5); ptr += 5; break;
case '>': memcpy(ptr, ">", 5); ptr += 5; break;
default: *ptr++ = *str;
}
str++;
}
*ptr = *str;
return dest;
}
int main(void)
{
/* Returns an allocated string */
char *str = encode("testing & <> \"", NULL);
if (str) {
printf("%s\n", str);
free(str);
}
/* Fills a passed string */
char str2[128];
encode("testing & <> \"", str2);
printf("%s\n", str2);
return 0;
}
Here is htmlspecialchars function safe implementation.
Info
char *_output is the result.
size_t outlen is the maximum number of bytes allowed to write to _output.
char *_input is the string to be HTML escaped.
size_t inlen is the length of string to be escaped (excluding null terminator).
This function returns the number of bytes written to _output, including null terminator.
Note: This function always puts null terminator to the _output.
Code
#include <stdio.h>
#include <assert.h>
#include <stddef.h>
#include <string.h>
#include <stdint.h>
#define likely(EXPR) __builtin_expect(!!(EXPR), 1)
#define unlikely(EXPR) __builtin_expect(!!(EXPR), 0)
/*
* `char *_output` is the result.
*
* `size_t outlen` is the maximum number of bytes allowed to write to `_output`.
*
* `char *_input` is the string to be HTML escaped.
*
* `size_t inlen` is the length of string to be escaped (excluding null
* terminator).
*
* This function returns the number of bytes written to `_output`, including
* null terminator.
*
* Note: This function always puts null terminator to the `_output`.
*
*/
size_t htmlspecialchars(char * restrict _output, size_t outlen,
const char * restrict _input, size_t inlen)
{
struct html_char_map {
const char to[8];
const uint8_t len;
};
static const struct html_char_map html_map[0xffu] = {
['<'] = {"<", 4},
['>'] = {">", 4},
['"'] = {""", 6},
['&'] = {"&", 5},
};
size_t j = 0;
uint8_t len = 0;
unsigned char * restrict output = (unsigned char *)_output;
const unsigned char * restrict input = (const unsigned char *)_input;
const unsigned char *in_end = input + inlen;
while (likely(input < in_end)) {
const unsigned char *cp;
const struct html_char_map *map_to = &html_map[(size_t)*input];
if (*map_to->to == '\0') {
cp = input;
len = 1;
} else {
cp = (const unsigned char *)map_to->to;
len = map_to->len;
}
if (unlikely((j + len - 1) >= outlen))
break;
memcpy(&output[j], cp, len);
j += len;
input++;
}
if (likely(outlen > 0)) {
if (unlikely((j + 1) > outlen))
j -= len;
output[++j] = '\0';
}
return j;
}
int main(void)
{
size_t len;
char output[0xff];
const char input[] = "Google<<>>";
const char expected_output[] = "<a href="https://www.google.com">Google</a><<>>";
/* Input length (without null terminator) */
size_t input_len = sizeof(input) - 1;
/* Max allowed write bytes */
size_t output_size = sizeof(output);
len = htmlspecialchars(output, output_size, input, input_len);
/* `len` includes null terminator char */
printf("Written bytes to output: %zu\n", len);
/* `output` */
printf("output = %s\n", output);
assert(!memcmp(output, expected_output, sizeof(expected_output)));
}
C++
unsigned int hash_djb2(char *str, unsigned int str_size)
{
unsigned int hash = 5381;
for(unsigned int c = 0; c < str_size; c++)
hash = ((hash << 5) + hash) + str[c];
return (hash & 0xFFFFFFFF);
}
int main()
{
string term = "one piece";
char* data = const_cast<char*>(term.c_str());
printf("%u", hash_djb2(data, term.size()));//2850035213
}
PHP
<?php
function hash_djb2($str)
{
$hash = 5381;
$length = strlen($str);
for($i = 0; $i < $length; $i++) {
$hash = ( ($hash << 5) + $hash ) + $str[$i];
}
return ($hash & 0xFFFFFFFF);
}
echo hash_djb2("one piece");//-233010523
?>
How to make PHP return the same result as C++ does?
The str[c] in PHP is the problem, because addition with it tries to parse
the string content as a number, ie. "123"=>123 and "O", "n" etc. becomes simply 0.
Use ord(str[c]) to get ASCII values.
Additionally, int casts and more &0xFFFFFFFF are probably a good idea,
else PHP can/will switch to double with larger values.
What is a simple, elegant way to list the first x number of IPv6 IPs from a IPv6 string.
For example,
listIPs("2600:f333:10:c000::0", 4)
echos
2600:f333:10:c000::1
2600:f333:10:c000::2
2600:f333:10:c000::3
2600:f333:10:c000::4
Here's a sample of code that may have worked for IPv4, as it converted to int:
$input = "2600:f333:10:c000::/51";
$max = 4;
list($block, $cidr) = explode("/", $input);
$first = inet_pton( $block );
echo inet_ntop($first) . "\n";
for ($i = 1; $i < $max; $i++) {
//todo: die if it has exceeded block size based on $cidr
echo inet_ntop($first + $i) . "\n"; //doesn't work, packed binary?
}
Here's an example program written in C (since I don't know C++). It's fairly fast, but I'm not really happy with it. Maybe someone can help me improve it.
Edit: Obviously, I wrote this before it was turned into a PHP-only question. Turning this into PHP is left as an exercise to the reader (ew).
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>
/*
* Syntax: ./ipv6_list <ip>/<cidr-prefix>
*/
int main(int argc, char **argv) {
uint8_t start[16];
uint8_t address[16];
uint8_t mask[16] = { 0 };
uint8_t prefix = 128;
char *prefix_location;
int i;
/* This is the octet that, when changed, will result in <IP> & <mask> != <start IP> */
int mask_check_octet = 0;
if(argc != 2)
return 1;
/* Find prefix */
if((prefix_location = strstr(argv[1], "/")) != NULL) {
char *prefix_search = prefix_location + 1;
char *prefix_remaining;
long prefix_test;
if(!isdigit(*prefix_search))
return 2;
errno = 0;
prefix_test = strtol(prefix_search, &prefix_remaining, 10);
if(errno == ERANGE || prefix_test < 0 || prefix_test > 128 || strcmp(prefix_remaining, "") != 0)
return 2;
prefix = (uint8_t)prefix_test;
*prefix_location = '\0'; /* So we can just pass argv[1] into inet_pton(3) */
}
/* Convert prefix into mask */
for(i = 0; i < 16; i++) {
if(prefix == 0)
break;
mask_check_octet = i;
if(prefix < 8) {
mask[i] = ~((1 << (8 - prefix)) - 1);
break;
}
else
mask[i] = UINT8_MAX;
prefix -= 8;
}
/* Find address */
if(inet_pton(AF_INET6, argv[1], start) != 1)
return 3;
/* Start at the beginning of the network */
for(i = 0; i < 16; i++) {
start[i] &= mask[i];
address[i] = start[i];
}
/* Iterate */
while((address[mask_check_octet] & mask[mask_check_octet]) == start[mask_check_octet]) {
char address_str[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, address, address_str, sizeof(address_str));
printf("%s\n", address_str);
/* Add one to the address */
for(i = 15; i >= 0; i--) {
if(address[i] != UINT8_MAX)
break;
}
address[i]++;
for(i++; i < 16; i++)
address[i] = 0;
};
return 0;
}
You can use standard shell commands to limit its output (or just modify the while loop):
nfontes#brioche:~$ ./ipv6_list '2607:fc50:0:d00::0/106' | head -n 200
2607:fc50:0:d00::
2607:fc50:0:d00::1
2607:fc50:0:d00::2
2607:fc50:0:d00::3
2607:fc50:0:d00::4
2607:fc50:0:d00::5
2607:fc50:0:d00::6
2607:fc50:0:d00::7
2607:fc50:0:d00::8
2607:fc50:0:d00::9
2607:fc50:0:d00::a
2607:fc50:0:d00::b
2607:fc50:0:d00::c
2607:fc50:0:d00::d
2607:fc50:0:d00::e
[...]
2607:fc50:0:d00::c0
2607:fc50:0:d00::c1
2607:fc50:0:d00::c2
2607:fc50:0:d00::c3
2607:fc50:0:d00::c4
2607:fc50:0:d00::c5
2607:fc50:0:d00::c6
2607:fc50:0:d00::c7
Something similar (in PHP). It takes an IPv4/IPv6 address and increments it by given value:
// Takes an IPv4/IPv6 address in string format, and increments it by given value
function incrementIp($ip, $increment)
{
$addr = inet_pton ( $ip );
for ( $i = strlen ( $addr ) - 1; $increment > 0 && $i >= 0; --$i )
{
$val = ord($addr[$i]) + $increment;
$increment = $val / 256;
$addr[$i] = chr($val % 256);
}
return inet_ntop ( $addr );
}
believe me I have tried to code this, tried Google, and haven't had any luck.
I'm trying to implement a CRC16 using this poly
x^16 + x^10 + x^8 + x^7 + x^3 + 1
using the C language. Since I understand PHP better I'm trying to get a function going, but I'm not getting the right answer of 28713. This code is generating a CRC of 32713.
function crc16($string,$crc=0) {
for ( $x=0; $x<strlen( $string ); $x++ ) {
$crc = $crc ^ ord( $string[$x] );
echo $crc.'<br />';
for ($y = 0; $y < 8 ; $y++) {
if ( ($crc & 0x0001) == 0x0001 ) $crc = ( ($crc >> 1 ) ^ 0x10589 );
else $crc = $crc >> 1;
}
}
return $crc;
}
echo 'CRC:'.crc16('10100011');
Please I beg anyone to give a hand with this..thanks in advance.
Some CRCs are defined to process the bits from each byte from MSB to LSB, and some are defined to process bits from LSB to MSB (the latter is generally the order which is described as "reflected" and uses a reversed polynomial). Your code puts new bits in at the LSB end of the CRC and shifts right, which is suitable for a reflected CRC, but CRC-16-DECT appears to be one of the non-reflected ones.
Your input of "10100011" suggests binary, but is being processed as an 8-byte ASCII string.
To see what happens when treating 10100011 as binary instead, and working from MSB first, here's a hand calculation (as 8 bits of input doesn't require very much effort):
polynomial coefficients
|
| 10100010 <--- quotient (irrelevant)
v __________
10000010110001001 ) 10100011 <-------- input
^ 10000010110001001
-----------------
= 100001110001001
^ 10000010110001001
-----------------
= 101110101101
^ 10000010110001001
-----------------
remainder (CRC) -----> = 111000000101001
= 0x7029 = 28713
So treating the input as binary and working MSB first is the right thing to do.
Here is some C code to do the job (as I'm not really into PHP, and ultimately you want C code anyway):
#include <stdio.h>
#include <stdint.h>
static uint16_t crc16(const uint8_t *data, size_t len)
{
size_t i, j;
uint16_t crc = 0;
for (i = 0; i < len; i++) {
crc ^= (data[i] << 8); /* data at top end, not bottom */
for (j = 0; j < 8; j++) {
if ((crc & 0x8000) == 0x8000) /* top bit, not bottom */
crc = (crc << 1) ^ 0x0589; /* shift left, not right */
else
crc <<= 1; /* shift left, not right */
}
}
return crc;
}
int main(void)
{
const uint8_t in[] = { 0xa3 }; /* = 10100011 in binary */
uint16_t crc = crc16(in, sizeof(in));
printf("%u (0x%x)\n", crc, crc);
return 0;
}
Result:
$ gcc -Wall -o crc16 crc16.c
$ ./crc16
28713 (0x7029)
$
Try changing 0x10589 to 0xA001:
function crc16($string,$crc=0) {
for ( $x=0; $x<strlen( $string ); $x++ ) {
$crc = $crc ^ ord( $string[$x] );
for ($y = 0; $y < 8; $y++) {
if ( ($crc & 0x0001) == 0x0001 ) $crc = ( ($crc >> 1 ) ^ 0xA001 );
else $crc = $crc >> 1;
}
}
return $crc;
}
This code works everytime, but I'm not exactly understanding what's going on.
char *MakeCRC(char *BitString)
{
static char Res[17]; // CRC Result
char CRC[16];
int i;
char DoInvert;
for (i=0; i<16; ++i) CRC[i] = 0; // Init before calculation
for (i=0; i<strlen(BitString); ++i)
{
DoInvert = ('1'==BitString[i]) ^ CRC[15]; // XOR required?
CRC[15] = CRC[14];
CRC[14] = CRC[13];
CRC[13] = CRC[12];
CRC[12] = CRC[11];
CRC[11] = CRC[10];
CRC[10] = CRC[9] ^ DoInvert;
CRC[9] = CRC[8];
CRC[8] = CRC[7] ^ DoInvert;
CRC[7] = CRC[6] ^ DoInvert;
CRC[6] = CRC[5];
CRC[5] = CRC[4];
CRC[4] = CRC[3];
CRC[3] = CRC[2] ^ DoInvert;
CRC[2] = CRC[1];
CRC[1] = CRC[0];
CRC[0] = DoInvert;
}
for (i=0; i<16; ++i) Res[15-i] = CRC[i] ? '1' : '0'; // Convert binary to ASCII
Res[16] = 0; // Set string terminator
return(Res);
}
// A simple test driver:
#include <stdio.h>
int main()
{
char *Data, *Result; // Declare two strings
Data = "1101000101000111";
Result = MakeCRC(Data); // Calculate CRC
printf("CRC of [%s] is [%s] with P=[10000010110001001]\n", Data, Result);
return(0);
}