1/*
2 * fips186prf.c    An implementation of the FIPS-186-2 SHA1-based PRF.
3 *
4 * The development of the EAP/SIM support was funded by Internet Foundation
5 * Austria (http://www.nic.at/ipa).
6 *
7 * This code was written from scratch by Michael Richardson, and it is
8 * dual licensed under both GPL and BSD.
9 *
10 * Version:     $Id: fips186prf.c,v 1.12 2008/06/05 12:17:33 aland Exp $
11 *
12 * GPL notice:
13 *
14 *   This program is free software; you can redistribute it and/or modify
15 *   it under the terms of the GNU General Public License as published by
16 *   the Free Software Foundation; either version 2 of the License, or
17 *   (at your option) any later version.
18 *
19 *   This program is distributed in the hope that it will be useful,
20 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
21 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 *   GNU General Public License for more details.
23 *
24 *   You should have received a copy of the GNU General Public License
25 *   along with this program; if not, write to the Free Software
26 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 *
28 * BSD notice:
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
32 * are met:
33 * 1. Redistributions of source code must retain the above copyright
34 *    notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 *    notice, this list of conditions and the following disclaimer in the
37 *    documentation and/or other materials provided with the distribution.
38 * 3. Neither the name of The NetBSD Foundation nor the names of its
39 *    contributors may be used to endorse or promote products derived
40 *    from this software without specific prior written permission.
41 *
42 * Copyright 2003  Michael Richardson <mcr@sandelman.ottawa.on.ca>
43 * Copyright 2006  The FreeRADIUS server project
44 *
45 */
46
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50
51#include <sys/types.h>
52#include <stdint.h>
53#include "fr_sha1.h"
54#include "fips186prf.h"
55
56/*
57 * we do it in 8-bit chunks, because we have to keep the numbers
58 * in network byte order (i.e. MSB)
59 *
60 * make it a structure so that we can do structure assignments.
61 */
62typedef struct onesixty {
63	uint8_t p[20];
64} onesixty;
65
66static void onesixty_add_mod(onesixty *sum, onesixty *a, onesixty *b)
67{
68	uint32_t s;
69	int i, carry;
70
71	carry = 0;
72	for(i=19; i>=0; i--) {
73/*	for(i=0; i<20; i++) {  */
74		s = a->p[i] + b->p[i] + carry;
75		sum->p[i] = s & 0xff;
76		carry = s >> 8;
77	}
78}
79
80/*
81 * run the FIPS-186-2 PRF on the given Master Key (160 bits)
82 * in order to derive 1280 bits (160 bytes) of keying data from
83 * it.
84 *
85 * Given that in EAP-SIM, this is coming from a 64-bit Kc it seems
86 * like an awful lot of "randomness" to pull out.. (MCR)
87 *
88 */
89
90__private_extern__
91void fips186_2prf(uint8_t mk[20], uint8_t finalkey[160])
92{
93	fr_SHA1_CTX context;
94	int j;
95	onesixty xval, xkey, w_0, w_1, sum, one;
96	uint8_t *f;
97	uint8_t zeros[64];
98
99	/*
100	 * let XKEY := MK,
101	 *
102	 * Step 3: For j = 0 to 3 do
103         *   a. XVAL = XKEY
104         *   b. w_0 = SHA1(XVAL)
105         *   c. XKEY = (1 + XKEY + w_0) mod 2^160
106         *   d. XVAL = XKEY
107         *   e. w_1 = SHA1(XVAL)
108         *   f. XKEY = (1 + XKEY + w_1) mod 2^160
109         * 3.3 x_j = w_0|w_1
110	 *
111	 */
112	memcpy(&xkey, mk, sizeof(xkey));
113
114	/* make the value 1 */
115	memset(&one,  0, sizeof(one));
116	one.p[19]=1;
117
118	f=finalkey;
119
120	for(j=0; j<4; j++) {
121		/*   a. XVAL = XKEY  */
122		xval = xkey;
123
124		/*   b. w_0 = SHA1(XVAL)  */
125		fr_SHA1Init(&context);
126
127		memset(zeros, 0, sizeof(zeros));
128		memcpy(zeros, xval.p, 20);
129		fr_SHA1Transform(&context, zeros);
130		fr_SHA1FinalNoLen(w_0.p, &context);
131
132		/*   c. XKEY = (1 + XKEY + w_0) mod 2^160 */
133		onesixty_add_mod(&sum,  &xkey, &w_0);
134		onesixty_add_mod(&xkey, &sum,  &one);
135
136		/*   d. XVAL = XKEY  */
137		xval = xkey;
138
139		/*   e. w_1 = SHA1(XVAL)  */
140		fr_SHA1Init(&context);
141
142		memset(zeros, 0, sizeof(zeros));
143		memcpy(zeros, xval.p, 20);
144		fr_SHA1Transform(&context, zeros);
145		fr_SHA1FinalNoLen(w_1.p, &context);
146
147		/*   f. XKEY = (1 + XKEY + w_1) mod 2^160 */
148		onesixty_add_mod(&sum,  &xkey, &w_1);
149		onesixty_add_mod(&xkey, &sum,  &one);
150
151		/* now store it away */
152		memcpy(f, &w_0, 20);
153		f += 20;
154
155		memcpy(f, &w_1, 20);
156		f += 20;
157	}
158}
159
160#ifdef TEST_FIPS186PRF
161/*
162  from RFC4186: A.5.  EAP-Request/SIM/Challenge
163
164   Next, the MK is calculated as specified in Section 7*.
165
166   MK = e576d5ca 332e9930 018bf1ba ee2763c7 95b3c712
167
168   And the other keys are derived using the PRNG:
169
170         K_encr = 536e5ebc 4465582a a6a8ec99 86ebb620
171         K_aut =  25af1942 efcbf4bc 72b39434 21f2a974
172         MSK =    39d45aea f4e30601 983e972b 6cfd46d1
173                  c3637733 65690d09 cd44976b 525f47d3
174                  a60a985e 955c53b0 90b2e4b7 3719196a
175                  40254296 8fd14a88 8f46b9a7 886e4488
176         EMSK =   5949eab0 fff69d52 315c6c63 4fd14a7f
177                  0d52023d 56f79698 fa6596ab eed4f93f
178                  bb48eb53 4d985414 ceed0d9a 8ed33c38
179                  7c9dfdab 92ffbdf2 40fcecf6 5a2c93b9
180 */
181uint8_t	mk[20] = { 0xe5, 0x76, 0xd5, 0xca,
182		    0x33, 0x2e, 0x99, 0x30,
183		    0x01, 0x8b, 0xf1, 0xba,
184		    0xee, 0x27, 0x63, 0xc7,
185		    0x95, 0xb3, 0xc7, 0x12 };
186
187uint8_t final_output[160] = {
188    0x53, 0x6e, 0x5e, 0xbc, 0x44, 0x65, 0x58, 0x2a, 0xa6, 0xa8, 0xec, 0x99,  0x86, 0xeb, 0xb6, 0x20,
189    0x25, 0xaf, 0x19, 0x42, 0xef, 0xcb, 0xf4, 0xbc, 0x72, 0xb3, 0x94, 0x34,  0x21, 0xf2, 0xa9, 0x74,
190    0x39, 0xd4, 0x5a, 0xea, 0xf4, 0xe3, 0x06, 0x01, 0x98, 0x3e, 0x97, 0x2b,  0x6c, 0xfd, 0x46, 0xd1,
191    0xc3, 0x63, 0x77, 0x33, 0x65, 0x69, 0x0d, 0x09, 0xcd, 0x44, 0x97, 0x6b,  0x52, 0x5f, 0x47, 0xd3,
192    0xa6, 0x0a, 0x98, 0x5e, 0x95, 0x5c, 0x53, 0xb0, 0x90, 0xb2, 0xe4, 0xb7,  0x37, 0x19, 0x19, 0x6a,
193    0x40, 0x25, 0x42, 0x96, 0x8f, 0xd1, 0x4a, 0x88, 0x8f, 0x46, 0xb9, 0xa7,  0x88, 0x6e, 0x44, 0x88,
194    0x59, 0x49, 0xea, 0xb0, 0xff, 0xf6, 0x9d, 0x52, 0x31, 0x5c, 0x6c, 0x63,  0x4f, 0xd1, 0x4a, 0x7f,
195    0x0d, 0x52, 0x02, 0x3d, 0x56, 0xf7, 0x96, 0x98, 0xfa, 0x65, 0x96, 0xab,  0xee, 0xd4, 0xf9, 0x3f,
196    0xbb, 0x48, 0xeb, 0x53, 0x4d, 0x98, 0x54, 0x14, 0xce, 0xed, 0x0d, 0x9a,  0x8e, 0xd3, 0x3c, 0x38,
197    0x7c, 0x9d, 0xfd, 0xab, 0x92, 0xff, 0xbd, 0xf2, 0x40, 0xfc, 0xec, 0xf6,  0x5a, 0x2c, 0x93, 0xb9,
198};
199
200int
201main(int argc, char *argv[])
202{
203	uint8_t finalkey[160];
204	int i, j, k;
205
206	fips186_2prf(mk, finalkey);
207
208	printf("Input was: ");
209	j=0;
210	for (i = 0; i < 20; i++) {
211		if(j==4) {
212			printf(" ");
213			j=0;
214		}
215		j++;
216
217		printf("%02x", mk[i]);
218	}
219
220	printf("\nOutput was: ");
221	j=0; k=0;
222	for (i = 0; i < 160; i++) {
223		if(k==16) {
224			printf("\n            ");
225			k=0;
226			j=0;
227		}
228		if(j==4) {
229			printf(" ");
230			j=0;
231		}
232		k++;
233		j++;
234
235		printf("%02x", finalkey[i]);
236	}
237	printf("\n");
238
239	if (bcmp(finalkey, final_output, sizeof(finalkey)) != 0) {
240	    fprintf(stderr, "Calculation FAILED\n");
241	}
242	else {
243	    printf("Calculation is correct!\n");
244	}
245	exit(0);
246	return (0);
247}
248#endif /* TEST_FIPS186PRF */
249