Dear lazy web,
After reading up on OpenID recently, I thought it would be a nice experiment to see what it would take to write a CGI script in C that takes care of OpenID on my website. I haven't managed to write a complete set of CGI scripts for OpenID yet, and I'm not sure I will do that (given the feature creep in the recent spec proposals). But I did write this tiny driver program to get a hang of HMAC SHA-1 digests and the libgcrypt library. Although libgcrypt does all the hard work for me, I thought it might be useful to copy paste this snippet to the net for future reference.
The program takes the data from standard input and, combined with a key that was given as argument, calculates the HMAC-SHA1 digest for the data. HMAC is a message authentication mechanism where a unique hash is calculated on a piece of data. The key that is used in generating the hash is normally shared between the two parties exchanging the data. See for more info HMAC: Keyed-Hashing for Message Authentication, Test Cases for HMAC-MD5 and HMAC-SHA-1 and a discussion of HMAC on Wikipedia.
To prove that the program works, here is one of the test vectors from RFC 2202:
$ echo -n "what do ya want for nothing?" | ./sha1-hmac-digest Jefe 0xeffcdf6ae5eb2fa2d27416d5f184df9c259a7c79
/* calculate a SHA-1 HMAC digest * FIXME common encoding for the digest... (base64?) */ #include <stdlib.h> #include <gcrypt.h> #include <stdio.h> #include <glib-2.0/glib.h> /* write md value into message string: */ static void print_hash(const unsigned char *md_string) { int len = gcry_md_get_algo_dlen(GCRY_MD_SHA1); char * mes = g_malloc(2 + (2 * len) + 1); char * mes_i = 0; int i = 0; mes[0] = '0'; mes[1] = 'x'; mes_i = mes + 2; for (i = 0; i < len; i++) { g_snprintf(mes_i, 3, "%x\n", md_string[i]); mes_i += 2;/* XXX assert pointer width equals char width */ } mes[2 + (2 * len)] = 0; g_print(mes); g_print("\n"); g_free(mes); } /* Read everything from standard input and calculate a SHA-1 HMAC from it. */ static void calc_hmac(FILE *instream, const char *key) { size_t num; gcry_error_t err = 0; gcry_md_hd_t digest = NULL; unsigned char *md_string = NULL; static const size_t bufsize = 4096; void *buffer = g_malloc(bufsize); err = gcry_md_open( &digest, GCRY_MD_SHA1, GCRY_MD_FLAG_SECURE | GCRY_MD_FLAG_HMAC); if (err) { g_printerr("%s\n", gcry_strerror(err)); goto done; } err = gcry_md_setkey(digest, key, strlen(key)); if (err) { g_printerr("%s\n", gcry_strerror(err)); goto done; } while ((num = fread(buffer, 1, bufsize, instream)) != 0) { gcry_md_write(digest, buffer, num); } md_string = gcry_md_read(digest, 0); g_assert(md_string != NULL); print_hash(md_string); done: gcry_md_close(digest); g_free(buffer); } int main(int argc, const char **argv) { if (argc > 1) { gcry_control (GCRYCTL_DISABLE_SECMEM, 0); calc_hmac(stdin, argv[1]); } exit(0); return 0; }
For overcompleteness, here are the commands to compile this script (with gcrypt.h safely living in /usr/include and libgrypt.so in /usr/lib):
cc -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -Wall -g -c sha1-hmac-digest.c cc -lglib-2.0 -lgcrypt -o sha1-hmac-digest sha1-hmac-digest.o
Obviously, there is no need to use glib. That's just
the library I use for printing to screen. You might as well have
used ordinary printf and friends from stdio.h.