The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
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 */
31RCSID("$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
41static _Thread_local EVP_MD_CTX *md5_hmac_ctx;
42
43static 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 */
60int 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 */
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:483
#define unlikely(_x)
Definition build.h:381
static fr_slen_t in
Definition dict.h:824
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:442
fr_md5_final_t fr_md5_final
Definition md5.c:443
fr_md5_ctx_reset_t fr_md5_ctx_reset
Definition md5.c:438
void fr_md5_ctx_free_from_list(fr_md5_ctx_t **ctx)
Definition md5.c:522
fr_md5_ctx_t * fr_md5_ctx_alloc_from_list(void)
Definition md5.c:477
void fr_md5_ctx_t
Definition md5.h:28
#define MD5_DIGEST_LENGTH
unsigned char uint8_t
#define fr_strerror_const(_msg)
Definition strerror.h:223
static size_t char fr_sbuff_t size_t inlen
Definition value.h:997