3

I am looking for solution how to compute hash for data structure. Let's assume we have a structure like this:

struct A
{
    float64_t array[4][4];
    float64_t x;
    float64_t y;
    uint8_t validationHash[32]; // here computed hash need to be stored
}

I also have function Sha256(cont char * input, uint8_t (&output)[32]) which as an arguments takes input and output - computed hash. I know that i need to convert each value from structure to const char *. But my question is what to do next, shall I compute a separate hash for each value from array, x and y and add them together or what?

Implementation of Sha256 is the same like here http://www.zedwood.com/article/cpp-sha256-function

  • 2
    If you want to compute a hash for a structure, then the hash should *not* be in that structure. – Eugene Sh. Jan 10 '20 at 17:14
  • 1
    You probably cannot do this with your `Sha256`. This function is probably designed for an input as NUL terminated string. You need to tell us more about that function. – Jabberwocky Jan 10 '20 at 17:14
  • 2
    How do you tell the Sha256 function how many bytes are in the input array? – selbie Jan 10 '20 at 17:14
  • Please choose one of the two language tags, either C or C++. They are not the same language and the answers may look very different for each. Also `float64_t` is not a standard type in either language. – walnut Jan 10 '20 at 17:15
  • 1
    what do you want the hash to do? Do you want to protect against accidental or malicious modifications? – Alan Birtles Jan 10 '20 at 17:17
  • If your `Sha256` really does take a nul terminated string and you really want to use it... then you can convert your memory into a hex ascii string with `sprintf` (or really a base ascii string) and then pass to your function. – KamilCuk Jan 10 '20 at 17:18
  • My Sha256 is exactly the same like here http://www.zedwood.com/article/cpp-sha256-function – Mateusz Czerepowicki Jan 10 '20 at 17:19
  • It's almost certain that there's no padding in this structure--you can verify that by adding a compile-time assertion that offsetof(validationHash) == 144. Given that, I'd just feed your 144 bytes directly into the hashing function as a blob, assuming you have a function that works on arbitrary bytes and not just nul-terminated strings. – Lee Daniel Crocker Jan 10 '20 at 17:20
  • I want to compute hash based on data from array, x and y. – Mateusz Czerepowicki Jan 10 '20 at 17:21
  • @AlanBirtles I need to solve problem like this, I am getting data with computed hash and then i need to based on recived data compute hash, then i need to compare both and say there are equal or not. I don't know how hash was computed further. – Mateusz Czerepowicki Jan 10 '20 at 17:36
  • 1
    @MateuszCzerepowicki If you need to verify a received hash but don't know how the received hash was computed, then you are out of luck. There is no universal, inviolable method for computing hash values, nor for combining multiple values in a hashing process. Ask the entity sending you the hash how to compute it. – JaMiT Jan 10 '20 at 19:23

1 Answers1

2

The SHA-256 hash function you linked to, like most cryptographic hash implementations, accepts a byte array as its input. So the very first step is to serialize the data you want to hash.

This isn't as trivial as casting your struct to a byte array. Serialization should be portable between operating systems and hardware. Struct alignment, endianness, etc can vary between system, so it's best to use a serialization library, and leave all those tricky strict aliasing questions to the library authors.

Best Option: A Serialization Library

Since you already appear to be using Boost (the float64_t type), you can use the Boost serialization library. First, create a serialization function to instruct Boost how to serialize A:

namespace boost {
namespace serialization {

template<class Archive>
void serialize(Archive & ar, A & a, const unsigned int version)
{
    ar & a.array;
    ar & a.x;
    ar & a.y;
}

} // namespace serialization
} // namespace boost

Then, serialize it to an in-memory stream:

std::ostringstream plaintext_buffer {};
{
    boost::archive::binary_oarchive oa(plaintext_buffer);
    oa << a;
}
std::string plaintext = plaintext_buffer.str();

Now you can use your SHA-256 hash function. I'll leave that part as an exercise to you.

  • Input: plaintext.data() for data, and plaintext.size() for size
  • Output: a.validationHash

Good Option: custom floating point serializer

According to the comments, you're limited to C++03 (I'll take that as C++98), and can't use any libraries. So first, let's redefine your function using the closest equivalent standard types:

struct A
{
    double array[4][4];
    double x;
    double y;
    uint8_t validationHash[32]; // here computed hash need to be stored
}

I slightly adapted this answer: Serialize double and float with C , which claims to be a portable IEEE 754 serializer. Cool! I changed the output to a memory buffer, replaced goto, and converted C casts to static_cast.

void serializeIeee754(double x, uint8_t* destination)
{
    int                     shift;
    unsigned long           sign, exp, hibits, hilong, lowlong;
    double                  fnorm, significand;
    int                     expbits = 11;
    int                     significandbits = 52;

    if(x == 0) {
        /* zero (can't handle signed zero) */
        hilong = 0;
        lowlong = 0;
    } else if(x > DBL_MAX) {
        /* infinity */
        hilong = 1024 + ((1 << (expbits - 1)) - 1);
        hilong <<= (31 - expbits);
        lowlong = 0;
    } else if(x < -DBL_MAX) {
        /* -infinity */
        hilong = 1024 + ((1 << (expbits - 1)) - 1);
        hilong <<= (31 - expbits);
        hilong |= (1 << 31);
        lowlong = 0;
    } else if(x != x) {
        /* NaN - dodgy because many compilers optimise out this test
        * isnan() is C99, POSIX.1 only, use it if you will.
        */
        hilong = 1024 + ((1 << (expbits - 1)) - 1);
        hilong <<= (31 - expbits);
        lowlong = 1234;
    } else {
        /* get the sign */
        if(x < 0) {
            sign = 1;
            fnorm = -x;
        } else {
            sign = 0;
            fnorm = x;
        }

        /* get the normalized form of f and track the exponent */
        shift = 0;
        while(fnorm >= 2.0) {
            fnorm /= 2.0;
            shift++;
        }
        while(fnorm < 1.0) {
            fnorm *= 2.0;
            shift--;
        }

        /* check for denormalized numbers */
        if(shift < -1022) {
            while(shift < -1022) {
                fnorm /= 2.0;
                shift++;
            }
            shift = -1023;
        } else {
            /* take the significant bit off mantissa */
            fnorm = fnorm - 1.0;
        }
        /* calculate the integer form of the significand */
        /* hold it in a  double for now */

        significand = fnorm * ((1LL << significandbits) + 0.5f);

        /* get the biased exponent */
        exp = shift + ((1 << (expbits - 1)) - 1);   /* shift + bias */

        /* put the data into two longs */
        hibits = static_cast<long>(significand / 4294967296);  /* 0x100000000 */
        hilong = (sign << 31) | (exp << (31 - expbits)) | hibits;
        lowlong = static_cast<unsigned long>(significand - hibits * 4294967296);
    }

    destination[0] = lowlong & 0xFF;
    destination[1] = (lowlong >> 8) & 0xFF;
    destination[2] = (lowlong >> 16) & 0xFF;
    destination[3] = (lowlong >> 24) & 0xFF;
    destination[4] = hilong & 0xFF;
    destination[5] = (hilong >> 8) & 0xFF;
    destination[6] = (hilong >> 16) & 0xFF;
    destination[7] = (hilong >> 24) & 0xFF;
}

Now, you can write your own serializer for A that writes to a 144 byte buffer:

void serializeA(A& a, uint8_t destination[144]) {
    uint8_t* out = destination;
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            serializeIeee754(a.array[i][j], out);
            out += 8;
        }
    }
    serializeIeee754(a.x, out);
    out += 8;
    serializeIeee754(a.y, out);
}

Then supply that buffer to your hash function.

parktomatomi
  • 3,851
  • 1
  • 14
  • 18