Why is getting the HP_HASHSIZE so weird?


A comment on the documentation page for Crypt­Get­Hash­Param notes that the "obvious" way to get the HP_HASH­SIZE is incorrect.

// Version 1: wrong.
DWORD size = 0;
if (CryptGetHashParam(hash, HP_HASHSIZE, nullptr, &size, 0)) ...

// Version 2: right.
DWORD size;
DWORD bufferSize = sizeof(size);
if (CryptGetHashParam(hash, HP_HASHSIZE, &size, &bufferSize, 0)) ...

What's going on here? I mean, the documentation says that if you want to get the size of a parameter, you pass nullptr for the buffer, and the DWORD* parameter gets the size of the buffer. So if I want to get the hash size, I should pass nullptr for the buffer, and the DWORD* parameter gets the size of the hash. But it doesn't. It always returns 4. What's going on?

What's going on is that you are working at the wrong level of indirection. The code in version 1 is not asking for the size of the hash. It's asking for the size of the HP_HASH­SIZE. In other words, you're asking for the size of the size. Since HP_HASH­SIZE is a DWORD, its size is 4. You then need to follow up with the code in version 2, which allocates a buffer of size 4 and asks for it to be filled in with the HP_HASH­SIZE.

A third way to get the size of the hash is to ignore HP_HASH­SIZE completely and go straight for the HP_HASH­VAL:

// Version 3: righter
DWORD hashSize = 0;
if (CryptGetHashParam(hash, HP_HASHVAL, nullptr, &hashSize, 0)) ...

I don't know why the crypto folks bothered to have a HP_HASH­SIZE parameter. Adding it only created confusion.

Comments (11)
  1. skSdnW says:

    Obviously the crypto designers believe that one day we will have hashes larger than 4 GiB.

  2. kantos says:

    Aren't we supposed to be using Cryptography Next Generation (https://msdn.microsoft.com/en-us/library/windows/desktop/aa376217(v=vs.85).aspx) these days anyway?

  3. Karellen says:

    OK, I understand the need for a function like CryptGetHashParam(), because some hash functions might have hash-specific parameters of arbitrary length which you might want to query. But "size" is one attributes all hash functions have. So why not have:
    DWORD CryptGetHashSize(HCRYPTHASH hHash, DWORD flags);

    As it is, everyone's just going to reinvent their own version of this function, probably inline a bunch of times, and possibly incorrectly (like version 1).

    If the designers of I/O APIs went to the same school of API design as the crypto lot, rather than bother with the multitude of fread(), fwrite(), iseof(), etc... calls, they'd probably end up with a single PerformIOOperation(HANDLE h, DWORD operation, BYTE * pbData, DWORD * pwdDataLen, DWORD flags), where "operation" describes what you want to do, and is *so* much more flexible!

    1. Ben Voigt says:

      The "PerformIOOperation" you are thinking of is named DeviceIoControl, and it is indeed more flexible.

      1. Karellen says:

        But how many people do you think actually use it for the basic job of reading and writing bytes to the device(-handle), instead of the more specific ReadFile() and WriteFile() calls?

    2. There's no need for HP_HASHSIZE at all. You get the hash size by asking for the size of HP_HASHVAL. My guess is that this function is wrapping an internal class, and that internal class has a HashSize property, sand somebody went through and blindly put every property of the internal class into the public surface without realizing that getting the HashSize is redundant with getting the size of HP_HASHVAL.

      1. cheong00 says:

        More forum error:
        Line: 377: Error: 'addComment' is undefined
        ======
        For the reason why HP_HASHVAL is not used, The description of HP_HASHVAL says:
        [quote]
        The hash value or message hash for the hash object specified by hHash. This value is generated based on the data supplied to the hash object earlier through the CryptHashData and CryptHashSessionKey functions.
        [/quote]
        The line suggest that I have to call those function like CryptHashData() before calling it, and by the time I already run it, it would be too late to allocate the buffer for it?

        Maybe another need for clarification on documentation?

        1. smf says:

          "The line suggest that I have to call those function like CryptHashData() before calling it, and by the time I already run it, it would be too late to allocate the buffer for it?"

          @cheong00 You have to call those functions before asking the value, but you don't have to call them before asking what size the value will be. The only argument for using HP_HASHSIZE would be if it was the maximum size and the size of HP_HASHVAL was dynamic.

          1. smf says:

            I thought of another reason, it might be quicker to get the value of HP_HASHSIZE than it is to ask for the size of HP_HASHVAL. Or someone at least thought that one day it might be quicker. It appears you aren't supposed to ask until the last minute anyway...

            "Applications must retrieve this value just before the HP_HASHVAL value so the correct amount of memory can be allocated."

  4. Joshua says:

    Personally I think Version 3 is the only right version. Imagine what would happen if you used Version 2, but Version 1 would have returned 1. Of course you could make it work on little-endian by setting size to zero first but ...

    1. smf says:

      I can't imagine how getting HP_HASHSIZE could return 1 as it is documented as a DWORD.

      "HP_HASHSIZE
      Hash value size

      DWORD value indicating the number of bytes in the hash value. This value will vary depending on the hash algorithm. Applications must retrieve this value just before the HP_HASHVAL value so the correct amount of memory can be allocated."

Comments are closed.

Skip to main content