6.6 Hashing a Single String

6.6.1 Problem

You have a single string of data that you would like to hash, and you don't like the complexity of the incremental interface.

6.6.2 Solution

Use an "all-in-one" interface, if available, or write your own wrapper, as shown in Section 6.6.3.

6.6.3 Discussion

Hash functions are not secure by themselves?not for a password system, not for message authentication, not for anything! If you do need a hash function by itself, be sure to at least protect against length extension attacks, as described in Recipe 6.7.

Complexity can certainly get you in trouble, and a simpler API can be better. While not every API provides a single function that can perform a cryptographic hash, many of them do. For example, OpenSSL provides an all-in-one API for each of the message digest algorithms it supports:

unsigned char *MD2(unsigned char *in, unsigned long n, unsigned char *md);
unsigned char *MD4(unsigned char *in, unsigned long n, unsigned char *md);
unsigned char *MD5(const unsigned char *in, unsigned long n, unsigned char *md);
unsigned char *MDC2(const unsigned char *in, unsigned long n, unsigned char *md);
unsigned char *RIPEMD160(const unsigned char *in, unsigned long n,
                         unsigned char *md);
unsigned char *SHA1(const unsigned char *in, unsigned long n, unsigned char *md);

APIs in this style are commonly seen, even outside the context of OpenSSL. Note that these functions require you to pass in a buffer into which the digest is placed, but they also return a pointer to that same buffer.

OpenSSL does not provide an all-in-one API for calculating message digests with the EVP interface. However, here's a simple wrapper that even allocates its result with malloc( ):

#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>
   
/* Returns 0 when malloc() fails. */
unsigned char *spc_digest_message(EVP_MD *type, unsigned char *in,
                                  unsigned long n, unsigned int *outlen) {
  EVP_MD_CTX    ctx;
  unsigned char *ret;
   
  EVP_DigestInit(&ctx, type);
  EVP_DigestUpdate(&ctx, in, n);
  if (!(ret = (unsigned char *)malloc(EVP_MD_CTX_size(&ctx))) return 0;
  EVP_DigestFinal(&ctx, ret, outlen);
  return ret;
}

Here's a simple example that uses the previous wrapper:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/evp.h>

int main(int argc, char *argv[  ]) {
  int           i;
  unsigned int  ol;
  unsigned char *s = "Testing...1...2...3...";
  unsigned char *r;
   
  r = spc_digest_message(EVP_sha1(  ), s, strlen(s), &ol);
   
  printf("SHA1(\"%s\") = ", s);
  for (i = 0;  i < ol;  i++) printf("%02x", r[i]);
  printf("\n");
   
  free(r);
  return 0;
}

Such a wrapper can be adapted easily to any incremental hashing API, simply by changing the names of the functions and the underlying data type, and removing the first argument of the wrapper if it is not necessary. Here is the same wrapper implemented using Microsoft's CryptoAPI:

#include <windows.h>
#include <wincrypt.h>
   
BYTE *SpcDigestMessage(ALG_ID Algid, BYTE *pbIn, DWORD cbIn, DWORD *cbOut) {
  BYTE       *pbOut;
  DWORD      cbData = sizeof(DWORD);
  HCRYPTHASH hHash;
  HCRYPTPROV hProvider;
   
  CryptAcquireContext(&hProvider, 0, MS_DEF_PROV, PROV_RSA_FULL, 0);
  CryptCreateHash(hProvider, Algid, 0, 0, &hHash);
  CryptHashData(hHash, pbIn, cbIn, 0);
  CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)cbOut, &cbData, 0);
  pbOut = (BYTE *)LocalAlloc(LMEM_FIXED, *cbOut);
  CryptGetHashParam(hHash, HP_HASHVAL, pbOut, cbOut, 0);
  CryptDestroyHash(hHash);
  CryptReleaseContext(hProvider, 0);
  return pbOut;
}

6.6.4 See Also

Recipe 6.7