Search code examples
cunionstype-conversion

When assigning a char array to union datatype, my compiler throws an error: "conversion from char* to non-scalar type"


Let me explain the overall function of my program. A user can enter a string, and that string is hashed according to the MD5 Hash algorithm. It then prints the string to stdout.

I found that using a union structure simplified the code:

union md5hash
{
     uint ui[4]; //= 16 bytes --> 128 bits 
     char ch[16]; //= 16 bytes --> 128 bits
};

My program works fine in that situation.

However, I would like to expand the functionality of my program. Instead of entering a string to be hashed, I would like an MD5 hash to be entered via stdin.

Here's a short program that attempts to replicate this functionality:

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

typedef unsigned int uint;

union md5hash
{
    uint ui[4]; //= 16 bytes --> 128 bits 
    char ch[16]; //= 16 bytes --> 128 bits
};

int main(void){


    std::string hash = "abcdefghjklqwertyuiopasdfghjklzx";
    std::cout << hash.length();
    char* chash = (char*) malloc(sizeof(hash.length()+1));
    memcpy(chash, hash.c_str(), hash.size()+1);
    for(int i = 0; chash[i] ; i++) printf("%c",chash[i]);
    md5hash test =  chash;
    getchar();

}

Yes, I know my array is 32 characters long, and the array in the union is only 16 characters (that's a problem I have yet to address). In any case, I need the hash entered via stdin to be in the same form as a string that is hashed.

For example:

Enter string: "Hello"
Goes through hash algorithm: 5d41402abc4b2a76b9719d911017c592

Enter hash:"5d41402abc4b2a76b9719d911017c592"

Are they equal? (Compare their hash values).

However, hash is a char array, while the hashed value is an md5hash datatype. How can I convert the hash entered through stdin to an md5hash datatype?

Also, I am using Dev C++ - which uses the Mingw port of GCC (GNU Compiler Collection) as it's compiler. The error I receive is

 conversion from `char*' to non-scalar type `md5hash' requested. 

(I'm not surprised that I received an error from the above program).

Any constructive input is appreciated.

EDIT:

This is how I am hashing my strings (it's not the full code, but this is how a string is prepared for hashing):

void md5_prep(char *c0)
{
  uint len = 0;
  char *c = c0; 
  while(*c) {len++; c++;}
  c[0] = 0x80; // bit 1 after the message (Appears counterintuitive, but remember c was incremented in the above loop
  ((uint*)c0)[14] = len * 8;    // message length in bits (Note: 4 * 14 is 56 - appends to end of buffer as stated in Wikipedia's psuedocode)
}

void print_md5(uint *hash, bool crlf)
{
  for(int i = 0; i != 16; i++) { printf("%02x", (uint)(((unsigned char *)hash)[i])); }
  if(crlf) printf("\n");
}

union md5hash
{
  uint ui[4]; //= 16 bytes --> 128 bits (allows the message to be divided into chunks due to union structure)
  char ch[16]; //= 16 bytes --> 128 bits
};


md5hash single_md5(char* ptext)
{
  md5hash h;

  char w[64] = {0}; //Declare an empty array
  //for(int i = 0; ptext[i]; i++) w[i] = ptext[i];
  strncpy(w, ptext, 56); //Copy over the text to be hashed; this effectively "zero pads" the string (limiting to 56 - my choice)
  md5_prep(w);
  MD5_CPU((uint*)&w[0], h.ui[0], h.ui[1], h.ui[2], h.ui[3]); //Hash the message

  return h;
}

//Test
int main(void){
  //Declarations
  std::string target = "Hello";

  char tes[120] = {0};
  tes[0] = 'h';
  tes[1] = 'e';
  for(int i =0; tes[i]; i++)
          printf("%c", tes[i]);
  printf("\n");

  //Execute
  md5hash test = single_md5(tes);
  print_md5(test.ui, false);

  return 0;
}

So, if I want to compare an entered hash to a computed hash from an entered string, I can do this

   a == target[0] && b == target[1] && c == target[2] && d == target[3]

Where a,b,c,d are ui[1], ui[2], ui[3], ui[4].

However, I need to somehow convert the hash string into that form defined by the union.


Solution

  • It seems that your problem is that the user enters a 32-bit textual (hexadecimal) representation of the hash, whereas you need the raw data. So, you will have to convert between the two:

    int hex2int(char ch)
    {
        if (isdigit(ch))
            return ch - '0';
    
        switch (ch) {
        case 'a': case 'A': return 10;
        case 'b': case 'B': return 11;
        case 'c': case 'C': return 12;
        case 'd': case 'D': return 13;
        case 'e': case 'E': return 14;
        case 'f': case 'F': return 15;
        default: abort(); // shan't happen
        }
    }
    
    char md5_hex2raw(char raw[16], const char hex[32])
    {
        for (int i = 0; i < 32; i += 2) {
            raw[i] = (hex2int(hex[0]) << 4) | hex2int(hex[1]);
        }
    }
    

    You can then just call the conversion function so that it copies directly into a union:

    md5hash hash;
    md5_hex2raw(hash.ch, user_entered_string);