The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
eap_fast_crypto.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program 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
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 3f2145ae9ddcdc921489fa204c8300ebf3cc0624 $
19 * @file eap_fast_crypto.c
20 * @brief Cryptographic functions for EAP-FAST.
21 *
22 * @author Alexander Clouter (alex@digriz.org.uk)
23 *
24 * @copyright 2016 Alan DeKok (aland@freeradius.org)
25 * @copyright 2016 The FreeRADIUS server project
26 */
27RCSID("$Id: 3f2145ae9ddcdc921489fa204c8300ebf3cc0624 $")
28USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */
29
30#include <stdio.h>
31#include <freeradius-devel/tls/base.h>
32#include <freeradius-devel/eap/tls.h>
33
34#include <freeradius-devel/tls/openssl_user_macros.h>
35#include <openssl/evp.h>
36#include <openssl/aes.h>
37#include <openssl/err.h>
38
39#include "eap_fast_crypto.h"
40
41/* EAP-FAST Pseudo-Random Function (T-PRF): RFC 4851, Section 5.5 */
42void T_PRF(unsigned char const *secret, unsigned int secret_len,
43 char const *prf_label,
44 unsigned char const *seed, unsigned int seed_len,
45 unsigned char *out, unsigned int out_len)
46{
47 size_t prf_size = strlen(prf_label);
48 size_t pos;
49 uint8_t *buf;
50
51 if (prf_size > 128) prf_size = 128;
52 prf_size++; /* include trailing zero */
53
54 buf = talloc_size(NULL, SHA1_DIGEST_LENGTH + prf_size + seed_len + 2 + 1);
55
56 memcpy(buf + SHA1_DIGEST_LENGTH, prf_label, prf_size);
57 if (seed) memcpy(buf + SHA1_DIGEST_LENGTH + prf_size, seed, seed_len);
58 *(uint16_t *)&buf[SHA1_DIGEST_LENGTH + prf_size + seed_len] = htons(out_len);
59 buf[SHA1_DIGEST_LENGTH + prf_size + seed_len + 2] = 1;
60
61 // T1 is just the seed
62 fr_hmac_sha1(buf, buf + SHA1_DIGEST_LENGTH, prf_size + seed_len + 2 + 1, secret, secret_len);
63
64#define MIN(a,b) (((a)>(b)) ? (b) : (a))
65 memcpy(out, buf, MIN(out_len, SHA1_DIGEST_LENGTH));
66
68 while (pos < out_len) {
69 buf[SHA1_DIGEST_LENGTH + prf_size + seed_len + 2]++;
70
71 fr_hmac_sha1(buf, buf, SHA1_DIGEST_LENGTH + prf_size + seed_len + 2 + 1, secret, secret_len);
72 memcpy(&out[pos], buf, MIN(out_len - pos, SHA1_DIGEST_LENGTH));
73
74 if (out_len - pos <= SHA1_DIGEST_LENGTH)
75 break;
76
77 pos += SHA1_DIGEST_LENGTH;
78 }
79
80 memset(buf, 0, SHA1_DIGEST_LENGTH + prf_size + seed_len + 2 + 1);
81 talloc_free(buf);
82}
83
84// http://stackoverflow.com/a/29838852
85static NEVER_RETURNS void handleErrors(void)
86{
87 unsigned long errCode;
88
89 fprintf(stderr, "An error occurred\n");
90 while((errCode = ERR_get_error()))
91 {
92 char *err = ERR_error_string(errCode, NULL);
93 fprintf(stderr, "%s\n", err);
94 }
95 abort();
96}
97
98// https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption#Authenticated_Encryption_using_GCM_mode
99int eap_fast_encrypt(uint8_t const *plaintext, size_t plaintext_len,
100 uint8_t const *aad, size_t aad_len,
101 uint8_t const *key, uint8_t *iv, unsigned char *ciphertext,
102 uint8_t *tag)
103{
104 EVP_CIPHER_CTX *ctx;
105
106 int len;
107
108 int ciphertext_len;
109
110
111 /* Create and initialise the context */
112 if (!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
113
114 /* Initialise the encryption operation. */
115 if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL))
116 handleErrors();
117
118 /* Set IV length if default 12 bytes (96 bits) is not appropriate */
119 if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL))
120 handleErrors();
121
122 /* Initialise key and IV */
123 if (1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) handleErrors();
124
125 /* Provide any AAD data. This can be called zero or more times as
126 * required
127 */
128 if (1 != EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len))
129 handleErrors();
130
131 /* Provide the message to be encrypted, and obtain the encrypted output.
132 * EVP_EncryptUpdate can be called multiple times if necessary
133 */
134 if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
135 handleErrors();
136 ciphertext_len = len;
137
138 /* Finalise the encryption. Normally ciphertext bytes may be written at
139 * this stage, but this does not occur in GCM mode
140 */
141 if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors();
142 ciphertext_len += len;
143
144 /* Get the tag */
145 if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag))
146 handleErrors();
147
148 /* Clean up */
149 EVP_CIPHER_CTX_free(ctx);
150
151 return ciphertext_len;
152}
153
154int eap_fast_decrypt(uint8_t const *ciphertext, size_t ciphertext_len,
155 uint8_t const *aad, size_t aad_len,
156 uint8_t const *tag, uint8_t const *key, uint8_t const *iv, uint8_t *plaintext)
157{
158 EVP_CIPHER_CTX *ctx;
159 int len;
160 int plaintext_len;
161 int ret;
162
163 /* Create and initialise the context */
164 if (!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
165
166 /* Initialise the decryption operation. */
167 if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL))
168 handleErrors();
169
170 /* Set IV length. Not necessary if this is 12 bytes (96 bits) */
171 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL))
172 handleErrors();
173
174 /* Initialise key and IV */
175 if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) handleErrors();
176
177 /* Provide any AAD data. This can be called zero or more times as
178 * required
179 */
180 if (!EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len))
181 handleErrors();
182
183 /* Provide the message to be decrypted, and obtain the plaintext output.
184 * EVP_DecryptUpdate can be called multiple times if necessary
185 */
186 if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
187 handleErrors();
188 plaintext_len = len;
189
190 {
191 unsigned char *tmp;
192
193 memcpy(&tmp, &tag, sizeof(tmp));
194
195 /* Set expected tag value. Works in OpenSSL 1.0.1d and later */
196 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tmp)) handleErrors();
197 }
198
199 /* Finalise the decryption. A positive return value indicates success,
200 * anything else is a failure - the plaintext is not trustworthy.
201 */
202 ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
203
204 /* Clean up */
205 EVP_CIPHER_CTX_free(ctx);
206
207 if (ret > 0)
208 {
209 /* Success */
210 plaintext_len += len;
211 return plaintext_len;
212 }
213 else
214 {
215 /* Verify failed */
216 return -1;
217 }
218}
219
220
221static void crypto_rfc4346_p_hash(uint8_t *out, size_t out_len,
222 EVP_MD const *evp_md,
223 uint8_t const *secret, size_t secret_len,
224 uint8_t const *seed, size_t seed_len)
225{
226 EVP_MD_CTX *ctx_a, *ctx_out;
227 EVP_PKEY *pkey;
228
229 uint8_t a[EVP_MAX_MD_SIZE];
230 size_t size;
231
232 ctx_a = EVP_MD_CTX_new();
233 ctx_out = EVP_MD_CTX_new();
234
235 MEM(pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, secret, secret_len));
236
237 EVP_DigestSignInit(ctx_a, NULL, evp_md, NULL, pkey);
238 EVP_DigestSignInit(ctx_out, NULL, evp_md, NULL, pkey);
239
240 size = EVP_MD_size(evp_md);
241
242 /* Calculate A(1) */
243 EVP_DigestSignUpdate(ctx_a, seed, seed_len);
244
245 /*
246 * OpenSSL <= 1.1.1 requires a non-null pointer for len
247 */
248 EVP_DigestSignFinal(ctx_a, a, &(size_t){ sizeof(a) });
249
250 while (1) {
251 /* Calculate next part of output */
252 EVP_DigestSignUpdate(ctx_out, a, size);
253 EVP_DigestSignUpdate(ctx_out, seed, seed_len);
254
255 /* Check if last part */
256 if (out_len < size) {
257 EVP_DigestSignFinal(ctx_out, a, &(size_t){ sizeof(a) });
258 memcpy(out, a, out_len);
259 break;
260 }
261
262 /* Place digest in output buffer */
263 EVP_DigestSignFinal(ctx_out, out, &(size_t){ EVP_MAX_MD_SIZE });
264 EVP_MD_CTX_reset(ctx_out);
265
266 EVP_DigestSignInit(ctx_out, NULL, evp_md, NULL, pkey);
267 out += size;
268 out_len -= size;
269
270 /* Calculate next A(i) */
271 EVP_MD_CTX_reset(ctx_a);
272 EVP_DigestSignInit(ctx_a, NULL, evp_md, NULL, pkey);
273 EVP_DigestSignUpdate(ctx_a, a, size);
274 EVP_DigestSignFinal(ctx_a, a, &(size_t){ EVP_MAX_MD_SIZE });
275 }
276
277 EVP_PKEY_free(pkey);
278
279 EVP_MD_CTX_free(ctx_a);
280 EVP_MD_CTX_free(ctx_out);
281#ifdef __STDC_LIB_EXT1__
282 memset_s(a, 0, sizeof(a), sizeof(a));
283#else
284 memset(a, 0, sizeof(a));
285#endif
286}
287
288
289static void eap_crypto_rfc4346_prf(uint8_t *out, size_t out_len, uint8_t *scratch,
290 uint8_t const *secret, size_t secret_len,
291 uint8_t const *seed, size_t seed_len)
292{
293 unsigned int i;
294 unsigned int len = (secret_len + 1) / 2;
295 uint8_t const *s1 = secret;
296 uint8_t const *s2 = secret + (secret_len - len);
297
298 crypto_rfc4346_p_hash(out, out_len, EVP_md5(), s1, len, seed, seed_len);
299 crypto_rfc4346_p_hash(scratch, out_len, EVP_sha1(), s2, len, seed, seed_len);
300
301 for (i = 0; i < out_len; i++) out[i] ^= scratch[i];
302}
303
304
305/*
306 * Same as before, but for EAP-FAST the order of {server,client}_random is flipped
307 */
308void eap_fast_tls_gen_challenge(SSL *s, uint8_t *buffer, uint8_t *scratch, size_t size, char const *prf_label)
309{
310 uint8_t *p;
311 size_t len, master_key_len;
312 uint8_t seed[128 + (2 * SSL3_RANDOM_SIZE)];
313 uint8_t master_key[SSL_MAX_MASTER_KEY_LENGTH];
314
315 len = strlen(prf_label);
316 if (len > 128) len = 128;
317
318 p = seed;
319 memcpy(p, prf_label, len);
320 p += len;
321 (void) SSL_get_server_random(s, p, SSL3_RANDOM_SIZE);
322 p += SSL3_RANDOM_SIZE;
323 (void) SSL_get_client_random(s, p, SSL3_RANDOM_SIZE);
324 p += SSL3_RANDOM_SIZE;
325
326 master_key_len = SSL_SESSION_get_master_key(SSL_get_session(s), master_key, sizeof(master_key));
327 eap_crypto_rfc4346_prf(buffer, size, scratch, master_key, master_key_len, seed, p - seed);
328}
static int const char char buffer[256]
Definition acutest.h:576
#define USES_APPLE_DEPRECATED_API
Definition build.h:470
#define RCSID(id)
Definition build.h:483
#define NEVER_RETURNS
Should be placed before the function return type.
Definition build.h:313
#define MEM(x)
Definition debug.h:36
static fr_slen_t err
Definition dict.h:824
int eap_fast_decrypt(uint8_t const *ciphertext, size_t ciphertext_len, uint8_t const *aad, size_t aad_len, uint8_t const *tag, uint8_t const *key, uint8_t const *iv, uint8_t *plaintext)
static NEVER_RETURNS void handleErrors(void)
void eap_fast_tls_gen_challenge(SSL *s, uint8_t *buffer, uint8_t *scratch, size_t size, char const *prf_label)
#define MIN(a, b)
int eap_fast_encrypt(uint8_t const *plaintext, size_t plaintext_len, uint8_t const *aad, size_t aad_len, uint8_t const *key, uint8_t *iv, unsigned char *ciphertext, uint8_t *tag)
static void crypto_rfc4346_p_hash(uint8_t *out, size_t out_len, EVP_MD const *evp_md, uint8_t const *secret, size_t secret_len, uint8_t const *seed, size_t seed_len)
USES_APPLE_DEPRECATED_API void T_PRF(unsigned char const *secret, unsigned int secret_len, char const *prf_label, unsigned char const *seed, unsigned int seed_len, unsigned char *out, unsigned int out_len)
static void eap_crypto_rfc4346_prf(uint8_t *out, size_t out_len, uint8_t *scratch, uint8_t const *secret, size_t secret_len, uint8_t const *seed, size_t seed_len)
Crypto function declarations.
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
talloc_free(reap)
unsigned short uint16_t
unsigned char uint8_t
static char * secret
#define SHA1_DIGEST_LENGTH
Definition sha1.h:29
static size_t char ** out
Definition value.h:997