/src/freeradius-server/src/lib/util/hmac_md5.c
Line | Count | Source |
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 | 1 | { |
45 | 1 | EVP_MD_CTX_free(arg); |
46 | 1 | return 0; |
47 | 1 | } |
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 | 9 | { |
63 | 9 | EVP_MD_CTX *ctx; |
64 | 9 | EVP_PKEY *pkey; |
65 | | |
66 | 9 | if (unlikely(!md5_hmac_ctx)) { |
67 | 1 | ctx = EVP_MD_CTX_new(); |
68 | 1 | if (unlikely(!ctx)) { |
69 | 0 | fr_strerror_const("Failed allocating EVP_MD_CTX for HMAC-MD5"); |
70 | 0 | return -1; |
71 | 0 | } |
72 | 1 | EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_ONESHOT); |
73 | 1 | fr_atexit_thread_local(md5_hmac_ctx, _hmac_md5_ctx_free_on_exit, ctx); |
74 | 8 | } else { |
75 | 8 | ctx = md5_hmac_ctx; |
76 | 8 | } |
77 | | |
78 | 9 | pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key, key_len); |
79 | 9 | if (unlikely(pkey == NULL)) { |
80 | 0 | fr_strerror_const("Failed allocating pkey for HMAC-MD5"); |
81 | 0 | return -1; |
82 | 0 | } |
83 | | |
84 | 9 | if (unlikely(EVP_DigestSignInit(ctx, NULL, EVP_md5(), NULL, pkey) != 1)) { |
85 | 0 | fr_strerror_const("Failed initialising EVP_MD_CTX for HMAC-MD5"); |
86 | 0 | error: |
87 | 0 | EVP_PKEY_free(pkey); |
88 | 0 | return -1; |
89 | 0 | } |
90 | 9 | if (unlikely(EVP_DigestSignUpdate(ctx, in, inlen) != 1)) { |
91 | 0 | fr_strerror_const("Failed ingesting data for HMAC-MD5"); |
92 | 0 | goto error; |
93 | 0 | } |
94 | | /* |
95 | | * OpenSSL <= 1.1.1 requires a non-null pointer for len |
96 | | */ |
97 | 9 | if (unlikely(EVP_DigestSignFinal(ctx, digest, &(size_t){ MD5_DIGEST_LENGTH }) != 1)) { |
98 | 0 | fr_strerror_const("Failed finalising HMAC-MD5"); |
99 | 0 | goto error; |
100 | 0 | } |
101 | | |
102 | 9 | EVP_PKEY_free(pkey); |
103 | 9 | EVP_MD_CTX_reset(ctx); |
104 | | |
105 | 9 | return 0; |
106 | 9 | } |
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 | | |
128 | | ctx = fr_md5_ctx_alloc_from_list(); |
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 | | |
179 | | fr_md5_ctx_free_from_list(&ctx); |
180 | | |
181 | | return 0; |
182 | | } |
183 | | #endif /* HAVE_OPENSSL_EVP_H */ |