3

I have a unsigned char array of which I'd like to calculate the CRC32 checksum.

The CRC32 function also expects a unsigned char pointer, however, it interprets the array as an ASCII array.

This is the CRC function:

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

unsigned int crc32(unsigned char *message) 
{
   int i, j;
   unsigned int byte, crc, mask;

   i = 0;
   crc = 0xFFFFFFFF;
   while (message[i] != 0) {
      byte = message[i];            // Get next byte.
      crc = crc ^ byte;
      for (j = 7; j >= 0; j--) {    // Do eight times.
         mask = -(crc & 1);
         crc = (crc >> 1) ^ (0xEDB88320 & mask);
      }
      i = i + 1;
   }
   return ~crc;
}

int main(int argc, char **argv)
{
    unsigned char *arr;
    if ((arr = malloc(64)) == NULL) {
        perror("Could not allocate memory");
        exit(EXIT_FAILURE);
    }
    char str[] = "47d46d17e759a1dec810758c08004510002127d90000401152e4c0a8b21fc0a8b2255b9b5b9c000db20caabbccddee00000000000000000000000000";
    memcpy(arr, str, strlen(str));
    // ...
    unsigned int crc = crc32(arr);
    printf("CRC: 0x%x\n", crc); // 0xB6BA014A instead of 0xBF6B57A2

    return 0;
}

Now, I'd like to calculate the CRC32 but the unsigned char array has to be interpreted as an hex array.

F.ex., this is the result of the calculated CRC:
Input:
"47d46d17e759a1dec810758c08004510002127d90000401152e4c0a8b21fc0a8b2255b9b5b9c000db20caabbccddee00000000000000000000000000"

  • as ASCII: 0xB6BA014A (this is what I usually get because it's interpreted as ASCII)
  • as Hex.: 0xBF6B57A2 (this is the checksum I want)
j3141592653589793238
  • 1,810
  • 2
  • 16
  • 38
  • 1
    It sounds like you are reading your input as a string rather than converting each pair of characters to the number the hex value represents. Hard to say without a [mcve], but it seems like you're just passing the wrong data to the function. – Retired Ninja Dec 01 '18 at 19:25
  • What you want to do is convert the hexadecimal numeral in `str` to data in a new array of `unsigned char`, which you can then pass to `crc32`. – Eric Postpischil Dec 01 '18 at 19:37
  • Ok. Then my question is how to achieve that in a swift way. – j3141592653589793238 Dec 01 '18 at 19:39
  • Create a new array of the required size. Examine the characters in `str`. You may wish to examine them in pairs. For each character, prepare the four bits it represents when interpreted as a hexadecimal digit. Put those bits into `str`. (If you do that two characters at a time, you will be putting eight bits into the new array at a time, which is generally more convenient.) If you do it this way, you will need to modify `crc32` to take the length as a separate parameter instead of deducing it from a null byte (since a null byte is valid data). – Eric Postpischil Dec 01 '18 at 19:42
  • Possible duplicate of [Hexadecimal string to byte array in C](https://stackoverflow.com/questions/3408706/hexadecimal-string-to-byte-array-in-c) – fdk1342 Dec 01 '18 at 19:42
  • Also `memcpy(arr, str, sizeof(str));` --> `memcpy(arr, str, strlen(str)); ` as `sizeof(str)` is `sizeof of pointer`. – kiran Biradar Dec 01 '18 at 19:42
  • 1
    Alternately, you can do the hexadecimal conversion inside `crc32`: Examine the characters in `message` two at a time. For each character, prepare the four bits it represents as a hexadecimal digit. Put the two four-bit values together to make one eight-bit value, and use that in the CRC. – Eric Postpischil Dec 01 '18 at 19:43
  • It seems like you only want to test the crc function. The problem is that the way you initialize the array. If you want the arrary to be initialized not as a string but as binary data it needs to be in the form of, for example, `arr[32] = { 0x47, 0xd4, ... )'` – fdk1342 Dec 01 '18 at 19:47
  • I can't change that. The array will always be initialized as non-binary. – j3141592653589793238 Dec 01 '18 at 19:49
  • Per @EricPostpischil suggestion, then you have to change the line `byte = message[i];` to not use the ASCII value but the byte value. See the Hexadecimal string question on how to use `sscanf()`. – fdk1342 Dec 01 '18 at 19:53
  • There is no such thing as a "HEX"-array or an "ASCII"-array. – Swordfish Dec 01 '18 at 19:58
  • True. I did't know how to express that. – j3141592653589793238 Dec 01 '18 at 19:59

1 Answers1

2

How to interpret an unsigned char array as hex array?

  • Convert each pair of hexadecimal characters in the string to a byte value. Code below converts via a compound literal to form a 3 byte string, followed by a call to strtoul().

    //                    v----------------------------------v _compound literal_
    arr2[i / 2] = strtoul((char[3]) {str[i], str[i + 1], '\0'}, 0, 16);
    

    More advanced code would test for the unexpected presence of non-hexadecimal characters or an odd/zero length.


CRC calculation changes needed

  • Change CRC calculation to a length based one rather than a string one.

    // unsigned int crc32(const char *)
    unsigned int crc32(const void *m, size_t len)
    

    Although not coded below, consider uint32_t instead of unsigned int in crc32() for correct operation when unsigned is not 32-bit.


Altogether

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

unsigned int crc32(const void *m, size_t len) {
  const unsigned char *message = m;
  size_t i;
  int j;
  unsigned int byte, crc, mask;

  i = 0;
  crc = 0xFFFFFFFF;
  //while (message[i] != 0) {
  while (i < len) {
    byte = message[i];            // Get next byte.
    crc = crc ^ byte;
    for (j = 7; j >= 0; j--) {    // Do eight times.
      mask = -(crc & 1);
      crc = (crc >> 1) ^ (0xEDB88320 & mask);
    }
    i = i + 1;
  }
  return ~crc;
}

Sample usage

int main() {
  char str[] =
      "47d46d17e759a1dec810758c08004510002127d90000401152e4c0a8b21fc0a8b2255b9b5b9c000db20caabbccddee00000000000000000000000000";
  size_t len = strlen(str);
  unsigned int crc = crc32(str, len);
  printf("CRC: 0x%X\n", crc); // 0xB6BA014A instead of 0xBF6B57A2

  size_t len2 = (len + 1) / 2;
  unsigned char arr2[len2];
  for (size_t i = 0; i < len; i += 2) {
    arr2[i / 2] = strtoul((char[3]) {str[i], str[i + 1], '\0'}, 0, 16);
  }
  crc = crc32(arr2, len2);
  printf("CRC: 0x%X\n", crc); // 0xB6BA014A instead of 0xBF6B57A2

  return 0;
}

Output

CRC: 0xB6BA014A
CRC: 0xBF6B57A2

OP original code had undefined behavior in that it looked for a null character with while (message[i] != 0) {, yet memcpy(arr, str, strlen(str)); failed to provide one.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256