Archive for May, 2011

02/05/2011

Using PHP to calculate a simple checksum

There is a very specific set of rules for sending a message in most protocols, and the checksum is sent at the end of a message to make sure no corruption has occurred in transmission.

In the instance of sending a card number to the access control application I have been using PHP to integrate with in an ongoing project, the following was laid down with the protocol:

“The end of a message is made up of the checksum of the message (hex byte), created by XORing all the ASCII values together of all the characters of the message up to this point, not including the checksum itself.”

The code I wrote uses the XOR (^) operator a lot, and evaluates equations inside a string value, which I’ve never had to do before. Also as the message must be sent in hex format, I’ve used the dechex() function to convert decimal values. I have¬†endeavoured to comment the code as fully as possible:

//Get card data from previous page
$card = $_GET['card'];
$part3 = $card;
echo "Card number: $card";
//The first part of the message 0030 session, 0D addcard (13)
$message = '00300D';
$part2 = $message;
//Add the card string
$message .= $card;
//Add the rest of the message
$part4 = '00030000000001000000000000';
$message .= '00030000000001000000000000';
echo "Message before assembly: $message";
//Determine the total length of the message, + the >, hex byte length,
//ASCII null character and hex byte checksum
$length = (strlen($message))+6;
echo "Decimal data length: $length";
//Convert the value to hex
$length = dechex ( $length );
//Convert lowercase hex letters to uppercase so the XOR checksum calculations are correct.
//First inialise $caps_length variable
$caps_length = '';
//Iterate through each of the length value, replaceing lowercase letters with uppercase
for ($i = 0, $j = strlen($length); $i < $j; $i++) {
if ('0' == $length[$i]) {$caps_length .='0';}
elseif ('1' == $length[$i]) {$caps_length .='1';}
elseif ('2' == $length[$i]) {$caps_length .='2';}
elseif ('3' == $length[$i]) {$caps_length .='3';}
elseif ('4' == $length[$i]) {$caps_length .='4';}
elseif ('5' == $length[$i]) {$caps_length .='5';}
elseif ('6' == $length[$i]) {$caps_length .='6';}
elseif ('7' == $length[$i]) {$caps_length .='7';}
elseif ('8' == $length[$i]) {$caps_length .='8';}
elseif ('9' == $length[$i]) {$caps_length .='9';}
elseif ('a' == $length[$i]) {$caps_length .='A';}
elseif ('b' == $length[$i]) {$caps_length .='B';}
elseif ('c' == $length[$i]) {$caps_length .='C';}
elseif ('d' == $length[$i]) {$caps_length .='D';}
elseif ('e' == $length[$i]) {$caps_length .='E';}
elseif ('f' == $length[$i]) {$caps_length .='F';}
}
echo "Hex data length: $caps_length";
//Assemble the string to be sent (note the ASCII null character will not
//affect the checksum calculation so is not included here)
$string = '>';
$string .= $caps_length;
$part1 = $string;
$string .= $message;
echo "Assembled Message: $string";
//Calculate the checksum
//First initialise the variables
$x = 0;
$conversion = '';
//Iterate through each character of the string to convert it to its
//ASCII equivalent, with the ^ to XOR with the next value
for ($i = 0, $j = strlen($string); $i < $j; $i++) {
if ('0' == $string[$i]) {$conversion .='48^';}
elseif ('1' == $string[$i]) {$conversion .='49^';}
elseif ('2' == $string[$i]) {$conversion .='50^';}
elseif ('3' == $string[$i]) {$conversion .='51^';}
elseif ('4' == $string[$i]) {$conversion .='52^';}
elseif ('5' == $string[$i]) {$conversion .='53^';}
elseif ('6' == $string[$i]) {$conversion .='54^';}
elseif ('7' == $string[$i]) {$conversion .='55^';}
elseif ('8' == $string[$i]) {$conversion .='56^';}
elseif ('9' == $string[$i]) {$conversion .='57^';}
elseif ('>' == $string[$i]) {$conversion .='62^';}
elseif ('A' == $string[$i]) {$conversion .='65^';}
elseif ('B' == $string[$i]) {$conversion .='66^';}
elseif ('C' == $string[$i]) {$conversion .='67^';}
elseif ('D' == $string[$i]) {$conversion .='68^';}
elseif ('E' == $string[$i]) {$conversion .='69^';}
elseif ('F' == $string[$i]) {$conversion .='70^';}
}
//Add on an extra 0. Makes no difference to the final value, but in necessary
//so as not to end the expression with ^.
$conversion .='0';
eval("\$checksum = " . $conversion . ";");
$checksum = dechex ( $checksum );
//Convert lowercase hex letters to uppercase
$final_checksum = '';
for ($i = 0, $j = strlen($checksum); $i < $j; $i++) {
if ('0' == $checksum[$i]) {$final_checksum .='0';}
elseif ('1' == $checksum[$i]) {$final_checksum .='1';}
elseif ('2' == $checksum[$i]) {$final_checksum .='2';}
elseif ('3' == $checksum[$i]) {$final_checksum .='3';}
elseif ('4' == $checksum[$i]) {$final_checksum .='4';}
elseif ('5' == $checksum[$i]) {$final_checksum .='5';}
elseif ('6' == $checksum[$i]) {$final_checksum .='6';}
elseif ('7' == $checksum[$i]) {$final_checksum .='7';}
elseif ('8' == $checksum[$i]) {$final_checksum .='8';}
elseif ('9' == $checksum[$i]) {$final_checksum .='9';}
elseif ('a' == $checksum[$i]) {$final_checksum .='A';}
elseif ('b' == $checksum[$i]) {$final_checksum .='B';}
elseif ('c' == $checksum[$i]) {$final_checksum .='C';}
elseif ('d' == $checksum[$i]) {$final_checksum .='D';}
elseif ('e' == $checksum[$i]) {$final_checksum .='E';}
elseif ('f' == $checksum[$i]) {$final_checksum .='F';}
}
echo "Calculation Performed: $conversion";
//Add an if statement in case the checksum is only
//one character Eg. to make 8 into 08 or A into 0A
if (strlen($final_checksum)==1) {
$new_final_checksum = '0';
$new_final_checksum .= $final_checksum;
$final_checksum = $new_final_checksum;
}
echo "Checksum: $final_checksum";
//The final string to be sent to the controller must now be assembled and sent:
$string .=$final_checksum;
$part5 = $final_checksum;
$final_string = $part1 . $part2 . $part3 . chr(0) . $part4 . $part5;
echo "Send this to controller: $final_string";

Sample output from this code:

Card number: 15198392
Message before assembly: 00300D1519839200030000000001000000000000
Decimal data length: 46
Hex data length: 2E
Assembled Message: >2E00300D1519839200030000000001000000000000
Calculation Performed: 62^50^69^48^48^51^48^48^68^49^53^49^57^56^51^57^50^48^48^48^51^
48^48^48^48^48^48^48^48^48^49^48^48^48^48^48^48^48^48^48^48^48^48^0
Checksum: 30
Send this to controller: >2E00300D151983920003000000000100000000000030

I’ve not included all details of the actual message here. If anybody reading this wants clarification or more information, please let me know.

Tags: , ,