The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
hmac_md5.c
Go to the documentation of this file.
1 /*
2  * This library is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU Lesser General Public
4  * License as published by the Free Software Foundation; either
5  * version 2.1 of the License, or (at your option) any later version.
6  *
7  * This library is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10  * Lesser General Public License for more details.
11  *
12  * You should have received a copy of the GNU Lesser General Public
13  * License along with this library; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /** MD5 HMAC not dependent on OpenSSL
18  *
19  * @file src/lib/util/hmac_md5.c
20  *
21  * @note New code that needs fast or incremental HMACs should use the OpenSSL EVP_* HMAC
22  * interface instead, as that can take advantage of acceleration instructions provided
23  * by various CPUs (and provides an incremental hashing interface).
24  *
25  * For the sake of illustration we provide the following sample code for the implementation
26  * of HMAC-MD5 as well as some corresponding test vectors (the code is based on MD5 code as
27  * described in [MD5]).
28  *
29  * @copyright 2000,2006 The FreeRADIUS server project
30  */
31 RCSID("$Id: 3432d82e70ccc491bbd10bf6cd63363adb638d4b $")
32 
33 #include <freeradius-devel/util/atexit.h>
34 #include <freeradius-devel/util/md5.h>
35 #include <freeradius-devel/util/strerror.h>
36 
37 #ifdef HAVE_OPENSSL_EVP_H
38 # include <freeradius-devel/tls/openssl_user_macros.h>
39 # include <openssl/hmac.h>
40 
41 static _Thread_local EVP_MD_CTX *md5_hmac_ctx;
42 
43 static int _hmac_md5_ctx_free_on_exit(void *arg)
44 {
45  EVP_MD_CTX_free(arg);
46  return 0;
47 }
48 
49 /** Calculate HMAC using OpenSSL's MD5 implementation
50  *
51  * @param digest Caller digest to be filled in.
52  * @param in Pointer to data stream.
53  * @param inlen length of data stream.
54  * @param key Pointer to authentication key.
55  * @param key_len Length of authentication key.
56  * @return
57  * - 0 on success.
58  * - -1 on error.
59  */
60 int fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen,
61  uint8_t const *key, size_t key_len)
62 {
63  EVP_MD_CTX *ctx;
64  EVP_PKEY *pkey;
65 
66  if (unlikely(!md5_hmac_ctx)) {
67  ctx = EVP_MD_CTX_new();
68  if (unlikely(!ctx)) {
69  fr_strerror_const("Failed allocating EVP_MD_CTX for HMAC-MD5");
70  return -1;
71  }
72  EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_ONESHOT);
73  fr_atexit_thread_local(md5_hmac_ctx, _hmac_md5_ctx_free_on_exit, ctx);
74  } else {
75  ctx = md5_hmac_ctx;
76  }
77 
78  pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key, key_len);
79  if (unlikely(pkey == NULL)) {
80  fr_strerror_const("Failed allocating pkey for HMAC-MD5");
81  return -1;
82  }
83 
84  if (unlikely(EVP_DigestSignInit(ctx, NULL, EVP_md5(), NULL, pkey) != 1)) {
85  fr_strerror_const("Failed initialising EVP_MD_CTX for HMAC-MD5");
86  error:
87  EVP_PKEY_free(pkey);
88  return -1;
89  }
90  if (unlikely(EVP_DigestSignUpdate(ctx, in, inlen) != 1)) {
91  fr_strerror_const("Failed ingesting data for HMAC-MD5");
92  goto error;
93  }
94  /*
95  * OpenSSL <= 1.1.1 requires a non-null pointer for len
96  */
97  if (unlikely(EVP_DigestSignFinal(ctx, digest, &(size_t){ MD5_DIGEST_LENGTH }) != 1)) {
98  fr_strerror_const("Failed finalising HMAC-MD5");
99  goto error;
100  }
101 
102  EVP_PKEY_free(pkey);
103  EVP_MD_CTX_reset(ctx);
104 
105  return 0;
106 }
107 #else
108 /** Calculate HMAC using internal MD5 implementation
109  *
110  * @param digest Caller digest to be filled in.
111  * @param in Pointer to data stream.
112  * @param inlen length of data stream.
113  * @param key Pointer to authentication key.
114  * @param key_len Length of authentication key.
115  * @return
116  * - 0 on success.
117  * - -1 on error.
118  */
119 int fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen,
120  uint8_t const *key, size_t key_len)
121 {
122  fr_md5_ctx_t *ctx;
123  uint8_t k_ipad[65]; /* inner padding - key XORd with ipad */
124  uint8_t k_opad[65]; /* outer padding - key XORd with opad */
125  uint8_t tk[16];
126  int i;
127 
129 
130  /* if key is longer than 64 bytes reset it to key=MD5(key) */
131  if (key_len > 64) {
132  fr_md5_update(ctx, key, key_len);
133  fr_md5_final(tk, ctx);
134  fr_md5_ctx_reset(ctx);
135 
136  key = tk;
137  key_len = 16;
138  }
139 
140  /*
141  * the HMAC_MD5 transform looks like:
142  *
143  * MD5(K XOR opad, MD5(K XOR ipad, in))
144  *
145  * where K is an n byte key
146  * ipad is the byte 0x36 repeated 64 times
147 
148  * opad is the byte 0x5c repeated 64 times
149  * and in is the data being protected
150  */
151 
152  /* start out by storing key in pads */
153  memset(k_ipad, 0, sizeof(k_ipad));
154  memset(k_opad, 0, sizeof(k_opad));
155  memcpy(k_ipad, key, key_len);
156  memcpy(k_opad, key, key_len);
157 
158  /* XOR key with ipad and opad values */
159  for (i = 0; i < 64; i++) {
160  k_ipad[i] ^= 0x36;
161  k_opad[i] ^= 0x5c;
162  }
163  /*
164  * perform inner MD5
165  */
166  fr_md5_update(ctx, k_ipad, 64); /* start with inner pad */
167  fr_md5_update(ctx, in, inlen); /* then in of datagram */
168  fr_md5_final(digest, ctx); /* finish up 1st pass */
169 
170 
171  /*
172  * perform outer MD5
173  */
174  fr_md5_ctx_reset(ctx);
175  fr_md5_update(ctx, k_opad, 64); /* start with outer pad */
176  fr_md5_update(ctx, digest, 16); /* then results of 1st hash */
177  fr_md5_final(digest, ctx); /* finish up 2nd pass */
178 
180 
181  return 0;
182 }
183 #endif /* HAVE_OPENSSL_EVP_H */
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition: atexit.h:221
#define RCSID(id)
Definition: build.h:444
#define unlikely(_x)
Definition: build.h:378
static fr_slen_t in
Definition: dict.h:645
int fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen, uint8_t const *key, size_t key_len)
Calculate HMAC using internal MD5 implementation.
Definition: hmac_md5.c:119
fr_md5_update_t fr_md5_update
Definition: md5.c:450
fr_md5_final_t fr_md5_final
Definition: md5.c:451
fr_md5_ctx_reset_t fr_md5_ctx_reset
Definition: md5.c:446
void fr_md5_ctx_free_from_list(fr_md5_ctx_t **ctx)
Definition: md5.c:530
fr_md5_ctx_t * fr_md5_ctx_alloc_from_list(void)
Definition: md5.c:485
void fr_md5_ctx_t
Definition: md5.h:28
#define MD5_DIGEST_LENGTH
Definition: merged_model.c:248
unsigned char uint8_t
Definition: merged_model.c:30
#define fr_strerror_const(_msg)
Definition: strerror.h:223
static size_t char fr_sbuff_t size_t inlen
Definition: value.h:984