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