Data Pile

Against Digital Amnesia

Programming With the APR - Using MD5

This article intends to show how to basically use the MD5 hashing algorithm that ships with the Apache Portable Runtime (APR) library.

Introduction

Sometimes you are really in need for some MD5 hashed strings. Most of the time you are in such a need if you want to create a custom password store where you don’t want an attacker to easily get the password if he compromises your system. So MD5 hashed password are great for such a purpose. Luckily the Apache Portable Runtime (APR) provides such a MD5 hashing mechanism to you, so you can easily use that and do not have to struggle with the complex details of how to actually create a MD5 hash.

Hashing

Hashing basically desribes a way of taking an arbitrary length input and calculating that down into a secure, tamperproof cryptographic value that can only be recalculated if the same input was given again. The basic idea is that the calculation that is necessary to produce the hashed output is very easy to perform, but if you just have the hashed version of the password you cannot easily reverse that operation (the reverse operation is very complex).

Salting

If you are creating a password store on your own, you probably don’t want a simple MD5 hashing mechanism, which the APR provides to you. You probably want to add some salt to your password management mechanism, since salting the password makes bruteforce attacks with rainbow tables onto the hashed password repository so much more inconvenient for the attacker.

Salting, for that who don’t know, simply sort of “enriches” your password with some random bytes, that will be thrown into the mix when hashing the actual password. Since a hash algorithm is only a good one if it flips about 50 percent of the bits in the output if you change a single input bit, you can imagine that a few added bits should absolutely be sufficient to change the result of the hash operation.

In order for the algorithm to be able to reconstruct the same hash again, he of course needs to know the salt that was initially used to create the corresponding hash. Therefore this salt value will be written in front of the final hashed password string.

A typical MD5 hash, or sometimes called an MD5 digest will look like:

1
$apr1$XGOEPMa5$eUAF1NzTmHoqGZJSD5P4q1

The c0de

The code itself is pretty straight forward. You might notice that I’m not using any APR data pool within the program itself. This is simply due to the fact that I’m not needing any ;).

The program first initializes internal APR data structures and registers a termination function. Then a randomized salt value is generated (you could also use a fixed salt, but that is not recommended). Finally the MD5 encoding is performed. The most problematic part here is to figure out what the actual length of the result will be. From what I observed the result is no longer than 37 characters, but that might change if the APR people e.g. decide to change their “$apr1$” prefix which indicates the custom salting algorithm to the library.

Simple MD5 Hashing Example With The APR (aprMd5.c) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include <apr.h>
#include <apr_base64.h>
#include <apr_general.h>
#include <apr_md5.h>
#define APR_WANT_STDIO
#include <apr_want.h>

/* This is neccessary since there seems to be
 * no real information to gather how long the actual
 * result string will be when the encoder finishes it's
 * work.
 *
 * Using another contellation, this should not be a problem,
 * since we would of course allocate the final result memory
 * from an APR pool. Where memory is managed, and the final length
 * of the result is known. 
 *
 * During several trial and error phases I observed the result string
 * to be no longer than 37 characters. But that might change if the 
 * e.g. change the apr id, where the "custom algorithm" is detected.
 */
#define MAX_STRING_LEN 256
#define SALT_LEN 9

void randomizeSalt(char *resultSalt)
{
  apr_status_t rv;
  char salt[SALT_LEN];
  char b64Salt[apr_base64_encode_len(SALT_LEN)];

  apr_base64_encode(b64Salt, salt, SALT_LEN);
  printf("Salt array before randomization:\t%s\n", b64Salt);
  rv = apr_generate_random_bytes(salt, SALT_LEN - 1);
  salt[SALT_LEN -1] = '\0';

  apr_base64_encode(b64Salt, salt, SALT_LEN);
  printf("Salt array  after randomization:\t%s\n", b64Salt);
  /* As we do just need 8 random characters, we just 
   * copy them (remember the 9th char is set to '\0' earlier
   */
  apr_cpystrn(resultSalt, b64Salt, SALT_LEN);
  return;
}

/* Compile with 
 * $> export APR_LIBS="`apr-1-config --cflags --cppflags --includes --ldflags --link-ld --libs`"
 * $> export APU_LIBS="`apu-1-config --includes --ldflags --link-ld --libs`"
 * $> gcc aprMd5.c -o aprMd5 $APR_LIBS $APU_LIBS
 */
int main(int argc, const char * const *argv)
{

  unsigned char md5Digest[APR_MD5_DIGESTSIZE];
  const char passwordToHash[] = "This is my very secure extrme 1337 pa55w0r)";

  /* This gives us the salt, so the same password
   * will not produce the same hash, given the assumption,
   * that the salt of course changes. 
   * Using this static string is, of course not any safer 
   * then just using the standard MD5 hashing.
   *
   * This MD5 version uses an APR specific salting.
   */
  //char salt[] = "12345678";
  char salt[9];
  apr_status_t rv;

  /* Actually use the app_initialize funciton if you are
   * not writing a library.
   *
   * This function is defined in apr_general.h
   */
  apr_app_initialize(&argc, &argv, NULL);
  atexit(apr_terminate);

  randomizeSalt(salt);

  unsigned char md5DigestSalted[MAX_STRING_LEN];

  /* This actually hashes the password into a final 
   * already in ASCII printable characters encoded 
   * string
   */
  apr_md5_encode(passwordToHash,
		 salt,
		 md5DigestSalted,
		 sizeof(md5DigestSalted));

  printf("The MD5 digest salted of:\n%s\nlooks like:\n%s\n"
	 "and is %d characters long\n",
	 passwordToHash,
	 md5DigestSalted,
	 strlen(md5DigestSalted));

  /* Now see if we can validate our "normal" password against the
   * hashed version.
   */
  rv = apr_password_validate(passwordToHash, md5DigestSalted);
  printf("Testing password: %s\n", passwordToHash);
  if (rv == APR_SUCCESS) {
    printf("[SUCCESS] The supplied password validated correctly against the hash\n");
  } else {
    printf("[FAILURE] The supplied password did not validate\n");
  }

  /* Offset the original password by one character, so this should not
   * be valid anymore.
   */
  rv = apr_password_validate(&passwordToHash[1], md5DigestSalted);
  printf("Testing password: %s\n", &passwordToHash[1]);
  if (rv == APR_SUCCESS) {
    printf("[SUCCESS] The supplied password validated correctly against the hash\n");
  } else {
    printf("[FAILURE] The supplied password did not validate\n");
  }

  apr_terminate();
  return 0;
}