549183e4c509dff6a6a406ed5732975119cb5ceb
[freeradius.git] / src / modules / rlm_eap / libeap / mppe_keys.c
1 /*
2  * mppe_keys.c
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2002  Axis Communications AB
21  * Copyright 2006  The FreeRADIUS server project
22  * Authors: Henrik Eriksson <henriken@axis.com> & Lars Viklund <larsv@axis.com>
23  */
24
25 RCSID("$Id$")
26 USES_APPLE_DEPRECATED_API       /* OpenSSL API has been deprecated by Apple */
27
28 #include "eap_tls.h"
29 #include <openssl/hmac.h>
30
31
32 /*
33  * TLS PRF from RFC 2246
34  */
35 static void P_hash(EVP_MD const *evp_md,
36                    unsigned char const *secret, unsigned int secret_len,
37                    unsigned char const *seed,   unsigned int seed_len,
38                    unsigned char *out, unsigned int out_len)
39 {
40         HMAC_CTX ctx_a, ctx_out;
41         unsigned char a[HMAC_MAX_MD_CBLOCK];
42         unsigned int size;
43
44         HMAC_CTX_init(&ctx_a);
45         HMAC_CTX_init(&ctx_out);
46 #ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
47         HMAC_CTX_set_flags(&ctx_a, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
48         HMAC_CTX_set_flags(&ctx_out, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
49 #endif
50         HMAC_Init_ex(&ctx_a, secret, secret_len, evp_md, NULL);
51         HMAC_Init_ex(&ctx_out, secret, secret_len, evp_md, NULL);
52
53         size = HMAC_size(&ctx_out);
54
55         /* Calculate A(1) */
56         HMAC_Update(&ctx_a, seed, seed_len);
57         HMAC_Final(&ctx_a, a, NULL);
58
59         while (1) {
60                 /* Calculate next part of output */
61                 HMAC_Update(&ctx_out, a, size);
62                 HMAC_Update(&ctx_out, seed, seed_len);
63
64                 /* Check if last part */
65                 if (out_len < size) {
66                         HMAC_Final(&ctx_out, a, NULL);
67                         memcpy(out, a, out_len);
68                         break;
69                 }
70
71                 /* Place digest in output buffer */
72                 HMAC_Final(&ctx_out, out, NULL);
73                 HMAC_Init_ex(&ctx_out, NULL, 0, NULL, NULL);
74                 out += size;
75                 out_len -= size;
76
77                 /* Calculate next A(i) */
78                 HMAC_Init_ex(&ctx_a, NULL, 0, NULL, NULL);
79                 HMAC_Update(&ctx_a, a, size);
80                 HMAC_Final(&ctx_a, a, NULL);
81         }
82
83         HMAC_CTX_cleanup(&ctx_a);
84         HMAC_CTX_cleanup(&ctx_out);
85         memset(a, 0, sizeof(a));
86 }
87
88 /*  EAP-FAST Pseudo-Random Function (T-PRF): RFC 4851, Section 5.5 */
89 void T_PRF(unsigned char const *secret, unsigned int secret_len,
90            char const *prf_label,
91            unsigned char const *seed,  unsigned int seed_len,
92            unsigned char *out, unsigned int out_len)
93 {
94         size_t prf_size = strlen(prf_label);
95         size_t pos;
96         uint8_t *buf;
97
98         if (prf_size > 128) prf_size = 128;
99         prf_size++;     /* include trailing zero */
100
101         buf = talloc_size(NULL, SHA1_DIGEST_LENGTH + prf_size + seed_len + 2 + 1);
102
103         memcpy(buf + SHA1_DIGEST_LENGTH, prf_label, prf_size);
104         if (seed) memcpy(buf + SHA1_DIGEST_LENGTH + prf_size, seed, seed_len);
105         *(uint16_t *)&buf[SHA1_DIGEST_LENGTH + prf_size + seed_len] = htons(out_len);
106         buf[SHA1_DIGEST_LENGTH + prf_size + seed_len + 2] = 1;
107
108         // T1 is just the seed
109         fr_hmac_sha1(buf, buf + SHA1_DIGEST_LENGTH, prf_size + seed_len + 2 + 1, secret, secret_len);
110
111 #define MIN(a,b) (((a)>(b)) ? (b) : (a))
112         memcpy(out, buf, MIN(out_len, SHA1_DIGEST_LENGTH));
113
114         pos = SHA1_DIGEST_LENGTH;
115         while (pos < out_len) {
116                 buf[SHA1_DIGEST_LENGTH + prf_size + seed_len + 2]++;
117
118                 fr_hmac_sha1(buf, buf, SHA1_DIGEST_LENGTH + prf_size + seed_len + 2 + 1, secret, secret_len);
119                 memcpy(&out[pos], buf, MIN(out_len - pos, SHA1_DIGEST_LENGTH));
120
121                 if (out_len - pos <= SHA1_DIGEST_LENGTH)
122                         break;
123
124                 pos += SHA1_DIGEST_LENGTH;
125         }
126
127         memset(buf, 0, SHA1_DIGEST_LENGTH + prf_size + seed_len + 2 + 1);
128         talloc_free(buf);
129 }
130
131 static void PRF(unsigned char const *secret, unsigned int secret_len,
132                 unsigned char const *seed,   unsigned int seed_len,
133                 unsigned char *out, unsigned char *buf, unsigned int out_len)
134 {
135         unsigned int i;
136         unsigned int len = (secret_len + 1) / 2;
137         uint8_t const *s1 = secret;
138         uint8_t const *s2 = secret + (secret_len - len);
139
140         P_hash(EVP_md5(),  s1, len, seed, seed_len, out, out_len);
141         P_hash(EVP_sha1(), s2, len, seed, seed_len, buf, out_len);
142
143         for (i=0; i < out_len; i++) {
144                 out[i] ^= buf[i];
145         }
146 }
147
148 #define EAPTLS_MPPE_KEY_LEN     32
149
150 /*
151  *      Generate keys according to RFC 2716 and add to reply
152  */
153 void eaptls_gen_mppe_keys(REQUEST *request, SSL *s, char const *prf_label)
154 {
155         uint8_t out[4 * EAPTLS_MPPE_KEY_LEN];
156         uint8_t *p;
157         size_t prf_size;
158
159         prf_size = strlen(prf_label);
160
161 #if OPENSSL_VERSION_NUMBER >= 0x10001000L
162         if (SSL_export_keying_material(s, out, sizeof(out), prf_label, prf_size, NULL, 0, 0) != 1) {
163                 ERROR("Failed generating keying material");
164                 return;
165         }
166 #else
167         {
168                 uint8_t seed[64 + (2 * SSL3_RANDOM_SIZE)];
169                 uint8_t buf[4 * EAPTLS_MPPE_KEY_LEN];
170
171                 p = seed;
172
173                 memcpy(p, prf_label, prf_size);
174                 p += prf_size;
175
176                 memcpy(p, s->s3->client_random, SSL3_RANDOM_SIZE);
177                 p += SSL3_RANDOM_SIZE;
178                 prf_size += SSL3_RANDOM_SIZE;
179
180                 memcpy(p, s->s3->server_random, SSL3_RANDOM_SIZE);
181                 prf_size += SSL3_RANDOM_SIZE;
182
183                 PRF(s->session->master_key, s->session->master_key_length,
184                     seed, prf_size, out, buf, sizeof(out));
185         }
186 #endif
187
188         p = out;
189         eap_add_reply(request, "MS-MPPE-Recv-Key", p, EAPTLS_MPPE_KEY_LEN);
190         p += EAPTLS_MPPE_KEY_LEN;
191         eap_add_reply(request, "MS-MPPE-Send-Key", p, EAPTLS_MPPE_KEY_LEN);
192
193         eap_add_reply(request, "EAP-MSK", out, 64);
194         eap_add_reply(request, "EAP-EMSK", out + 64, 64);
195 }
196
197
198 #define FR_TLS_PRF_CHALLENGE    "ttls challenge"
199
200 /*
201  *      Generate the TTLS challenge
202  *
203  *      It's in the TLS module simply because it's only a few lines
204  *      of code, and it needs access to the TLS PRF functions.
205  */
206 void eapttls_gen_challenge(SSL *s, uint8_t *buffer, size_t size)
207 {
208 #if OPENSSL_VERSION_NUMBER >= 0x10001000L
209         SSL_export_keying_material(s, buffer, size, FR_TLS_PRF_CHALLENGE,
210                                    sizeof(FR_TLS_PRF_CHALLENGE) - 1, NULL, 0, 0);
211
212 #else
213         uint8_t out[32], buf[32];
214         uint8_t seed[sizeof(FR_TLS_PRF_CHALLENGE)-1 + 2*SSL3_RANDOM_SIZE];
215         uint8_t *p = seed;
216
217         memcpy(p, FR_TLS_PRF_CHALLENGE, sizeof(FR_TLS_PRF_CHALLENGE)-1);
218         p += sizeof(FR_TLS_PRF_CHALLENGE)-1;
219         memcpy(p, s->s3->client_random, SSL3_RANDOM_SIZE);
220         p += SSL3_RANDOM_SIZE;
221         memcpy(p, s->s3->server_random, SSL3_RANDOM_SIZE);
222
223         PRF(s->session->master_key, s->session->master_key_length,
224             seed, sizeof(seed), out, buf, sizeof(out));
225         memcpy(buffer, out, size);
226 #endif
227 }
228
229 /*
230  *      Actually generates EAP-Session-Id, which is an internal server
231  *      attribute.  Not all systems want to send EAP-Key-Nam
232  */
233 void eaptls_gen_eap_key(RADIUS_PACKET *packet, SSL *s, uint32_t header)
234 {
235         VALUE_PAIR *vp;
236         uint8_t *p;
237
238         vp = fr_pair_afrom_num(packet, PW_EAP_SESSION_ID, 0);
239         if (!vp) return;
240
241         vp->vp_length = 1 + 2 * SSL3_RANDOM_SIZE;
242         p = talloc_array(vp, uint8_t, vp->vp_length);
243
244         p[0] = header & 0xff;
245
246         SSL_get_client_random(s, p + 1, SSL3_RANDOM_SIZE);
247         SSL_get_server_random(s, p + 1 + SSL3_RANDOM_SIZE, SSL3_RANDOM_SIZE);
248
249         vp->vp_octets = p;
250         fr_pair_add(&packet->vps, vp);
251 }
252
253 /*
254  *      Same as before, but for EAP-FAST the order of {server,client}_random is flipped
255  */
256 void eap_fast_tls_gen_challenge(SSL *s, uint8_t *buffer, uint8_t *scratch, size_t size, char const *prf_label)
257 {
258         uint8_t *p;
259         size_t len, master_key_len;
260         uint8_t seed[128 + 2*SSL3_RANDOM_SIZE];
261         uint8_t master_key[SSL_MAX_MASTER_KEY_LENGTH];
262
263         len = strlen(prf_label);
264         if (len > 128) len = 128;
265
266         p = seed;
267         memcpy(p, prf_label, len);
268         p += len;
269         SSL_get_server_random(s, p, SSL3_RANDOM_SIZE);
270         p += SSL3_RANDOM_SIZE;
271         SSL_get_client_random(s, p, SSL3_RANDOM_SIZE);
272         p += SSL3_RANDOM_SIZE;
273
274         master_key_len = SSL_SESSION_get_master_key(SSL_get_session(s), master_key, sizeof(master_key));
275         PRF(master_key, master_key_len, seed, p - seed, buffer, scratch, size);
276 }
277
278