The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
hmac_sha1.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 /** SHA1 HMAC not dependent on OpenSSL
18  *
19  * @note New code that needs fast or incremental HMACs should use the OpenSSL EVP_* HMAC
20  * interface instead, as that can take advantage of acceleration instructions provided
21  * by various CPUs (and provides an incremental hashing interface).
22  *
23  * Adapted from hmacmd5.c (HMAC-MD5). Test cases from RFC2202.
24  *
25  * @file src/lib/util/hmac_sha1.c
26  *
27  * @author Michael Richardson (mcr@sandelman.ottawa.on.ca)
28  *
29  * @copyright 2003 Michael Richardson (mcr@sandelman.ottawa.on.ca)
30  * @copyright 2000,2003,2006 The FreeRADIUS server project
31  */
32 RCSID("$Id: 4a3248a5337b73485d00af6dd21370e3dd0fe5f7 $")
33 
34 #include <freeradius-devel/util/sha1.h>
35 #include <freeradius-devel/util/atexit.h>
36 #include <freeradius-devel/util/strerror.h>
37 
38 #ifdef HMAC_SHA1_DATA_PROBLEMS
39 unsigned int sha1_data_problems = 0;
40 #endif
41 
42 #ifdef HAVE_OPENSSL_EVP_H
43 # include <freeradius-devel/tls/openssl_user_macros.h>
44 # include <openssl/hmac.h>
45 
46 static _Thread_local EVP_MD_CTX *sha1_hmac_ctx;
47 
48 static int _hmac_sha1_ctx_free_on_exit(void *arg)
49 {
50  EVP_MD_CTX_free(arg);
51  return 0;
52 }
53 
54 /** Calculate HMAC using OpenSSL's SHA1 implementation
55  *
56  * @param digest Caller digest to be filled in.
57  * @param in Pointer to data stream.
58  * @param inlen length of data stream.
59  * @param key Pointer to authentication key.
60  * @param key_len Length of authentication key.
61  * @return
62  * - 0 on success.
63  * - -1 on error.
64  */
65 int fr_hmac_sha1(uint8_t digest[SHA1_DIGEST_LENGTH], uint8_t const *in, size_t inlen,
66  uint8_t const *key, size_t key_len)
67 {
68  EVP_MD_CTX *ctx;
69  EVP_PKEY *pkey;
70 
71  if (unlikely(!sha1_hmac_ctx)) {
72  ctx = EVP_MD_CTX_new();
73  if (unlikely(!ctx)) {
74  fr_strerror_const("Failed allocating EVP_MD_CTX for HMAC-SHA1");
75  return -1;
76  }
77  EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_ONESHOT);
78  fr_atexit_thread_local(sha1_hmac_ctx, _hmac_sha1_ctx_free_on_exit, ctx);
79  } else {
80  ctx = sha1_hmac_ctx;
81  }
82 
83  pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key, key_len);
84  if (unlikely(pkey == NULL)) {
85  fr_strerror_const("Failed allocating pkey for HMAC-SHA1");
86  return -1;
87  }
88 
89  if (unlikely(EVP_DigestSignInit(ctx, NULL, EVP_sha1(), NULL, pkey) != 1)) {
90  fr_strerror_const("Failed initialising EVP_MD_CTX for HMAC-SHA1");
91  error:
92  EVP_PKEY_free(pkey);
93  return -1;
94  }
95  if (unlikely(EVP_DigestSignUpdate(ctx, in, inlen) != 1)) {
96  fr_strerror_const("Failed ingesting data for HMAC-SHA1");
97  goto error;
98  }
99  /*
100  * OpenSSL <= 1.1.1 requires a non-null pointer for len
101  */
102  if (unlikely(EVP_DigestSignFinal(ctx, digest, &(size_t){ SHA1_DIGEST_LENGTH }) != 1)) {
103  fr_strerror_const("Failed finalising HMAC-SHA1");
104  goto error;
105  }
106 
107  EVP_PKEY_free(pkey);
108  EVP_MD_CTX_reset(ctx);
109 
110  return 0;
111 }
112 #else
113 /** Calculate HMAC using internal SHA1 implementation
114  *
115  * @param digest Caller digest to be filled in.
116  * @param in Pointer to data stream.
117  * @param inlen length of data stream.
118  * @param key Pointer to authentication key.
119  * @param key_len Length of authentication key.
120  * @return
121  * - 0 on success.
122  * - -1 on error.
123  */
124 int fr_hmac_sha1(uint8_t digest[static SHA1_DIGEST_LENGTH], uint8_t const *in, size_t inlen,
125  uint8_t const *key, size_t key_len)
126 {
127  fr_sha1_ctx ctx;
128  uint8_t k_ipad[65]; /* inner padding - key XORd with ipad */
129  uint8_t k_opad[65]; /* outer padding - key XORd with opad */
130  uint8_t tk[20];
131  int i;
132  /* if key is longer than 64 bytes reset it to key=SHA1(key) */
133  if (key_len > 64) {
134 
135  fr_sha1_ctx tctx;
136 
137  fr_sha1_init(&tctx);
138  fr_sha1_update(&tctx, key, key_len);
139  fr_sha1_final(tk, &tctx);
140 
141  key = tk;
142  key_len = 20;
143  }
144 
145 #ifdef HMAC_SHA1_DATA_PROBLEMS
146  if(sha1_data_problems)
147  {
148  int j,k;
149 
150  printf("\nhmac-sha1 key(%d): ", key_len);
151  j=0; k=0;
152  for (i = 0; i < key_len; i++) {
153  if(j==4) {
154  printf("_");
155  j=0;
156  }
157  j++;
158 
159  printf("%02x", key[i]);
160  }
161  printf("\nDATA: (%d) ",inlen);
162 
163  j=0; k=0;
164  for (i = 0; i < inlen; i++) {
165  if(k==20) {
166  printf("\n ");
167  k=0;
168  j=0;
169  }
170  if(j==4) {
171  printf("_");
172  j=0;
173  }
174  k++;
175  j++;
176 
177  printf("%02x", in[i]);
178  }
179  printf("\n");
180  }
181 #endif
182 
183 
184  /*
185  * the HMAC_SHA1 transform looks like:
186  *
187  * SHA1(K XOR opad, SHA1(K XOR ipad, in))
188  *
189  * where K is an n byte key
190  * ipad is the byte 0x36 repeated 64 times
191 
192  * opad is the byte 0x5c repeated 64 times
193  * and in is the data being protected
194  */
195 
196  /* start out by storing key in pads */
197  memset(k_ipad, 0, sizeof(k_ipad));
198  memset(k_opad, 0, sizeof(k_opad));
199  memcpy(k_ipad, key, key_len);
200  memcpy(k_opad, key, key_len);
201 
202  /* XOR key with ipad and opad values */
203  for (i = 0; i < 64; i++) {
204  k_ipad[i] ^= 0x36;
205  k_opad[i] ^= 0x5c;
206  }
207  /*
208  * perform inner SHA1
209  */
210  fr_sha1_init(&ctx); /* init ctx for 1st pass */
211  fr_sha1_update(&ctx, k_ipad, 64); /* start with inner pad */
212  fr_sha1_update(&ctx, in, inlen); /* then in of datagram */
213  fr_sha1_final(digest, &ctx); /* finish up 1st pass */
214  /*
215  * perform outer SHA1
216  */
217  fr_sha1_init(&ctx); /* init ctx for 2nd pass */
218  fr_sha1_update(&ctx, k_opad, 64); /* start with outer pad */
219  fr_sha1_update(&ctx, digest, 20); /* then results of 1st hash */
220  fr_sha1_final(digest, &ctx); /* finish up 2nd pass */
221 
222 #ifdef HMAC_SHA1_DATA_PROBLEMS
223  if (sha1_data_problems) {
224  int j;
225 
226  printf("\nhmac-sha1 mac(20): ");
227  j=0;
228  for (i = 0; i < 20; i++) {
229  if(j==4) {
230  printf("_");
231  j=0;
232  }
233  j++;
234 
235  printf("%02x", digest[i]);
236  }
237  printf("\n");
238  }
239 #endif
240  return 0;
241 }
242 #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_sha1(uint8_t digest[static SHA1_DIGEST_LENGTH], uint8_t const *in, size_t inlen, uint8_t const *key, size_t key_len)
Calculate HMAC using internal SHA1 implementation.
Definition: hmac_sha1.c:124
unsigned char uint8_t
Definition: merged_model.c:30
void fr_sha1_init(fr_sha1_ctx *context)
Definition: sha1.c:93
void fr_sha1_final(uint8_t digest[static SHA1_DIGEST_LENGTH], fr_sha1_ctx *context)
Definition: sha1.c:141
void fr_sha1_update(fr_sha1_ctx *context, uint8_t const *in, size_t len)
Definition: sha1.c:105
#define SHA1_DIGEST_LENGTH
Definition: sha1.h:29
#define fr_strerror_const(_msg)
Definition: strerror.h:223
static size_t char fr_sbuff_t size_t inlen
Definition: value.h:984