How does this code extract the signature? - php

I have to debug an old PHP script from a developer who has left the company. I understand the most part of the code, except the following function. My question: What does...
if($seq == 0x03 || $seq == 0x30)
...mean in context of extracting the signature out of an X.509 certificate?
public function extractSignature($certPemString) {
$bin = $this->ConvertPemToBinary($certPemString);
if(empty($certPemString) || empty($bin))
{
return false;
}
$bin = substr($bin,4);
while(strlen($bin) > 1)
{
$seq = ord($bin[0]);
if($seq == 0x03 || $seq == 0x30)
{
$len = ord($bin[1]);
$bytes = 0;
if ($len & 0x80)
{
$bytes = ($len & 0x0f);
$len = 0;
for ($i = 0; $i < $bytes; $i++)
{
$len = ($len << 8) | ord($bin[$i + 2]);
}
}
if($seq == 0x03)
{
return substr($bin,3 + $bytes, $len);
}
else
{
$bin = substr($bin,2 + $bytes + $len);
}
}
else
{
return false;
}
}
return false;
}

An X.509 certificate contains data in multiple sections (called Tag-Length-Value triplets). Each section starts with a Tag byte, which indicates the data format of the section. You can see a list of these data types here.
0x03 is the Tag byte for the BIT STRING data type, and 0x30 is the Tag byte for the SEQUENCE data type.
So this code is designed to handle the BIT STRING and SEQUENCE data types. If you look at this part:
if($seq == 0x03)
{
return substr($bin,3 + $bytes, $len);
}
else // $seq == 0x30
{
$bin = substr($bin,2 + $bytes + $len);
}
you can see that the function is designed to skip over Sequences (0x30), until it finds a Bit String (0x03), at which point it returns the value of the Bit String.
You might be wondering why the magic number is 3 for Bit String and 2 for Sequence. That is because in a Bit String, the first value byte is a special extra field which indicates how many bits are unused in the last byte of the data. (For example, if you're sending 13 bits of data, it will take up 2 bytes = 16 bits, and the "unused bits" field will be 3.)
Next issue: the Length field. When the length of the Value is less than 128 bytes, the length is simply specified using a single byte (the most significant bit will be 0). If the length is 128 or greater, then the first length byte has bit 7 set, and the remaining 7 bits indicates how many following bytes contain the length (in big-endian order). More description here. The parsing of the length field happens in this section of the code:
$len = ord($bin[1]);
$bytes = 0;
if ($len & 0x80)
{
// length is greater than 127!
$bytes = ($len & 0x0f);
$len = 0;
for ($i = 0; $i < $bytes; $i++)
{
$len = ($len << 8) | ord($bin[$i + 2]);
}
}
After that, $bytes contains the number of extra bytes used by the length field, and $len contains the length of the Value field (in bytes).
Did you spot the error in the code? Remember,
If the length is 128 or greater, then the first length byte has bit 7
set, and the remaining 7 bits indicates how many following bytes
contain the length.
but the code says $bytes = ($len & 0x0f), which only takes the lower 4 bits of the byte! It should be:
$bytes = ($len & 0x7f);
Of course, this error is only a problem for extremely long messages: it will work fine as long as the length value will fit within 0x0f = 15 bytes, meaning the data has to be less than 256^15 bytes. That's about a trillion yottabytes, which ought to be enough for anybody.

As Pateman says above, you just have a logical if, we're just checking if $seq is either 0x30 or 0x03.
I have a feeling you already know that though, so here goes. $seq is the first byte of the certificate, which is probably either the version of the certificate or the magic number to denote that the file is a certificate (also known as "I'm guessing this because 10:45 is no time to start reading RFCs").
In this case, we're comparing against 0x30 and 0x03. These numbers are expressed in hexadecimal (as is every number starting with 0x), which is base-16. This is just really a very convenient shorthand for binary, as each hex digit corresponds to exactly four binary bits. A quick table is this:
0 = 0000
1 = 0001
2 = 0010
3 = 0011
...
...
E = 1110
F = 1111
Equally well, we could have said if($seq == 3 || $seq == 48), but hex is just much easier to read and understand in this case.

I'd hazard a guess that it's a byte-order-independent check for version identifier '3' in an x.509 certificate. See RFC 1422, p7. The rest is pulling the signature byte-by-byte.

ord() gets the value of the ASCII character you pass it. In this case it's checking to see if the ASCII character is either a 0 or end of text (according to this ASCII table).

0x03 and 0x30 are hex values. Look that up and you'll have what $seq is matching to

Related

Convert string to consistent but random 1 of 10 options

I have many strings. Each string something like:
"i_love_pizza_123"
"whatever_this_is_now_later"
"programming_is_awesome"
"stack_overflow_ftw"
...etc
I need to be able to convert each string to a random number, 1-10. Each time that string gets converted, it should consistently be the same number. A sampling of strings, even with similar text should result in a fairly even spread of values 1-10.
My first thought was to do something like md5($string), then break down a-f,0-9 into ten roughly-equal groups, determine where the first character of the hash falls, and put it in that group. But doing so seems to have issues when converting 16 down to 10 by multiplying by 0.625, but that causes the spread to be uneven.
Thoughts on a good method to consistently convert a string to a random/repeatable number, 1-10? There has to be an easier way.
Here's a quick demo how you can do it.
function getOneToTenHash($str) {
$hash = hash('sha256', $str, true);
$unpacked = unpack("L", $hash); // convert first 4 bytes of hash to 32-bit unsigned int
$val = $unpacked[1];
return ($val % 10) + 1; // get 1 - 10 value
}
for ($i = 0; $i < 100; $i++) {
echo getOneToTenHash('str' . $i) . "\n";
}
How it works:
Basically you get the output of a hash function and downscale it to desired range (1..10 in this case).
In the example above, I used sha256 hash function which returns 32 bytes of arbitrary binary data. Then I extract just first 4 bytes as integer value (unpack()).
At this point I have a 4 bytes integer value (0..4294967295 range). In order to downscale it to 1..10 range I just take the remainder of division by 10 (0..9) and add 1.
It's not the only way to downscale the range but an easy one.
So, the above example consists of 3 steps:
get the hash value
convert the hash value to integer
downscale integer range
A much shorter example with crc32() function which returns integer value right away thus allowing us to omit step 2:
function getOneToTenHash($str) {
$int = crc32($str); // 0..4294967295
return ($int % 10) + 1; // 1..10
}
below maybe what u want
$inStr = "hello world";
$md5Str = md5($inStr);
$len = strlen($md5Str);
$out = 0;
for($i=0; $i<$len; $i++) {
$out = 7*$out + intval($md5Str[$i]); // if you want more random, can and random() here
}
$out = ($out % 10 + 9)%10; // scope= [1,10]

Need help ensuring text shifted 'x' amount of spaces is transformed into letters, not random symbols

I'm working on a page on cryptography, and have decided to include a Caesar Cipher, a method in which you shift a letter 'X' amount of spaces left or right. Example: Encrypting the letter 'A' with a shift parameter of 2. A => C.
The code below is working somewhat decently. It's able to encrypt/decrypt the text I enter, as long as it's between certain ASCII values.
The values 65-90 in ASCII is uppercase letters, and 97-122 is lowercase letters. My problem is if my shift parameter is so large, that I exceed these intervals.
Let's say I have the letter 'A' which I want to encrypt with a shift parameter of 100. If I try this, the letter 'A' becomes this character (http://www.theasciicode.com.ar/ascii-codes/spanish-enye-capital-letter-n-tilde-enie-uppercase-ascii-code-165.gif), which can't show up in my browser, and is converted into a weird question mark.
My question is, what are my options for avoiding these weird symbols? Is it possible to make it so it only includes letters (uppercase and lowercase) and maybe numbers as well?
I hope you can help me. I will be online all day, so if you need any more information, just ask :-)
My form field:
<form method="post">
<input type="text" name="textCaesarEncrypt" autocomplete="off" placeholder="Indtast tekst">
<input type="number" name="shiftCaesarEncrypt" autocomplete="off">
<label>Krypter</label>
<input type="radio" name="caesarEncryptOrDecrypt" value="caesarEncrypt">
<label>Dekrypter</label>
<input type="radio" name="caesarEncryptOrDecrypt" value="caesarDecrypt">
<input type="submit" name="submitCaesarEncrypt" value="Videre">
My PHP:
<?php
$caesarEncryptOrDecrypt = $_POST ["caesarEncryptOrDecrypt"];
if (!empty($_POST['textCaesarEncrypt']) AND
!empty($_POST['shiftCaesarEncrypt'])){
$string = $_POST['textCaesarEncrypt'];
$shift = $_POST['shiftCaesarEncrypt'];
$shiftedString = "";
if($caesarEncryptOrDecrypt == "caesarEncrypt") {
for ($i = 0; $i < strlen($string); $i++) {
$ascii = ord($string[$i]);
$shiftedChar = chr($ascii + $shift);
$shiftedString .= $shiftedChar;
}
echo $shiftedString;
}
if($caesarEncryptOrDecrypt == "caesarDecrypt") {
for ($i = 0; $i < strlen($string); $i++) {
$ascii = ord($string[$i]);
$shiftedChar = chr($ascii - $shift);
$shiftedString .= $shiftedChar;
}
echo $shiftedString;
}
}
?>
First of all, it's important to mention that Caesar-Cipher is not recommended, it's too weak, weak, weak, too weak and pretty easy to be broken.
for sure you don't want to use an encrypting system which was used before more that 2000 years
Julius Caesar: 13 July 100 BC – 15 March 44 BC)
However, The key here is in the up next formula -in the encrypting case-:
while to decrypt your encrypted data , you will need to use the next formula :
where En(x) refers to encrypted char, x = char, n = shift value;
formulas images source [wikipedia]
However this formula is pretty good for humans, but not for computers , humans only have 26 Latin character, while computers have ASCII which represented by 128, also we don't care about the case of the letter, so Hello = heLLo for us, while it is not the same with computers which represents a as 97 and A as 65.
so you will need to convert your ASCII to easy 1-26 range of character.
here i will implement a simple edit in your encrypting part, and I hope that helps you to implement the rest of decrypting part of your code.
for ($i = 0, $n = strlen($string); $i < $n; $i++) {
$char = $string[$i];
// here we are checking the character type
// to set the range which will be used later
if (ctype_upper($char)) {
$range = 65;
} else if (ctype_lower($char)) {
$range = 97;
}
// convert the ascii indexed chars into alphabetic indexed chars
$ascii = ord($char) - $range;
// the caesar folrmula based on alphabetic indexed chars
$shiftedChar = chr((($ascii + $shift) % 26) + $range);
$shiftedString .= $shiftedChar;
}
echo $shiftedString;
If you want to make sure that only alphabetic characters will be encoded, you may use ctype_alpha to check that, it's returns true if the character is alphabetic chars.
It's also important to notice that in decrypting Caesar-cipher , you will need as mentioned before to use the next formula :
$shiftedChar = chr((($ascii - $shift) % 26) + $range);
// ^ notice this
also to change your ranges as follows :
if (ctype_upper($char)) {
$range = 90; // start with Z
} else if (ctype_lower($char)) {
$range = 122; // start with z
}
another great helpful source from cs50 , this is a Caesar-cipher C tutorial , but it has the same concept.

Store a playing cards deck in MySQL (single column)

I'm doing a game with playing cards and have to store shuffled decks in MySQL.
What is the most efficient way to store a deck of 52 cards in a single column? And save/retrieve those using PHP.
I need 6 bits to represent a number from 0 to 52 and thus thought of saving the deck as binary data but I've tried using PHP's pack function without much luck. My best shot is saving a string of 104 characters (52 zero-padded integers) but that's far from optimal.
Thanks.
I do agree that it's not necessary or rather impractical to do any of this, but for fun, why not ;)
From your question I'm not sure if you aim to have all cards encoded as one value and stored accordingly or whether you want to encode cards individually. So I assume the former.
Further I assume you have a set 52 cards (or items) that you represent with an integer value between 1 and 52.
I see some approaches, outlined as follows, not all actually for the better of using less space, but included for the sake of being complete:
using a comma separated list (CSV), total length of 9+2*42+51 = 144 characters
turning each card into a character ie a card is represented with 8 bits, total length of 52 characters
encoding each card with those necessary 6 bits and concatenating just the bits without the otherwise lost 2 bits (as in the 2nd approach), total length of 39 characters
treat the card-ids as coefficients in a polynomial of form p(cards) = cards(1)*52^51 + cards(2)*52^50 + cards(3)*52^49 + ... + cards(52)*52^0 which we use to identify the card-set. Roughly speaking p(cards) must lie in the value range of [0,52^52], which means that the value can be represented with log(52^52)/log(2) = 296.422865343.. bits or with a byte sequence of length 37.052 respectively 38.
There naturally are further approaches, taking into account mere practical, technical or theoretical aspects, as is also visible through the listed approaches.
For the theoretical approaches (which I consider the most interesting) it is helpful to know a bit about information theory and entropy. Essentially, depending on what is known about a problem, no further information is required, respectively only the information to clarify all remaining uncertainty is needed.
As we are working with bits and bytes, it mostly is interesting for us in terms of memory usage which practically speaking is bit- or byte-based (if you consider only bit-sequences without the underlying technologies and hardware); that is, bits represent one of two states, ergo allow the differentiation of two states. This is trivial but important, actually :/
then, if you want to represent N states in a base B, you will need log(N) / log(B) digits in that base, or in your example log(52) / log(2) = 5.70.. -> 6 bits. you will notice that actually only 5.70.. bits would be required, which means with 6 bits we actually have a loss.
Which is the moment the problem transformation comes in: instead of representing 52 states individually, the card set as a whole can be represented. The polynomial approach is a way to do this. Essentially it works as it assumes a base of 52, ie where the card set is represented as 1'4'29'31.... or mathematically speaking: 52 + 1 = 1'1 == 53 decimal, 1'1 + 1 = 1'2 == 54 decimal, 1'52'52 + 1 = 2'1'1 == 5408 decimal,
But if you further look at the polynomial-approach you will notice that there is a total of 52^52 possible values whereas we would only ever use 52! = 1*2*3*...*52 because once a card is fixed the remaining possibilities decrease, respectively the uncertainty or entropy decreases. (please note that 52! / 52^52 = 4.7257911e-22 ! which means the polynomial is a total waste of space).
If we now were to use a value in [1,52!] which is pretty much the theoretical minimum, we could represent the card set with log(52!) / log(2) = 225.581003124.. bits = 28.1976.. bytes. Problem with that is, that any of the values represented as such does not contain any structure from which we can derive its semantics, which means that for each of the 52! possible values (well 52! - 1, if you consider the principle of exclusion) we need a reference of its meaning, ie a lookup table of 52! values and that would certainly be a memory overkill.
Although we can make a compromise with the knowledge of the decreasing entropy of an encoded ordered set. As an example: we sequentially encode each card with the minimum number of bits required at that point in the sequence. So assume N<=52 cards remain, then in each step a card can be represented in log(N)/log(2) bits, meaning that the number of required bits decreases, until for the last card, you don't need a bit in the first place. This would give about (please correct)..
20 * 6 bits + 16 * 5 bits + 8 * 4 bits + 4 * 3 bits + 2 * 2 bits + 1 bit = 249 bits = 31.125.. bytes
But still there would be a loss because of the partial bits used unnecessarily, but the structure in the data totally makes up for that.
So a question might be, hej can we combine the polynomial with this??!?11?! Actually, I have to think about that, I'm getting tired.
Generally speaking, knowing about the structure of a problem drastically helps decreasing the necessary memory space. Practically speaking, in this day and age, for your average high-level developer such low level considerations are not so important (hej, 100kByte of wasted space, so what!) and other considerations are weighted higher; also because the underlying technologies are often reducing memory usage by themselves, be it your filesystem or gzip-ed web-server responses, etc. The general knowledge of these kind of things though is still helpful in creating your services and datastructures.
But these latter approaches are very problem-specific "compression procedures"; general compression works differently, where as an example approach the procedures sequencially run through the bytes of the data and for any unseen bit sequences add these to a lookup table and represent the actual sequence with an index (as a sketch).
Well enough of funny talk, let's get technical!
1st approach "csv"
// your unpacked card set
$cards = range(1,52);
$coded = implode(',',$cards);
$decoded = explode(',',$coded);
2nd approach: 1 card = 1 character
// just a helper
// (not really necessary, but using this we can pretty print the resulting string)
function chr2($i)
{
return chr($i + 32);
}
function myPack($cards)
{
$ar = array_map('chr2',$cards);
return implode('',$ar);
}
function myUnpack($str)
{
$set = array();
$len = strlen($str);
for($i=0; $i<$len; $i++)
$set[] = ord($str[$i]) - 32; // adjust this shift along with the helper
return $set;
}
$str = myPack($cards);
$other_cards = myUnpack($str);
3rd approach, 1 card = 6 bits
$set = ''; // target string
$offset = 0;
$carry = 0;
for($i=0; $i < 52; $i++)
{
$c = $cards[$i];
switch($offset)
{
case 0:
$carry = ($c << 2);
$next = null;
break;
case 2:
$next = $carry + $c;
$carry = 0;
break;
case 4:
$next = $carry + ($c>>2);
$carry = ($c << 6) & 0xff;
break;
case 6:
$next = $carry + ($c>>4);
$carry = ($c << 4) & 0xff;
break;
}
if ($next !== null)
{
$set .= chr($next);
}
$offset = ($offset + 6) % 8;
}
// and $set it is!
$new_cards = array(); // the target array for cards to be unpacked
$offset = 0;
$carry = 0;
for($i=0; $i < 39; $i++)
{
$o = ord(substr($set,$i,1));
$new = array();
switch($offset)
{
case 0:
$new[] = ($o >> 2) & 0x3f;
$carry = ($o << 4) & 0x3f;
break;
case 4:
$new[] = (($o >> 6) & 3) + $carry;
$new[] = $o & 0x3f;
$carry = 0;
$offset += 6;
break;
case 6:
$new[] = (($o >> 4) & 0xf) + $carry;
$carry = ($o & 0xf) << 2;
break;
}
$new_cards = array_merge($new_cards,$new);
$offset = ($offset + 6) % 8;
}
4th approach, the polynomial, just outlined (please consider using bigints because of the integer overflow)
$encoded = 0;
$base = 52;
foreach($cards as $c)
{
$encoded = $encoded*$base + $c;
}
// and now save the binary representation
$decoded = array();
for($i=0; $i < 52; $i++)
{
$v = $encoded % $base;
$encoded = ($encoded - $v) / $base;
array_shift($v, $decoded);
}

PHP: XOR cypher decryption function doesnt work

Lately, I have been doing some research into cryptography.
To get a better understanding of all of it, I have been trying to write a more advanced version of the XOR cypher in PHP.
I got the encryption function to work just fine, but the decryption function output is quite strange and totally different from the message inputted.
The idea of the algorithm is to run a XOR operation first on the first and last character, then on the second and one but last character, and so on.
After that, it runs a XOR operation on the first two characters and the last two characters, then the third and fourth character and the 2th and 3th to last, and so on once again.
This goes on with blocks of 3, 4, 5 and more characters.
The code I have right now:
<?php
function encrypt($message, $key) {
$output_text = '';
// Add zeros at the end until the length of the message corresponds with the length of
the key
$message = str_pad($message,strlen($key),0);
if((strlen($message) % 2)) {
// The lenght of the message is odd, add a zero
$message = $message . 0;
}
// Define the final length of the message
$length = strlen($message);
// Firstly, take 1 character, then 2, then 3, etc. until you reach half the length of the message
for($characters=1; $characters<=($length/2); $characters++) {
// Loop from i til half the length of the message
for($i=0; $i<=(($length/2)-1); $i += $characters) {
// Take the first and last character, the the first two and the last two, etc.
// Stop when it crosses half the length
if( ($i + $characters ) >= ( $length / 2 ) ) break;
// firstly, the characters at the beginning
$beginning = substr($message, $i, $characters);
for($j=0; $j<$characters; $j++) {
$position = ( $i + 1 ) + $j;
$output_text .= chr(ord($beginning{$j}) ^ ord($key{$position}));
}
// Then those at the end
$ending = substr($message, $length-(($i+1) * $characters), $characters);
for($j=0; $j<$characters; $j++) {
$position = ( $length - ( ( $i + 1 )* $characters) ) + $j;
$output_text .= chr(ord($ending{$j}) ^ ord($key{$position}));
}
}
}
return $output_text;
}
function decrypt($message, $key) {
$output_text = null;
// Define the final length of the message
$length = strlen($message);
// Firstly, take 1 character, then 2, then 3, etc. until you reach half the length of the message
for($characters=1; $characters<=($length/2); $characters++) {
// Loop from i til half the length of the message
for($i=0; $i<=(($length/2)-1); $i += $characters) {
// Take the first and last character, the the first two and the last two, etc.
// Stop when it crosses half the length
if( ($i + $characters ) >= ( $length / 2 ) ) break;
// firstly, the characters at the beginning
$beginning = substr($message, $i, $characters);
for($j=0; $j<$characters; $j++) {
$position = ( $i + 1 ) + $j;
$output_text .= chr(ord($key{$position}) ^ ord($beginning{$j}));
}
// The those at the end
$ending = substr($message, $length-(($i+1) * $characters), $characters);
for($j=0; $j<$characters; $j++) {
$position = ( $length - ( ( $i + 1 )* $characters) ) + $j;
$output_text .= chr(ord($key{$position}) ^ ord($ending{$j}));
}
}
}
return $output_text;
}
$message = 'sampletextjusttotrythisoutcreatedin2012';
$key = '123';
$output_text = encrypt($message, $key);
echo $output_text . '<br /><hr />';
echo decrypt($output_text, $key);
Thanks in advance for trying to help me!
Let's start with something a bit simpler - given a message and a key, XOR the message with the key to encrypt it. XOR the encrypted message with the key to decrypt it.
$msg = "The rooster crows at midnight!";
$key = "secret key";
$cipher_text = simple_xor($msg, $key);
$plain_text = simple_xor($cipher_text, $key);
echo "Original msg: $msg\n";
echo "Supplied key: $key\n";
echo "\n";
echo "Cipher Text: " . base64_encode($cipher_text) . "\n";
echo " Decrypted: " . $plain_text . "\n";
function simple_xor($input, $key) {
# Input must be of even length.
if (strlen($input) % 2)
$input .= '0';
# Keys longer than the input will be truncated.
if (strlen($key) > strlen($input))
$key = substr($key, 0, strlen($input));
# Keys shorter than the input will be padded.
if (strlen($key) < strlen($input))
$key = str_pad($key, strlen($input), '0', STR_PAD_RIGHT);
# Now the key and input are the same length.
# Zero is used for any trailing padding required.
# Simple XOR'ing, each input byte with each key byte.
$result = '';
for ($i = 0; $i < strlen($input); $i++) {
$result .= $input{$i} ^ $key{$i};
}
return $result;
}
Here you can see intrinsic value of XOR. Given Msg XOR Key = C then C XOR Key = Msg and C XOR Msg = Key.
Now lets return to your approach - it appears you wish to mix more characters together to generate a 'stronger' encrypted result. Before doing that, take a moment to reflect on what creates encryption strength when using XOR in this fashion. During this process, assume the attacker has the above code, but not the $msg or $key.
The attacker will know how long your message and key are, because this algorithm always generates a result that is the same number of bytes as the message and key.
The strongest key will be one where each byte is different - this way the result will not contain patterns. For example, if you encrypt English text with a key containing just one repeated byte, I might notice that the cipher-text contains one byte repeated multiple times. This is probably the letter 'e' in your plain-text, the most popular vowel. If the key contained completely random bytes, then any pattern spotted in the cipher-text would not help me identify the plain-text.
So, is a msg of 'Feet' and key of 'abcd' strong? Well, it's certainly stronger than using a key of '0000', but it could be stronger. The attacker might assume that you used a simple key, containing just lower-case letters. This means to brute force this key, the attacker needs to try 26 ^ 8 possible options. This can be done in less than a second on modern computers. A better key would incorporate upper-case letters, digits, punctuation and other characters. An even better key would include non-printable characters as well, for example: $key = chr(27) . chr(6) . 'q.';
Another interesting element to consider with this algorithm is that it requires the key to be of equal length to the msg. This means to strongly encrypt a large amount of text (such as a novel) requires a key that's completely random that's also as long as the novel. Most mainstream algorithms avoid this requirement by encrypting the message in blocks. There are many different ways to implement block encoding, let me illustrate one known as cipher-block-chaining (CBC).
Simple CBC works by taking the first few bytes of the plain-text, XOR'ing it with the key, which generates the first few bytes of the cipher-text. The next few bytes of the plain-text are encrypted by XOR'ing them with the first few bytes of the cipher-text AND the key. This process repeats until all plain-text is encrypted. This creates a chain, where each block in the cipher text has been created using the prior block and key. To decrypt the last result, you must XOR the cipher-text with the previous cipher-text block and then again with the key.
Strong algorithms incorporate other features to ensure the cipher-text is as random as possible, including features that allow you to determine if an encrypted message has been modified. A good place to read more about block cipher modes is Wikipedia: http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation
The Cryptography page also has a great set of introductory information on different forms of encryption and the process of Cryptanalysis. http://en.wikipedia.org/wiki/Cryptography
Right now, the hardest part of "decrypting" a string is figuring out how long the input was. If we take that as an additional parameter, we can nearly decrypt it like this:
function decrypt($cipher, $messagelen, $key) {
if($messagelen % 2) { $messagelen++; }
$x = substr($cipher, -$messagelen + 2);
$y = substr($x, 0, strlen($key) - 1) ^ substr($key, 1);
$z = substr($x, strlen($key) - 1);
return $y . $z;
}
This is made much easier because most of the message appears in the clear at the end of the ciphertext. Oops. The only characters in that repetition which are "encrypted" are the first few, which are just XORed with the key.
The middle two characters are irretrievably lost due to an off-by-one error in encryption. Notes on how to fix this are in my comments below.

How to implement a Longitudinal Redundancy Check (LRC/CRC8/XOR8) checksum in PHP?

I'm having real problems trying to implement a XOR8/LRC checksum in PHP, according to the algorithm present here: http://en.wikipedia.org/wiki/Longitudinal_redundancy_check
What I'm trying to do is, given any string calculate its LRC checksum.
For example, I know for sure this string:
D$1I 11/14/2006 18:15:00 1634146 3772376 3772344 3772312 3772294 1*
Has a hexadecimal checksum of 39 (including the last * char).
For anyone interested what is the meaning of the string, it's is a DART (Deep-ocean Assesment and Reporting of Tsunamis) message - http://nctr.pmel.noaa.gov/Dart/Pdf/dartMsgManual3.01.pdf.
I convert the string to a binary string with 1's and 0's. From there, I try to create a byte array and apply the algorithm to the byte array, but it's not working and I can't figure out why.
The function I'm using for converting to String to Binary String is:
function str2binStr($str) {
$ret = '';
for ($i = 0, $n = strlen($str); $i < $n; ++$i)
$ret .= str_pad(decbin(ord($str[$i])), 8, 0, STR_PAD_LEFT);
return $ret;
}
The function I'm using for converting from Binary String to Binary Array is:
function byteStr2byteArray($s) {
return array_slice(unpack("C*", "\0".$s), 1);
}
Finally, the LRC implementation I'm using, with bitwise operators, is:
function lrc($byteArr) {
$lrc = 0;
$byteArrLen = count($byteArr);
for ($i = 0; $i < $byteArrLen; $i++) {
$lrc = ($lrc + $byteArr[$i]) & 0xFF;
}
$lrc = (($lrc ^ 0xFF) + 1) & 0xFF;
return $lrc;
}
Then, we convert the final decimal result of the LRC checksum with dechex($checksum + 0), so we have the final hexadecimal checksum.
After all these operations, I'm not getting the expected result, so any help will be highly appreciated.
Thanks in advance.
Also, I can't make it work following the CRC8-Check in PHP answer.
I'm afraid that nobody on StackOverflow can help you, and here's why. This question was bugging me so I went to the DART website you mentionned to take a look at their specs. Two problems became apparent:
The first one is you have misunderstood part of their specs. Messages start with a Carriage Return (\r or \0x0D) and the asterisk * is not part of the checksum
The second, bigger problem is that their specs contain several errors. Some of them may originate from bad copy/paste and/or an incorrect transformation from Microsoft .doc to PDF.
I have taken the time to inspect some of them so that would be nice if you could contact the specs authors or maintainers so they can fix them or clarify them. Here is what I've found.
2.1.2 The message breakdown mentions C/I as message status even though it doesn't appear in the example message.
2.1.3 The checksum is wrong, off by 0x31 which corresponds to the character 1.
2.2.3 The six checksums are wrong, off by 0x2D which corresponds to the character -.
2.3.1.2 I think there's a <cr> missing between dev3 and tries
2.3.1.3 The checksum is off by 0x0D and there's no delimiter between dev3 and tries. The checksum would be correct if there was a carriage return between the dev3 value and the tries value.
2.3.2.2-3 Same as 2.3.1.2-3.
2.3.3.3 Wrong checksum again, and there's no delimiter before tries.
2.4.2 The message breakdown mentions D$2 = message ID which should be D$3 = message ID.
Here's the code I used to verify their checksums:
$messages = array(
"\rD\$0 11/15/2006 13:05:28 3214.2972 N 12041.3991 W* 46",
"\rD\$1I 11/14/2006 18:15:00 1634146 3772376 3772344 3772313 3772294 1* 39",
"\rD\$1I 11/14/2006 19:15:00 1634146 3772275 3772262 3772251 3772249 1* 38",
"\rD\$1I 11/14/2006 20:15:00 1634146 3772249 3772257 3772271 3772293 1* 3E",
"\rD\$1I 11/14/2006 21:15:00 1634146 3772315 3772341 3772373 3772407 1* 39",
"\rD\$1I 11/14/2006 22:15:00 1634146 3772440 3772472 3772506 3772540 1* 3C",
"\rD\$1I 11/14/2006 23:15:00 1634146 3772572 3772603 3772631 3772657 1* 3B",
"\rD\$2I 00 tt 18:32:45 ts 18:32:00 3772311\r00000063006201* 22",
"\rD\$2I 01 tt 18:32:45 ts 18:32:00 3772311\r000000630062706900600061005f005ffffafff9fff8fff8fff7fff6fff401* 21",
"\rD\$2I 02 tt 18:32:45 ts 18:32:00 3772335\rfffdfffafff7fff5fff1ffeeffea00190048ffe1ffddffdaffd8ffd5ffd101* 21"
);
foreach ($messages as $k => $message)
{
$pos = strpos($message, '*');
$payload = substr($message, 0, $pos);
$crc = trim(substr($message, $pos + 1));
$checksum = 0;
foreach (str_split($payload, 1) as $c)
{
$checksum ^= ord($c);
}
$crc = hexdec($crc);
printf(
"Expected: %02X - Computed: %02X - Difference: %02X - Possibly missing: %s\n",
$crc, $checksum, $crc ^ $checksum, addcslashes(chr($crc ^ $checksum), "\r")
);
}
For what it's worth, here's a completely unoptimized, straight-up implementation of the algorithm from Wikipedia:
$buffer = 'D$1I 11/14/2006 18:15:00 1634146 3772376 3772344 3772312 3772294 1*';
$LRC = 0;
foreach (str_split($buffer, 1) as $b)
{
$LRC = ($LRC + ord($b)) & 0xFF;
}
$LRC = (($LRC ^ 0xFF) + 1) & 0xFF;
echo dechex($LRC);
It results in 0x0E for the string from your example, so either I've managed to fudge the implementation or the algorithm that produced 0x39 is not the same.
I realize that this question pretty old, but I had trouble figuring out how to do this. It's working now, so I figured I should paste the code. In my case, the checksum needs to return as an ASCII string.
public function getLrc($string)
{
$LRC = 0;
// Get hex checksum.
foreach (str_split($string, 1) as $char) {
$LRC ^= ord($char);
}
$hex = dechex($LRC);
// convert hex to string
$str = '';
for($i=0;$i<strlen($hex);$i+=2) $str .= chr(hexdec(substr($hex,$i,2)));
return $str;
}

Categories