1/*
2 * Copyright (c) 2012-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * eapaka_plugin.c
26 * - EAP-AKA client
27 */
28
29/*
30 * Modification History
31 *
32 * October 8, 2012	Dieter Siegmund (dieter@apple)
33 * - created
34 */
35#include "EAPClientProperties.h"
36#include <EAP8021X/EAPClientPlugin.h>
37#include <EAP8021X/EAPClientProperties.h>
38#include <CoreFoundation/CFData.h>
39#include <CoreFoundation/CFArray.h>
40#include <CoreFoundation/CFDictionary.h>
41#include <SystemConfiguration/SCValidation.h>
42#include <stdbool.h>
43#include <unistd.h>
44#include <stdlib.h>
45#include <string.h>
46#include <stdio.h>
47#include <syslog.h>
48#include <sys/param.h>
49#include <CommonCrypto/CommonDigest.h>
50#include <CommonCrypto/CommonCryptor.h>
51#include <sys/param.h>
52#include <EAP8021X/EAP.h>
53#include <EAP8021X/EAPUtil.h>
54#include <EAP8021X/EAPClientModule.h>
55#include <TargetConditionals.h>
56#include "myCFUtil.h"
57#include "printdata.h"
58#include "fips186prf.h"
59#include "SIMAccess.h"
60#include "nbo.h"
61#include "EAPSIMAKAPersistentState.h"
62#include "EAPSIMAKA.h"
63#include "EAPSIMAKAUtil.h"
64#include "EAPLog.h"
65
66#define EAP_AKA_NAME		"EAP-AKA"
67
68/*
69 * kEAPClientPropEAPAKARES
70 * kEAPClientPropEAPAKACk
71 * kEAPClientPropEAPAKAIk
72 * - static (RES, Ck, Ik) used for testing
73 */
74#define kEAPClientPropEAPAKARES		CFSTR("EAPAKARES")	/* data */
75#define kEAPClientPropEAPAKACk		CFSTR("EAPAKACk")	/* data */
76#define kEAPClientPropEAPAKAIk		CFSTR("EAPAKAIk")	/* data */
77
78/**
79 ** Protocol-specific defines
80 **/
81
82enum {
83    kEAPAKAClientStateNone = 0,
84    kEAPAKAClientStateIdentity = 1,
85    kEAPAKAClientStateChallenge = 2,
86    kEAPAKAClientStateReauthentication = 3,
87    kEAPAKAClientStateSuccess = 4,
88    kEAPAKAClientStateFailure = 5
89};
90typedef int	EAPAKAClientState;
91
92typedef struct {
93    CFDataRef		res;
94    CFDataRef		ck;
95    CFDataRef		ik;
96} AKAStaticKeys, * AKAStaticKeysRef;
97
98/*
99 * Type: EAPAKAContext
100 * Purpose:
101 *   Holds the EAP-AKA module's context private data.
102 */
103typedef struct {
104    EAPClientPluginDataRef 	plugin;
105    EAPClientState		plugin_state;
106    EAPAKAClientState		state;
107    int				previous_identifier;
108    int				identity_count;
109    int				n_required_rands;
110    EAPSIMAKAAttributeType	last_identity_type;
111    CFDataRef			last_identity;
112    EAPSIMAKAKeyInfo		key_info;
113    bool			key_info_valid;
114    EAPSIMAKAPersistentStateRef	persist;
115    bool			reauth_success;
116    AKAStaticKeys		static_keys;
117    uint8_t			pkt[1500];
118} EAPAKAContext, *EAPAKAContextRef;
119
120/**
121 ** Identity routines
122 **/
123STATIC CFStringRef
124copy_imsi_identity(CFStringRef imsi, CFStringRef realm)
125{
126
127    if (realm != NULL) {
128	return (CFStringCreateWithFormat(NULL, NULL,
129					 CFSTR("0" "%@" "@" "%@"),
130					 imsi, realm));
131    }
132    return (CFStringCreateWithFormat(NULL, NULL, CFSTR("0" "%@"),
133				     imsi));
134}
135
136STATIC CFStringRef
137copy_static_realm(CFDictionaryRef properties)
138{
139    CFStringRef	realm = NULL;
140
141    if (properties == NULL) {
142	return (NULL);
143    }
144    realm = isA_CFString(CFDictionaryGetValue(properties,
145					      kEAPClientPropEAPSIMAKARealm));
146    if (realm != NULL) {
147	CFRetain(realm);
148    }
149    return (realm);
150}
151
152STATIC CFStringRef
153copy_static_imsi(CFDictionaryRef properties)
154{
155    CFStringRef		imsi;
156
157    if (properties == NULL) {
158	return (NULL);
159    }
160    imsi = isA_CFString(CFDictionaryGetValue(properties,
161					     kEAPClientPropEAPSIMAKAIMSI));
162    if (imsi == NULL) {
163	return (NULL);
164    }
165    return (CFRetain(imsi));
166}
167
168STATIC CFStringRef
169copy_static_identity(CFDictionaryRef properties)
170{
171    CFStringRef		imsi;
172    CFStringRef		realm;
173    CFStringRef		ret_identity;
174
175    imsi = copy_static_imsi(properties);
176    if (imsi == NULL) {
177	return (NULL);
178    }
179    realm = copy_static_realm(properties);
180    ret_identity = copy_imsi_identity(imsi, realm);
181    my_CFRelease(&imsi);
182    my_CFRelease(&realm);
183    return (ret_identity);
184}
185
186#if TARGET_OS_EMBEDDED
187STATIC CFStringRef
188copy_pseudonym_identity(CFStringRef pseudonym, CFStringRef realm)
189{
190    if (realm != NULL) {
191	return (CFStringCreateWithFormat(NULL, NULL,
192					 CFSTR("%@" "@" "%@"),
193					 pseudonym, realm));
194    }
195    return (CFRetain(pseudonym));
196}
197
198STATIC CFStringRef
199create_identity(EAPSIMAKAPersistentStateRef persist,
200		EAPSIMAKAAttributeType requested_type,
201		CFStringRef realm,
202		Boolean * is_reauth_id_p)
203{
204    CFStringRef			ret_identity = NULL;
205
206    if (is_reauth_id_p != NULL) {
207	*is_reauth_id_p = FALSE;
208    }
209    if (persist == NULL) {
210	return (NULL);
211    }
212    if (requested_type == kAT_ANY_ID_REQ
213	|| requested_type == kAT_FULLAUTH_ID_REQ) {
214	CFStringRef		reauth_id;
215	CFStringRef		pseudonym;
216
217	reauth_id = EAPSIMAKAPersistentStateGetReauthID(persist);
218	pseudonym = EAPSIMAKAPersistentStateGetPseudonym(persist);
219	if (requested_type == kAT_ANY_ID_REQ && reauth_id != NULL) {
220	    if (is_reauth_id_p != NULL) {
221		*is_reauth_id_p = TRUE;
222	    }
223	    ret_identity = CFRetain(reauth_id);
224	}
225	else if (pseudonym != NULL) {
226	    ret_identity = copy_pseudonym_identity(pseudonym, realm);
227	}
228    }
229    if (ret_identity == NULL) {
230	/* use permanent id */
231	ret_identity
232	    = copy_imsi_identity(EAPSIMAKAPersistentStateGetIMSI(persist),
233				 realm);
234    }
235    return (ret_identity);
236}
237
238STATIC CFStringRef
239sim_identity_create(EAPSIMAKAPersistentStateRef persist,
240		    CFDictionaryRef properties,
241		    EAPSIMAKAAttributeType identity_type,
242		    Boolean * is_reauth_id_p)
243{
244    CFStringRef		realm = NULL;
245    CFStringRef		ret_identity = NULL;
246
247    if (is_reauth_id_p != NULL) {
248	*is_reauth_id_p = FALSE;
249    }
250    realm = copy_static_realm(properties);
251    if (realm == NULL) {
252	realm = SIMCopyRealm();
253    }
254    ret_identity = create_identity(persist, identity_type, realm,
255				   is_reauth_id_p);
256    my_CFRelease(&realm);
257    return (ret_identity);
258}
259
260#else /* TARGET_OS_EMBEDDED */
261
262STATIC CFStringRef
263sim_identity_create(EAPSIMAKAPersistentStateRef persist,
264		    CFDictionaryRef properties,
265		    EAPSIMAKAAttributeType identity_type,
266		    Boolean * is_reauth_id_p)
267{
268    if (is_reauth_id_p != NULL) {
269	*is_reauth_id_p = FALSE;
270    }
271    return (NULL);
272}
273
274#endif /* TARGET_OS_EMBEDDED */
275
276/**
277 ** Utility Routines
278 **/
279STATIC EAPSIMAKAAttributeType
280S_get_identity_type(CFDictionaryRef dict)
281{
282    CFStringRef			identity_type_str;
283
284    if (dict == NULL) {
285	identity_type_str = NULL;
286    }
287    else {
288	identity_type_str
289	    = CFDictionaryGetValue(dict, kEAPClientPropEAPSIMAKAIdentityType);
290	identity_type_str = isA_CFString(identity_type_str);
291    }
292    return (EAPSIMAKAIdentityTypeGetAttributeType(identity_type_str));
293}
294
295STATIC void
296AKAStaticKeysClear(AKAStaticKeysRef keys)
297{
298    bzero(keys, sizeof(*keys));
299    return;
300}
301
302STATIC void
303AKAStaticKeysRelease(AKAStaticKeysRef keys)
304{
305    my_CFRelease(&keys->res);
306    my_CFRelease(&keys->ck);
307    my_CFRelease(&keys->ik);
308    return;
309}
310
311STATIC bool
312AKAStaticKeysInitWithProperties(AKAStaticKeysRef keys,
313				CFDictionaryRef properties)
314{
315    CFDataRef	ck;
316    CFDataRef	ik;
317    CFDataRef	res;
318    bool	success = FALSE;
319
320    if (properties == NULL) {
321	goto done;
322    }
323    res = CFDictionaryGetValue(properties, kEAPClientPropEAPAKARES);
324    ck = CFDictionaryGetValue(properties, kEAPClientPropEAPAKACk);
325    ik = CFDictionaryGetValue(properties, kEAPClientPropEAPAKAIk);
326    if (res == NULL && ck == NULL && ik == NULL) {
327	goto done;
328    }
329    success = TRUE;
330    if (isA_CFData(ck) == NULL) {
331	EAPLOG_FL(LOG_NOTICE, "invalid/missing EAPAKACk property");
332	success = FALSE;
333    }
334    if (isA_CFData(ik) == NULL) {
335	EAPLOG_FL(LOG_NOTICE, "invalid/missing EAPAKAIk property");
336	success = FALSE;
337    }
338    if (isA_CFData(res) == NULL) {
339	EAPLOG_FL(LOG_NOTICE, "invalid/missing EAPAKARES property");
340	success = FALSE;
341    }
342    my_FieldSetRetainedCFType(&keys->ck, ck);
343    my_FieldSetRetainedCFType(&keys->ik, ik);
344    my_FieldSetRetainedCFType(&keys->res, res);
345
346 done:
347    return (success);
348}
349
350STATIC void
351EAPAKAContextSetLastIdentity(EAPAKAContextRef context, CFDataRef identity_data)
352{
353    if (identity_data != NULL) {
354	CFRetain(identity_data);
355    }
356    if (context->last_identity != NULL) {
357	CFRelease(context->last_identity);
358    }
359    context->last_identity = identity_data;
360    return;
361}
362
363STATIC void
364EAPAKAContextClear(EAPAKAContextRef context)
365{
366    bzero(context, sizeof(*context));
367    context->plugin_state = kEAPClientStateAuthenticating;
368    context->state = kEAPAKAClientStateNone;
369    context->previous_identifier = -1;
370    return;
371}
372
373STATIC void
374EAPAKAContextFree(EAPAKAContextRef context)
375{
376    EAPSIMAKAPersistentStateRelease(context->persist);
377    EAPAKAContextSetLastIdentity(context, NULL);
378    AKAStaticKeysRelease(&context->static_keys);
379    EAPAKAContextClear(context);
380    free(context);
381    return;
382}
383
384STATIC EAPPacketRef
385make_response_packet(EAPAKAContextRef context,
386		     EAPPacketRef in_pkt, EAPSIMAKAPacketSubtype subtype,
387		     TLVBufferRef tb_p)
388{
389    EAPAKAPacketRef	pkt;
390
391    pkt = (EAPAKAPacketRef)context->pkt;
392    TLVBufferInit(tb_p, pkt->attrs,
393		  sizeof(context->pkt) - offsetof(EAPAKAPacket, attrs));
394    pkt->code = kEAPCodeResponse;
395    pkt->identifier = in_pkt->identifier;
396    pkt->type = kEAPTypeEAPAKA;
397    pkt->subtype = subtype;
398    net_uint16_set(pkt->reserved, 0);
399    return ((EAPPacketRef)pkt);
400}
401
402STATIC EAPPacketRef
403make_client_error_packet(EAPAKAContextRef context,
404			 EAPPacketRef in_pkt, ClientErrorCode code)
405{
406    AttrUnion			attr;
407    EAPPacketRef		pkt;
408    TLVBufferDeclare(		tb_p);
409
410    pkt = make_response_packet(context, in_pkt,
411			       kEAPSIMAKAPacketSubtypeClientError, tb_p);
412    attr.tlv_p = TLVBufferAllocateTLV(tb_p, kAT_CLIENT_ERROR_CODE,
413				      sizeof(AT_CLIENT_ERROR_CODE));
414    if (attr.tlv_p == NULL) {
415	EAPLOG(LOG_NOTICE, "eapaka: failed allocating AT_CLIENT_ERROR_CODE, %s",
416	       TLVBufferErrorString(tb_p));
417	return (NULL);
418    }
419    net_uint16_set(attr.at_client_error_code->ce_client_error_code, code);
420    EAPPacketSetLength(pkt,
421		       offsetof(EAPAKAPacket, attrs) + TLVBufferUsed(tb_p));
422
423    return (pkt);
424}
425
426STATIC void
427save_persistent_state(EAPAKAContextRef context)
428{
429    CFStringRef		ssid = NULL;
430#if TARGET_OS_EMBEDDED
431    CFStringRef		trust_domain;
432
433    trust_domain = CFDictionaryGetValue(context->plugin->properties,
434	    				kEAPClientPropTLSTrustExceptionsDomain);
435    if (my_CFEqual(trust_domain, kEAPTLSTrustExceptionsDomainWirelessSSID)) {
436	ssid = CFDictionaryGetValue(context->plugin->properties,
437				    kEAPClientPropTLSTrustExceptionsID);
438    }
439#endif
440    EAPSIMAKAPersistentStateSave(context->persist, context->key_info_valid,
441				 ssid);
442    return;
443}
444
445STATIC EAPPacketRef
446eapaka_identity(EAPAKAContextRef context,
447		const EAPPacketRef in_pkt,
448		TLVListRef tlvs_p,
449		EAPClientStatus * client_status)
450{
451    CFStringRef		identity = NULL;
452    CFDataRef		identity_data = NULL;
453    EAPSIMAKAAttributeType identity_req_type;
454    EAPPacketRef	pkt = NULL;
455    Boolean		reauth_id_used = FALSE;
456    TLVBufferDeclare(	tb_p);
457
458    if (context->state != kEAPAKAClientStateIdentity) {
459	/* starting over */
460	context->plugin_state = kEAPClientStateAuthenticating;
461	context->identity_count = 0;
462	context->last_identity_type = 0;
463	context->state = kEAPAKAClientStateIdentity;
464    }
465    context->identity_count++;
466    if (context->identity_count > kEAPSIMAKAIdentityAttributesCount) {
467	EAPLOG(LOG_NOTICE, "eapaka: too many Identity packets (%d > %d)",
468	       context->identity_count, kEAPSIMAKAIdentityAttributesCount);
469	*client_status = kEAPClientStatusProtocolError;
470	goto done;
471    }
472    identity_req_type = TLVListLookupIdentityAttribute(tlvs_p);
473    switch (identity_req_type) {
474    case kAT_ANY_ID_REQ:
475	if (context->identity_count > 1) {
476	    EAPLOG(LOG_NOTICE,
477		   "eapaka: AT_ANY_ID_REQ at Identity #%d",
478		   context->identity_count);
479	    *client_status = kEAPClientStatusProtocolError;
480	    goto done;
481	}
482	break;
483    case kAT_FULLAUTH_ID_REQ:
484	if (context->identity_count > 1
485	    && context->last_identity_type != kAT_ANY_ID_REQ) {
486	    EAPLOG(LOG_NOTICE,
487		   "eapaka: AT_FULLAUTH_ID_REQ follows %s at Identity #%d",
488		   EAPSIMAKAAttributeTypeGetString(context->last_identity_type),
489		   context->identity_count);
490	    *client_status = kEAPClientStatusProtocolError;
491	    goto done;
492	}
493	break;
494    case kAT_PERMANENT_ID_REQ:
495	if (context->identity_count > 1
496	    && context->last_identity_type != kAT_ANY_ID_REQ
497	    && context->last_identity_type != kAT_FULLAUTH_ID_REQ) {
498	    EAPLOG(LOG_NOTICE,
499		   "eapaka: AT_PERMANENT_ID_REQ follows %s at Identity #%d",
500		   EAPSIMAKAAttributeTypeGetString(context->last_identity_type),
501		   context->identity_count);
502	    *client_status = kEAPClientStatusProtocolError;
503	    goto done;
504	}
505	break;
506    default:
507	EAPLOG(LOG_NOTICE, "eapaka: AKA-Identity missing *ID_REQ");
508	*client_status = kEAPClientStatusProtocolError;
509	goto done;
510	break;
511    }
512
513    /* create our response */
514    context->last_identity_type = identity_req_type;
515    pkt = make_response_packet(context, in_pkt,
516			       kEAPSIMAKAPacketSubtypeAKAIdentity, tb_p);
517    if (context->static_keys.ck != NULL) {
518	identity = copy_static_identity(context->plugin->properties);
519    }
520    else {
521	identity = sim_identity_create(context->persist,
522				       context->plugin->properties,
523				       identity_req_type, &reauth_id_used);
524    }
525    if (identity == NULL) {
526	EAPLOG(LOG_NOTICE, "eapaka: can't find SIM identity");
527	*client_status = kEAPClientStatusResourceUnavailable;
528	pkt = NULL;
529	goto done;
530    }
531    if (!TLVBufferAddIdentityString(tb_p, identity, &identity_data)) {
532	EAPLOG(LOG_NOTICE, "eapaka: can't add AT_IDENTITY, %s",
533	       TLVBufferErrorString(tb_p));
534	*client_status = kEAPClientStatusInternalError;
535	pkt = NULL;
536	goto done;
537    }
538    EAPAKAContextSetLastIdentity(context, identity_data);
539    my_CFRelease(&identity_data);
540
541    /* we didn't have a fast re-auth ID */
542    if (reauth_id_used == FALSE) {
543	context->key_info_valid = FALSE;
544    }
545
546    EAPPacketSetLength(pkt,
547		       offsetof(EAPAKAPacket, attrs) + TLVBufferUsed(tb_p));
548
549 done:
550    my_CFRelease(&identity);
551    return (pkt);
552}
553
554STATIC bool
555eapaka_challenge_process_encr_data(EAPAKAContextRef context, TLVListRef tlvs_p)
556{
557    uint8_t *		decrypted_buffer = NULL;
558    TLVListDeclare(	decrypted_tlvs_p);
559    AT_ENCR_DATA * 	encr_data_p;
560    AT_IV * 		iv_p;
561    CFStringRef		next_reauth_id;
562    CFStringRef		next_pseudonym;
563
564    TLVListInit(decrypted_tlvs_p);
565    encr_data_p = (AT_ENCR_DATA *)TLVListLookupAttribute(tlvs_p, kAT_ENCR_DATA);
566    if (encr_data_p == NULL) {
567	return (TRUE);
568    }
569    iv_p = (AT_IV *)TLVListLookupAttribute(tlvs_p, kAT_IV);
570    if (iv_p == NULL) {
571	EAPLOG(LOG_NOTICE,
572	       "eapaka: Challenge missing AT_IV");
573	return (FALSE);
574    }
575    decrypted_buffer
576	= EAPSIMAKAKeyInfoDecryptTLVList(&context->key_info, encr_data_p, iv_p,
577					 decrypted_tlvs_p);
578    if (decrypted_buffer == NULL) {
579	EAPLOG(LOG_NOTICE, "eapaka: Challenge decrypt AT_ENCR_DATA failed");
580	return (FALSE);
581    }
582    {
583	CFStringRef	str;
584
585	str = TLVListCopyDescription(decrypted_tlvs_p);
586	EAPLOG(-LOG_DEBUG, "Decrypted TLVs:\n%@", str);
587	CFRelease(str);
588    }
589
590    /* save the next fast re-auth id */
591    next_reauth_id = TLVListCreateStringFromAttribute(decrypted_tlvs_p,
592						      kAT_NEXT_REAUTH_ID);
593    if (next_reauth_id != NULL) {
594	EAPSIMAKAPersistentStateSetReauthID(context->persist,
595					    next_reauth_id);
596	CFRelease(next_reauth_id);
597    }
598    /* save the next pseudonym */
599    next_pseudonym = TLVListCreateStringFromAttribute(decrypted_tlvs_p,
600						      kAT_NEXT_PSEUDONYM);
601    if (next_pseudonym != NULL) {
602	EAPSIMAKAPersistentStateSetPseudonym(context->persist,
603					     next_pseudonym);
604	CFRelease(next_pseudonym);
605    }
606    if (decrypted_buffer != NULL) {
607	free(decrypted_buffer);
608    }
609    TLVListFree(decrypted_tlvs_p);
610    return (TRUE);
611}
612
613STATIC bool
614eapaka_authenticate(EAPAKAContextRef context,
615		    CFDataRef rand, CFDataRef autn, AKAAuthResultsRef results)
616{
617    bool	success;
618
619    if (context->static_keys.ck != NULL) {
620	/* use statically defined information */
621	AKAAuthResultsInit(results);
622	AKAAuthResultsSetCK(results, context->static_keys.ck);
623	AKAAuthResultsSetIK(results, context->static_keys.ik);
624	AKAAuthResultsSetRES(results, context->static_keys.res);
625	success = TRUE;
626    }
627    else {
628	success = SIMAuthenticateAKA(rand, autn, results);
629    }
630    return (success);
631
632}
633
634
635STATIC EAPPacketRef
636eapaka_challenge(EAPAKAContextRef context,
637		 const EAPPacketRef in_pkt,
638		 TLVListRef tlvs_p,
639		 EAPClientStatus * client_status)
640{
641    AKAAuthResults	aka_results;
642    CFDataRef		autn;
643    AT_AUTN *		autn_p;
644    bool		auth_success;
645    CFDataRef		ck;
646    CFDataRef		ik;
647    int			len;
648    AT_MAC *		mac_p;
649    EAPPacketRef	pkt = NULL;
650    AT_RAND *		rand_p;
651    CFDataRef		rand;
652    CFDataRef		res;
653    AT_RES *		res_p;
654    CC_SHA1_CTX		sha1_context;
655    TLVBufferDeclare(	tb_p);
656
657    AKAAuthResultsInit(&aka_results);
658    context->plugin_state = kEAPClientStateAuthenticating;
659    context->state = kEAPAKAClientStateChallenge;
660    EAPSIMAKAPersistentStateSetCounter(context->persist, 1); /* XXX */
661    context->reauth_success = FALSE;
662    rand_p = (AT_RAND *)TLVListLookupAttribute(tlvs_p, kAT_RAND);
663    if (rand_p == NULL) {
664	EAPLOG(LOG_NOTICE, "eapaka: Challenge is missing AT_RAND");
665	*client_status = kEAPClientStatusProtocolError;
666	goto done;
667    }
668    autn_p = (AT_AUTN *)TLVListLookupAttribute(tlvs_p, kAT_AUTN);
669    if (autn_p == NULL) {
670	EAPLOG(LOG_NOTICE, "eapaka: Challenge is missing AT_AUTN");
671	*client_status = kEAPClientStatusProtocolError;
672	goto done;
673    }
674    mac_p = (AT_MAC *)TLVListLookupAttribute(tlvs_p, kAT_MAC);
675    if (mac_p == NULL) {
676	EAPLOG(LOG_NOTICE,
677	       "eapaka: Challenge is missing AT_MAC");
678	*client_status = kEAPClientStatusProtocolError;
679	goto done;
680    }
681
682    /* get CK, IK, RES from the SIM */
683    rand = CFDataCreateWithBytesNoCopy(NULL, rand_p->ra_rand, RAND_SIZE,
684				       kCFAllocatorNull);
685    autn = CFDataCreateWithBytesNoCopy(NULL, autn_p->an_autn, AUTN_SIZE,
686				       kCFAllocatorNull);
687    auth_success = eapaka_authenticate(context, rand, autn, &aka_results);
688    CFRelease(rand);
689    CFRelease(autn);
690    if (auth_success == FALSE) {
691	*client_status = kEAPClientStatusInternalError;
692	goto done;
693    }
694    ck = aka_results.ck;
695    if (ck == NULL) {
696	CFDataRef		auts;
697	EAPSIMAKAPacketSubtype	subtype;
698
699	auts = aka_results.auts;
700	subtype = (auts != NULL)
701	    ? kEAPSIMAKAPacketSubtypeAKASynchronizationFailure
702	    : kEAPSIMAKAPacketSubtypeAKAAuthenticationReject;
703	pkt = make_response_packet(context, in_pkt, subtype, tb_p);
704	if (auts != NULL) {
705	    AT_AUTS *		auts_p;
706	    int			len;
707
708	    len = (int)CFDataGetLength(auts);
709	    if (len != AUTS_SIZE) {
710		EAPLOG(LOG_NOTICE,
711		       "eapaka: SIM bogus AUTS size %d (should be %d)",
712		       len, AUTS_SIZE);
713		*client_status = kEAPClientStatusInternalError;
714	    }
715	    auts_p = (AT_AUTS *)
716		TLVBufferAllocateTLV(tb_p, kAT_AUTS, sizeof(AT_AUTS));
717	    bcopy(CFDataGetBytePtr(auts), auts_p->as_auts, AUTS_SIZE);
718	}
719	EAPPacketSetLength(pkt,
720			   offsetof(EAPAKAPacket, attrs) + TLVBufferUsed(tb_p));
721	goto done;
722    }
723
724    /*
725     * generate the MK:
726     * MK = SHA1(Identity|IK|CK)
727     */
728    CC_SHA1_Init(&sha1_context);
729    if (context->last_identity != NULL) {
730	CC_SHA1_Update(&sha1_context, CFDataGetBytePtr(context->last_identity),
731		       (int)CFDataGetLength(context->last_identity));
732    }
733    else {
734	CC_SHA1_Update(&sha1_context, context->plugin->username,
735		       context->plugin->username_length);
736    }
737    ik = aka_results.ik;
738    CC_SHA1_Update(&sha1_context, CFDataGetBytePtr(ik),
739		   (int)CFDataGetLength(ik));
740    CC_SHA1_Update(&sha1_context, CFDataGetBytePtr(ck),
741		   (int)CFDataGetLength(ck));
742    CC_SHA1_Final(EAPSIMAKAPersistentStateGetMasterKey(context->persist),
743		  &sha1_context);
744
745    /* now run PRF to generate keying material */
746    fips186_2prf(EAPSIMAKAPersistentStateGetMasterKey(context->persist),
747		 context->key_info.key);
748
749    /* validate the MAC */
750    if (!EAPSIMAKAKeyInfoVerifyMAC(&context->key_info,
751				   in_pkt, mac_p->ma_mac, NULL, 0)) {
752	EAPLOG(LOG_NOTICE,
753	       "eapaka: Challenge AT_MAC not valid");
754	*client_status = kEAPClientStatusProtocolError;
755	goto done;
756    }
757
758    /* check for and process encrypted data */
759    if (eapaka_challenge_process_encr_data(context, tlvs_p) == FALSE) {
760	*client_status = kEAPClientStatusProtocolError;
761	goto done;
762    }
763
764    /* create our response */
765    pkt = make_response_packet(context, in_pkt,
766			       kEAPSIMAKAPacketSubtypeAKAChallenge, tb_p);
767    /* AT_MAC */
768    mac_p = (AT_MAC *)TLVBufferAllocateTLV(tb_p, kAT_MAC,
769					   sizeof(AT_MAC));
770    if (mac_p == NULL) {
771	EAPLOG(LOG_NOTICE, "eapaka: failed allocating AT_MAC, %s",
772	       TLVBufferErrorString(tb_p));
773	*client_status = kEAPClientStatusInternalError;
774	pkt = NULL;
775	goto done;
776    }
777    net_uint16_set(mac_p->ma_reserved, 0);
778
779    /* AT_RES */
780    res = aka_results.res;
781    len = (int)CFDataGetLength(res);
782    res_p = (AT_RES *)TLVBufferAllocateTLV(tb_p, kAT_RES,
783					   offsetof(AT_RES, rs_res) + len);
784    if (res_p == NULL) {
785	EAPLOG(LOG_NOTICE, "eapaka: failed allocating AT_RES, %s",
786	       TLVBufferErrorString(tb_p));
787	*client_status = kEAPClientStatusInternalError;
788	pkt = NULL;
789	goto done;
790    }
791#define NBITS_PER_BYTE	8
792    net_uint16_set(res_p->rs_res_length, len * NBITS_PER_BYTE);
793    bcopy(CFDataGetBytePtr(res), res_p->rs_res, len);
794
795    EAPPacketSetLength(pkt,
796		       offsetof(EAPAKAPacket, attrs) + TLVBufferUsed(tb_p));
797
798    /* compute/set the MAC value */
799    EAPSIMAKAKeyInfoSetMAC(&context->key_info, pkt, mac_p->ma_mac, NULL, 0);
800
801    /* as far as we're concerned, we're successful */
802    context->state = kEAPAKAClientStateSuccess;
803    context->key_info_valid = TRUE;
804    save_persistent_state(context);
805
806 done:
807    AKAAuthResultsRelease(&aka_results);
808    return (pkt);
809}
810
811STATIC void
812eapaka_compute_reauth_key(EAPAKAContextRef context,
813			  AT_COUNTER * counter_p,
814			  AT_NONCE_S * nonce_s_p)
815
816{
817    const void *	identity;
818    int			identity_length;
819
820    if (context->last_identity != NULL) {
821	identity = CFDataGetBytePtr(context->last_identity);
822	identity_length = (int)CFDataGetLength(context->last_identity);
823    }
824    else {
825	identity = context->plugin->username;
826	identity_length = context->plugin->username_length;
827    }
828    EAPSIMAKAKeyInfoComputeReauthKey(&context->key_info,
829				     context->persist,
830				     identity, identity_length,
831				     counter_p, nonce_s_p);
832    return;
833}
834
835#define ENCR_BUFSIZE 	(sizeof(AT_COUNTER) + sizeof(AT_COUNTER_TOO_SMALL))
836#define ENCR_BUFSIZE_R	AT_ENCR_DATA_ROUNDUP(ENCR_BUFSIZE)
837
838STATIC EAPPacketRef
839eapaka_reauthentication(EAPAKAContextRef context,
840			const EAPPacketRef in_pkt,
841			TLVListRef tlvs_p,
842			EAPClientStatus * client_status)
843{
844    uint16_t		at_counter;
845    AT_COUNTER *	counter_p;
846    bool		force_fullauth = FALSE;
847    uint8_t		encr_buffer[ENCR_BUFSIZE_R];
848    TLVBufferDeclare(	encr_tb_p);
849    uint8_t *		decrypted_buffer = NULL;
850    TLVListDeclare(	decrypted_tlvs_p);
851    AT_ENCR_DATA * 	encr_data_p;
852    AT_IV * 		iv_p;
853    AT_MAC *		mac_p;
854    CFStringRef		next_reauth_id;
855    AT_NONCE_S *	nonce_s_p;
856    EAPPacketRef	pkt = NULL;
857    CFStringRef		reauth_id = NULL;
858    TLVBufferDeclare(	tb_p);
859
860    TLVListInit(decrypted_tlvs_p);
861    if (context->key_info_valid == FALSE) {
862	EAPLOG(LOG_NOTICE,
863	       "eapaka: Reauthentication but no key info available");
864	*client_status = kEAPClientStatusProtocolError;
865	goto done;
866    }
867    reauth_id = EAPSIMAKAPersistentStateGetReauthID(context->persist);
868    if (reauth_id == NULL) {
869	EAPLOG(LOG_NOTICE,
870	       "eapaka: received Reauthentication but don't have reauth id");
871	*client_status = kEAPClientStatusProtocolError;
872	goto done;
873    }
874    context->state = kEAPAKAClientStateReauthentication;
875    context->plugin_state = kEAPClientStateAuthenticating;
876
877    /* validate the MAC */
878    mac_p = (AT_MAC *)TLVListLookupAttribute(tlvs_p, kAT_MAC);
879    if (mac_p == NULL) {
880	EAPLOG(LOG_NOTICE,
881	       "eapaka: Reauthentication is missing AT_MAC");
882	*client_status = kEAPClientStatusProtocolError;
883	goto done;
884    }
885    if (!EAPSIMAKAKeyInfoVerifyMAC(&context->key_info, in_pkt, mac_p->ma_mac,
886				   NULL, 0)) {
887	EAPLOG(LOG_NOTICE,
888	       "eapaka: Reauthentication AT_MAC not valid");
889	*client_status = kEAPClientStatusProtocolError;
890	goto done;
891    }
892
893    /* packet must contain AT_ENCR_DATA, AT_IV */
894    encr_data_p = (AT_ENCR_DATA *)TLVListLookupAttribute(tlvs_p, kAT_ENCR_DATA);
895    iv_p = (AT_IV *)TLVListLookupAttribute(tlvs_p, kAT_IV);
896    if (encr_data_p == NULL || iv_p == NULL) {
897	if (encr_data_p == NULL) {
898	    EAPLOG(LOG_NOTICE,
899		   "eapaka:  Reauthentication missing AT_ENCR_DATA");
900	}
901	if (iv_p == NULL) {
902	    EAPLOG(LOG_NOTICE,
903		   "eapaka:  Reauthentication missing AT_IV");
904	}
905	*client_status = kEAPClientStatusProtocolError;
906	goto done;
907    }
908    decrypted_buffer
909	= EAPSIMAKAKeyInfoDecryptTLVList(&context->key_info, encr_data_p, iv_p,
910					 decrypted_tlvs_p);
911    if (decrypted_buffer == NULL) {
912	EAPLOG(LOG_NOTICE,
913	       "eapaka: failed to decrypt Reauthentication AT_ENCR_DATA");
914	*client_status = kEAPClientStatusProtocolError;
915	goto done;
916    }
917    {
918	CFStringRef	str;
919
920	str = TLVListCopyDescription(decrypted_tlvs_p);
921	EAPLOG(-LOG_DEBUG, "Decrypted TLVs:\n%@", str);
922	CFRelease(str);
923    }
924
925    /* Reauthentication must contain AT_NONCE_S, AT_COUNTER */
926    nonce_s_p
927	= (AT_NONCE_S *)TLVListLookupAttribute(decrypted_tlvs_p, kAT_NONCE_S);
928    counter_p
929	= (AT_COUNTER *)TLVListLookupAttribute(decrypted_tlvs_p, kAT_COUNTER);
930    if (nonce_s_p == NULL || counter_p == NULL) {
931	if (nonce_s_p == NULL) {
932	    EAPLOG(LOG_NOTICE,
933		   "eapaka: Reauthentication AT_ENCR_DATA missing AT_NONCE_S");
934	}
935	if (counter_p == NULL) {
936	    EAPLOG(LOG_NOTICE,
937		   "eapaka: Reauthentication AT_ENCR_DATA missing AT_COUNTER");
938	}
939	*client_status = kEAPClientStatusProtocolError;
940	goto done;
941    }
942
943    /* check the at_counter */
944    at_counter = net_uint16_get(counter_p->co_counter);
945    if (at_counter < EAPSIMAKAPersistentStateGetCounter(context->persist)) {
946	force_fullauth = TRUE;
947    }
948    else {
949	/* save the next fast re-auth id */
950	next_reauth_id = TLVListCreateStringFromAttribute(decrypted_tlvs_p,
951							  kAT_NEXT_REAUTH_ID);
952	if (next_reauth_id != NULL) {
953	    EAPSIMAKAPersistentStateSetReauthID(context->persist,
954						next_reauth_id);
955	    CFRelease(next_reauth_id);
956	}
957	EAPSIMAKAPersistentStateSetCounter(context->persist, at_counter);
958    }
959
960    /* create our response */
961    pkt = make_response_packet(context, in_pkt,
962			       kEAPSIMAKAPacketSubtypeReauthentication, tb_p);
963
964    /*
965     * create nested attributes containing:
966     * 	AT_COUNTER
967     *  AT_COUNTER_TOO_SMALL (if necessary)
968     */
969    TLVBufferInit(encr_tb_p, encr_buffer, sizeof(encr_buffer));
970    if (TLVBufferAddCounter(encr_tb_p, at_counter) == FALSE) {
971	EAPLOG(LOG_NOTICE, "eapaka: failed allocating AT_COUNTER, %s",
972	       TLVBufferErrorString(tb_p));
973	*client_status = kEAPClientStatusInternalError;
974	pkt = NULL;
975	goto done;
976    }
977    if (force_fullauth
978	&& TLVBufferAddCounterTooSmall(encr_tb_p) == FALSE) {
979	EAPLOG(LOG_NOTICE,
980	       "eapaka: failed allocating AT_COUNTER_TOO_SMALL, %s",
981	       TLVBufferErrorString(tb_p));
982	*client_status = kEAPClientStatusInternalError;
983	pkt = NULL;
984	goto done;
985    }
986
987    /* AT_IV and AT_ENCR_DATA */
988    if (!EAPSIMAKAKeyInfoEncryptTLVs(&context->key_info, tb_p, encr_tb_p)) {
989	*client_status = kEAPClientStatusInternalError;
990	pkt = NULL;
991	goto done;
992    }
993
994    /* AT_MAC */
995    mac_p = (AT_MAC *)TLVBufferAllocateTLV(tb_p, kAT_MAC,
996					   sizeof(AT_MAC));
997    if (mac_p == NULL) {
998	EAPLOG(LOG_NOTICE, "eapaka: failed allocating AT_MAC, %s",
999	       TLVBufferErrorString(tb_p));
1000	*client_status = kEAPClientStatusInternalError;
1001	pkt = NULL;
1002	goto done;
1003    }
1004    net_uint16_set(mac_p->ma_reserved, 0);
1005
1006    /* set the packet length */
1007    EAPPacketSetLength(pkt,
1008		       offsetof(EAPSIMPacket, attrs) + TLVBufferUsed(tb_p));
1009
1010    /* compute/set the MAC value */
1011    EAPSIMAKAKeyInfoSetMAC(&context->key_info, pkt,
1012			   mac_p->ma_mac, nonce_s_p->nc_nonce_s,
1013			   sizeof(nonce_s_p->nc_nonce_s));
1014
1015    if (force_fullauth == FALSE) {
1016	/* as far as we're concerned, we're successful */
1017	context->state = kEAPAKAClientStateSuccess;
1018	eapaka_compute_reauth_key(context, counter_p, nonce_s_p);
1019	context->key_info_valid = TRUE;
1020	context->reauth_success = TRUE;
1021    }
1022    else {
1023	context->key_info_valid = FALSE;
1024    }
1025    save_persistent_state(context);
1026
1027 done:
1028    if (decrypted_buffer != NULL) {
1029	free(decrypted_buffer);
1030    }
1031    TLVListFree(decrypted_tlvs_p);
1032    return (pkt);
1033}
1034
1035#define ENCR_BUFSIZE_NOTIF 	(sizeof(AT_COUNTER))
1036#define ENCR_BUFSIZE_NOTIF_R	AT_ENCR_DATA_ROUNDUP(ENCR_BUFSIZE_NOTIF)
1037
1038STATIC EAPPacketRef
1039eapaka_notification(EAPAKAContextRef context,
1040		    const EAPPacketRef in_pkt,
1041		    TLVListRef tlvs_p,
1042		    EAPClientStatus * client_status,
1043		    EAPClientDomainSpecificError * error)
1044{
1045    bool		after_auth;
1046    uint16_t		current_at_counter;
1047    bool		do_replay_protection = FALSE;
1048    AT_NOTIFICATION *	notification_p;
1049    AT_MAC *		mac_p;
1050    uint16_t		notification_code = 0;
1051    EAPPacketRef	pkt = NULL;
1052    TLVBufferDeclare(	tb_p);
1053
1054    *client_status = kEAPClientStatusOK;
1055    *error = 0;
1056    notification_p =
1057	(AT_NOTIFICATION *)TLVListLookupAttribute(tlvs_p, kAT_NOTIFICATION);
1058
1059    if (notification_p == NULL) {
1060	EAPLOG(LOG_NOTICE, "eapaka: Notification does not contain "
1061	       "AT_NOTIFICATION attribute");
1062	*client_status = kEAPClientStatusProtocolError;
1063	goto done;
1064    }
1065
1066    notification_code = net_uint16_get(notification_p->nt_notification);
1067    after_auth = ATNotificationPhaseIsAfterAuthentication(notification_code);
1068    if (ATNotificationCodeIsSuccess(notification_code) && after_auth == FALSE) {
1069	EAPLOG(LOG_NOTICE,
1070	       "eapaka: Notification code '%d' indicates "
1071	       "success before authentication", notification_code);
1072	*client_status = kEAPClientStatusProtocolError;
1073	goto done;
1074    }
1075    /* validate the MAC */
1076    mac_p = (AT_MAC *)TLVListLookupAttribute(tlvs_p, kAT_MAC);
1077    if (mac_p == NULL) {
1078	if (after_auth) {
1079	    EAPLOG(LOG_NOTICE, "eapaka: Notification is missing AT_MAC");
1080	    *client_status = kEAPClientStatusProtocolError;
1081	    goto done;
1082	}
1083    }
1084    else {
1085	if (after_auth == FALSE) {
1086	    EAPLOG(LOG_NOTICE,
1087		   "eapaka: Notification incorrectly contains AT_MAC");
1088	    *client_status = kEAPClientStatusProtocolError;
1089	    goto done;
1090	}
1091
1092	if (!EAPSIMAKAKeyInfoVerifyMAC(&context->key_info, in_pkt,
1093				       mac_p->ma_mac, NULL, 0)) {
1094	    EAPLOG(LOG_NOTICE, "eapaka: Notification AT_MAC not valid");
1095	    *client_status = kEAPClientStatusProtocolError;
1096	    goto done;
1097	}
1098    }
1099    current_at_counter = EAPSIMAKAPersistentStateGetCounter(context->persist);
1100    do_replay_protection = context->reauth_success && after_auth;
1101    if (do_replay_protection) {
1102	uint16_t	at_counter;
1103	uint8_t *	decrypted_buffer;
1104	AT_ENCR_DATA *	encr_data_p;
1105	TLVListDeclare(	decrypted_tlvs_p);
1106	bool		has_counter = FALSE;
1107	AT_IV * 	iv_p;
1108
1109	encr_data_p
1110	    = (AT_ENCR_DATA *)TLVListLookupAttribute(tlvs_p, kAT_ENCR_DATA);
1111	iv_p = (AT_IV *)TLVListLookupAttribute(tlvs_p, kAT_IV);
1112	if (encr_data_p == NULL || iv_p == NULL) {
1113	    EAPLOG(LOG_NOTICE,
1114		   "eapaka: Notification after re-auth missing "
1115		   "AT_ENCR_DATA (%p) or AT_IV (%p)", encr_data_p, iv_p);
1116	    *client_status = kEAPClientStatusProtocolError;
1117	    goto done;
1118	}
1119	TLVListInit(decrypted_tlvs_p);
1120	decrypted_buffer
1121	    = EAPSIMAKAKeyInfoDecryptTLVList(&context->key_info,
1122					     encr_data_p, iv_p,
1123					     decrypted_tlvs_p);
1124	if (decrypted_buffer != NULL) {
1125	    AT_COUNTER *	counter_p;
1126	    CFStringRef		str;
1127
1128	    str = TLVListCopyDescription(decrypted_tlvs_p);
1129	    EAPLOG(-LOG_DEBUG, "Decrypted TLVs:\n%@", str);
1130	    CFRelease(str);
1131
1132	    counter_p = (AT_COUNTER *)TLVListLookupAttribute(decrypted_tlvs_p,
1133							     kAT_COUNTER);
1134	    if (counter_p != NULL) {
1135		at_counter = net_uint16_get(counter_p->co_counter);
1136		has_counter = TRUE;
1137	    }
1138	    free(decrypted_buffer);
1139	    TLVListFree(decrypted_tlvs_p);
1140	}
1141	else {
1142	    EAPLOG(LOG_NOTICE,
1143		   "eapaka: failed to decrypt Notification AT_ENCR_DATA");
1144	    *client_status = kEAPClientStatusInternalError;
1145	    goto done;
1146	}
1147	if (!has_counter) {
1148	    EAPLOG(LOG_NOTICE,
1149		   "eapaka:  Notification AT_ENCR_DATA missing AT_COUNTER");
1150	    *client_status = kEAPClientStatusProtocolError;
1151	    goto done;
1152	}
1153	if (at_counter != current_at_counter) {
1154	    EAPLOG(LOG_NOTICE, "eapaka: Notification AT_COUNTER (%d) does not "
1155		   "match current counter (%d)", at_counter,
1156		   current_at_counter);
1157	    *client_status = kEAPClientStatusProtocolError;
1158	    goto done;
1159	}
1160    }
1161
1162    /* create our response */
1163    pkt = make_response_packet(context, in_pkt,
1164			       kEAPSIMAKAPacketSubtypeNotification,
1165			       tb_p);
1166    if (do_replay_protection) {
1167	uint8_t			encr_buffer[ENCR_BUFSIZE_NOTIF_R];
1168	TLVBufferDeclare(	encr_tb_p);
1169
1170	/*
1171	 * create nested attributes containing:
1172	 *    AT_COUNTER
1173	 */
1174	TLVBufferInit(encr_tb_p, encr_buffer, sizeof(encr_buffer));
1175	if (TLVBufferAddCounter(encr_tb_p, current_at_counter) == FALSE) {
1176	    EAPLOG(LOG_NOTICE, "eapaka: failed to allocate AT_COUNTER, %s",
1177		   TLVBufferErrorString(encr_tb_p));
1178	    *client_status = kEAPClientStatusAllocationFailed;
1179	    goto done;
1180	}
1181
1182	/* AT_IV and AT_ENCR_DATA */
1183	if (!EAPSIMAKAKeyInfoEncryptTLVs(&context->key_info, tb_p, encr_tb_p)) {
1184	    *client_status = kEAPClientStatusInternalError;
1185	    pkt = NULL;
1186	    goto done;
1187	}
1188    }
1189    if (mac_p != NULL) {
1190	/* AT_MAC */
1191	mac_p = (AT_MAC *)TLVBufferAllocateTLV(tb_p, kAT_MAC, sizeof(AT_MAC));
1192	if (mac_p == NULL) {
1193	    EAPLOG(LOG_NOTICE, "eapaka: failed allocating AT_MAC, %s",
1194		   TLVBufferErrorString(tb_p));
1195	    *client_status = kEAPClientStatusAllocationFailed;
1196	    pkt = NULL;
1197	    goto done;
1198	}
1199	net_uint16_set(mac_p->ma_reserved, 0);
1200    }
1201
1202    /* set the packet length */
1203    EAPPacketSetLength(pkt,
1204		       offsetof(EAPSIMPacket, attrs) + TLVBufferUsed(tb_p));
1205    if (mac_p != NULL) {
1206	/* compute/set the MAC value */
1207	EAPSIMAKAKeyInfoSetMAC(&context->key_info, pkt, mac_p->ma_mac, NULL, 0);
1208    }
1209    if (ATNotificationCodeIsSuccess(notification_code)) {
1210	context->state = kEAPAKAClientStateSuccess;
1211    }
1212    else {
1213	const char *	str;
1214
1215	context->state = kEAPAKAClientStateFailure;
1216	*client_status = kEAPClientStatusPluginSpecificError;
1217	*error = EAPSIMAKAStatusForATNotificationCode(notification_code);
1218	str = ATNotificationCodeGetString(notification_code);
1219	if (str == NULL) {
1220	    EAPLOG(LOG_NOTICE,
1221		   "eapaka: Notification code '%d' unrecognized failure",
1222		   notification_code);
1223	}
1224	else {
1225	    EAPLOG(LOG_NOTICE, "eapaka: Notification: %s", str);
1226	}
1227    }
1228
1229 done:
1230    return (pkt);
1231}
1232
1233STATIC EAPPacketRef
1234eapaka_request(EAPAKAContextRef context,
1235	       const EAPPacketRef in_pkt,
1236	       EAPClientStatus * client_status,
1237	       EAPClientDomainSpecificError * error)
1238{
1239    EAPAKAPacketRef	eapaka_in = (EAPAKAPacketRef)in_pkt;
1240    EAPPacketRef	eapaka_out = NULL;
1241    uint16_t		in_length = EAPPacketGetLength(in_pkt);
1242    uint8_t		subtype;
1243    TLVListDeclare(	tlvs_p);
1244
1245    TLVListInit(tlvs_p);
1246    if (in_length <= kEAPSIMAKAPacketHeaderLength) {
1247	EAPLOG_FL(LOG_NOTICE, "length %d <= %ld",
1248		  in_length, kEAPSIMAKAPacketHeaderLength);
1249	*client_status = kEAPClientStatusProtocolError;
1250	goto done;
1251    }
1252    if (TLVListParse(tlvs_p, eapaka_in->attrs,
1253		     in_length - kEAPSIMAKAPacketHeaderLength) == FALSE) {
1254	EAPLOG_FL(LOG_NOTICE, "parse failed: %s",
1255		  TLVListErrorString(tlvs_p));
1256	*client_status = kEAPClientStatusProtocolError;
1257	goto done;
1258    }
1259    if (context->state != kEAPAKAClientStateNone
1260	&& context->previous_identifier == in_pkt->identifier) {
1261	/* re-send our previous response */
1262	return ((EAPPacketRef)context->pkt);
1263    }
1264    subtype = eapaka_in->subtype;
1265    switch (subtype) {
1266    case kEAPSIMAKAPacketSubtypeAKAChallenge:
1267	eapaka_out = eapaka_challenge(context, in_pkt, tlvs_p, client_status);
1268	break;
1269    case kEAPSIMAKAPacketSubtypeAKAIdentity:
1270	eapaka_out = eapaka_identity(context, in_pkt, tlvs_p, client_status);
1271	break;
1272    case kEAPSIMAKAPacketSubtypeNotification:
1273	eapaka_out = eapaka_notification(context, in_pkt, tlvs_p,
1274					 client_status, error);
1275	break;
1276    case kEAPSIMAKAPacketSubtypeReauthentication:
1277	eapaka_out
1278	    = eapaka_reauthentication(context, in_pkt, tlvs_p, client_status);
1279	break;
1280    default:
1281	*client_status = kEAPClientStatusProtocolError;
1282	EAPLOG_FL(LOG_NOTICE, "unexpected Subtype %s",
1283		  EAPSIMAKAPacketSubtypeGetString(subtype));
1284	*client_status = kEAPClientStatusProtocolError;
1285	goto done;
1286    }
1287
1288 done:
1289    TLVListFree(tlvs_p);
1290    if (*client_status != kEAPClientStatusOK) {
1291	context->plugin_state = kEAPClientStateFailure;
1292	context->state = kEAPAKAClientStateFailure;
1293    }
1294    if (eapaka_out == NULL
1295	&& *client_status == kEAPClientStatusProtocolError) {
1296	eapaka_out
1297	    = make_client_error_packet(context, in_pkt,
1298				       kClientErrorCodeUnableToProcessPacket);
1299    }
1300    if (eapaka_out != NULL) {
1301	context->previous_identifier = in_pkt->identifier;
1302    }
1303    return (eapaka_out);
1304
1305}
1306
1307/**
1308 ** EAP-AKA module functions
1309 **/
1310
1311/*
1312 * Declare these here to ensure that the compiler
1313 * generates appropriate errors/warnings
1314 */
1315EAPClientPluginFuncIntrospect eapaka_introspect;
1316STATIC EAPClientPluginFuncVersion eapaka_version;
1317STATIC EAPClientPluginFuncEAPType eapaka_type;
1318STATIC EAPClientPluginFuncEAPName eapaka_name;
1319STATIC EAPClientPluginFuncInit eapaka_init;
1320STATIC EAPClientPluginFuncFree eapaka_free;
1321STATIC EAPClientPluginFuncProcess eapaka_process;
1322STATIC EAPClientPluginFuncFreePacket eapaka_free_packet;
1323STATIC EAPClientPluginFuncSessionKey eapaka_session_key;
1324STATIC EAPClientPluginFuncServerKey eapaka_server_key;
1325STATIC EAPClientPluginFuncMasterSessionKeyCopyBytes eapaka_msk_copy_bytes;
1326STATIC EAPClientPluginFuncPublishProperties eapaka_publish_props;
1327STATIC EAPClientPluginFuncUserName eapaka_user_name_copy;
1328STATIC EAPClientPluginFuncCopyIdentity eapaka_copy_identity;
1329STATIC EAPClientPluginFuncCopyPacketDescription eapaka_copy_packet_description;
1330
1331
1332STATIC EAPClientStatus
1333eapaka_init(EAPClientPluginDataRef plugin, CFArrayRef * require_props,
1334	    EAPClientDomainSpecificError * error)
1335{
1336    EAPAKAContextRef		context = NULL;
1337    EAPSIMAKAAttributeType 	identity_type;
1338    CFStringRef			imsi = NULL;
1339    AKAStaticKeys		static_keys;
1340
1341    AKAStaticKeysClear(&static_keys);
1342    if (AKAStaticKeysInitWithProperties(&static_keys, plugin->properties)) {
1343	imsi = copy_static_imsi(plugin->properties);
1344	if (imsi == NULL) {
1345	    AKAStaticKeysRelease(&static_keys);
1346	    return (kEAPClientStatusConfigurationInvalid);
1347	}
1348	EAPLOG(LOG_NOTICE, "EAP-AKA: using static information");
1349    }
1350    else {
1351	/* check for a SIM module */
1352	imsi = SIMCopyIMSI();
1353	if (imsi == NULL) {
1354	    EAPLOG(LOG_NOTICE, "EAP-AKA: no SIM available");
1355	    return (kEAPClientStatusResourceUnavailable);
1356	}
1357	EAPLOG(LOG_NOTICE, "EAP-AKA: SIM found");
1358    }
1359
1360    /* allocate a context */
1361    context = (EAPAKAContextRef)malloc(sizeof(*context));
1362    if (context == NULL) {
1363	CFRelease(imsi);
1364	AKAStaticKeysRelease(&static_keys);
1365	return (kEAPClientStatusAllocationFailed);
1366    }
1367    EAPAKAContextClear(context);
1368    context->static_keys = static_keys;
1369    identity_type = S_get_identity_type(plugin->properties);
1370    context->persist
1371	= EAPSIMAKAPersistentStateCreate(kEAPTypeEAPAKA,
1372					 CC_SHA1_DIGEST_LENGTH,
1373					 imsi, identity_type);
1374    CFRelease(imsi);
1375    if (EAPSIMAKAPersistentStateGetReauthID(context->persist) != NULL) {
1376	/* now run PRF to generate keying material */
1377	fips186_2prf(EAPSIMAKAPersistentStateGetMasterKey(context->persist),
1378		     context->key_info.key);
1379	context->key_info_valid = TRUE;
1380    }
1381    context->plugin = plugin;
1382    plugin->private = context;
1383    return (kEAPClientStatusOK);
1384}
1385
1386STATIC void
1387eapaka_free(EAPClientPluginDataRef plugin)
1388{
1389    EAPAKAContextFree(plugin->private);
1390    plugin->private = NULL;
1391    return;
1392}
1393
1394STATIC void
1395eapaka_free_packet(EAPClientPluginDataRef plugin, EAPPacketRef arg)
1396{
1397    return;
1398}
1399
1400STATIC EAPClientState
1401eapaka_process(EAPClientPluginDataRef plugin,
1402	       const EAPPacketRef in_pkt,
1403	       EAPPacketRef * out_pkt_p,
1404	       EAPClientStatus * client_status,
1405	       EAPClientDomainSpecificError * error)
1406{
1407    EAPAKAContextRef	context = (EAPAKAContextRef)plugin->private;
1408
1409    *client_status = kEAPClientStatusOK;
1410    *error = 0;
1411    switch (in_pkt->code) {
1412    case kEAPCodeRequest:
1413	*out_pkt_p = eapaka_request(context, in_pkt, client_status, error);
1414	break;
1415    case kEAPCodeSuccess:
1416	context->previous_identifier = -1;
1417	if (context->state == kEAPAKAClientStateSuccess) {
1418	    context->plugin_state = kEAPClientStateSuccess;
1419	}
1420	break;
1421    case kEAPCodeFailure:
1422	context->previous_identifier = -1;
1423	context->plugin_state = kEAPClientStateFailure;
1424	break;
1425    default:
1426	break;
1427    }
1428    return (context->plugin_state);
1429}
1430
1431STATIC const char *
1432eapaka_failure_string(EAPClientPluginDataRef plugin)
1433{
1434    return (NULL);
1435}
1436
1437STATIC void *
1438eapaka_session_key(EAPClientPluginDataRef plugin, int * key_length)
1439{
1440    EAPAKAContextRef	context = (EAPAKAContextRef)plugin->private;
1441
1442    if (context->state == kEAPAKAClientStateSuccess
1443	&& context->key_info_valid) {
1444	*key_length = 32;
1445	return (context->key_info.s.msk);
1446    }
1447    return (NULL);
1448}
1449
1450STATIC void *
1451eapaka_server_key(EAPClientPluginDataRef plugin, int * key_length)
1452{
1453    EAPAKAContextRef	context = (EAPAKAContextRef)plugin->private;
1454
1455    if (context->state == kEAPAKAClientStateSuccess
1456	&& context->key_info_valid) {
1457	*key_length = 32;
1458	return (context->key_info.s.msk + 32);
1459    }
1460    return (NULL);
1461}
1462
1463STATIC int
1464eapaka_msk_copy_bytes(EAPClientPluginDataRef plugin,
1465		      void * msk, int msk_size)
1466{
1467    EAPAKAContextRef	context = (EAPAKAContextRef)plugin->private;
1468    int			ret_msk_size = sizeof(context->key_info.s.msk);
1469
1470    if (msk_size < ret_msk_size
1471	|| context->key_info_valid == FALSE
1472	|| context->state != kEAPAKAClientStateSuccess) {
1473	ret_msk_size = 0;
1474    }
1475    else {
1476	bcopy(context->key_info.s.msk, msk, ret_msk_size);
1477    }
1478    return (ret_msk_size);
1479}
1480
1481STATIC CFDictionaryRef
1482eapaka_publish_props(EAPClientPluginDataRef plugin)
1483{
1484    return (NULL);
1485}
1486
1487STATIC CFStringRef
1488eapaka_user_name_copy(CFDictionaryRef properties)
1489{
1490    EAPSIMAKAAttributeType 	identity_type;
1491    CFStringRef			imsi;
1492    EAPSIMAKAPersistentStateRef persist;
1493    CFStringRef			ret_identity;
1494
1495    ret_identity = copy_static_identity(properties);
1496    if (ret_identity != NULL) {
1497	return (ret_identity);
1498    }
1499    imsi = SIMCopyIMSI();
1500    if (imsi == NULL) {
1501	return (NULL);
1502    }
1503    identity_type = S_get_identity_type(properties);
1504    persist = EAPSIMAKAPersistentStateCreate(kEAPTypeEAPAKA,
1505					     CC_SHA1_DIGEST_LENGTH,
1506					     imsi, identity_type);
1507    CFRelease(imsi);
1508    if (persist != NULL) {
1509	ret_identity = sim_identity_create(persist, properties,
1510					   identity_type, NULL);
1511	EAPSIMAKAPersistentStateRelease(persist);
1512    }
1513    return (ret_identity);
1514}
1515
1516/*
1517 * Function: eapaka_copy_identity
1518 * Purpose:
1519 *   Return the current identity we should use in responding to an
1520 *   EAP Request Identity packet.
1521 */
1522STATIC CFStringRef
1523eapaka_copy_identity(EAPClientPluginDataRef plugin)
1524{
1525    EAPAKAContextRef	context = (EAPAKAContextRef)plugin->private;
1526
1527    EAPAKAContextSetLastIdentity(context, NULL);
1528    context->state = kEAPAKAClientStateNone;
1529    context->previous_identifier = -1;
1530    if (context->static_keys.ck != NULL) {
1531	return (copy_static_identity(plugin->properties));
1532    }
1533    return (sim_identity_create(context->persist, plugin->properties,
1534				kAT_ANY_ID_REQ, NULL));
1535}
1536
1537STATIC CFStringRef
1538eapaka_copy_packet_description(const EAPPacketRef pkt, bool * packet_is_valid)
1539{
1540    return (EAPSIMAKAPacketCopyDescription(pkt, packet_is_valid));
1541}
1542
1543STATIC EAPType
1544eapaka_type(void)
1545{
1546    return (kEAPTypeEAPAKA);
1547
1548}
1549
1550STATIC const char *
1551eapaka_name(void)
1552{
1553    return (EAP_AKA_NAME);
1554}
1555
1556STATIC EAPClientPluginVersion
1557eapaka_version(void)
1558{
1559    return (kEAPClientPluginVersion);
1560}
1561
1562STATIC struct func_table_ent {
1563    const char *		name;
1564    void *			func;
1565} func_table[] = {
1566#if 0
1567    { kEAPClientPluginFuncNameIntrospect, eapaka_introspect },
1568#endif /* 0 */
1569    { kEAPClientPluginFuncNameVersion, eapaka_version },
1570    { kEAPClientPluginFuncNameEAPType, eapaka_type },
1571    { kEAPClientPluginFuncNameEAPName, eapaka_name },
1572    { kEAPClientPluginFuncNameInit, eapaka_init },
1573    { kEAPClientPluginFuncNameFree, eapaka_free },
1574    { kEAPClientPluginFuncNameProcess, eapaka_process },
1575    { kEAPClientPluginFuncNameFreePacket, eapaka_free_packet },
1576    { kEAPClientPluginFuncNameFailureString, eapaka_failure_string },
1577    { kEAPClientPluginFuncNameSessionKey, eapaka_session_key },
1578    { kEAPClientPluginFuncNameServerKey, eapaka_server_key },
1579    { kEAPClientPluginFuncNameMasterSessionKeyCopyBytes,
1580      eapaka_msk_copy_bytes },
1581    { kEAPClientPluginFuncNamePublishProperties, eapaka_publish_props },
1582    { kEAPClientPluginFuncNameUserName, eapaka_user_name_copy },
1583    { kEAPClientPluginFuncNameCopyIdentity, eapaka_copy_identity },
1584    { kEAPClientPluginFuncNameCopyPacketDescription,
1585      eapaka_copy_packet_description },
1586    { NULL, NULL},
1587};
1588
1589
1590EAPClientPluginFuncRef
1591eapaka_introspect(EAPClientPluginFuncName name)
1592{
1593    struct func_table_ent * scan;
1594
1595    for (scan = func_table; scan->name != NULL; scan++) {
1596	if (strcmp(name, scan->name) == 0) {
1597	    return (scan->func);
1598	}
1599    }
1600    return (NULL);
1601}
1602
1603#ifdef TEST_EAPAKA_PLUGIN
1604
1605STATIC uint8_t S_res_static[] = {
1606    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
1607};
1608STATIC uint8_t S_ck_static[] = {
1609    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
1610};
1611STATIC uint8_t S_ik_static[] = {
1612    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27
1613};
1614#define RAND_STATIC					\
1615    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,	\
1616	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37
1617
1618STATIC uint8_t S_rand_static[RAND_SIZE] = {
1619    RAND_STATIC
1620};
1621
1622#define AUTN_STATIC					\
1623    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,	\
1624	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47
1625
1626STATIC uint8_t S_autn_static[AUTN_SIZE] = {
1627    AUTN_STATIC
1628};
1629
1630STATIC void
1631add_data_to_dict(CFMutableDictionaryRef dict, CFStringRef key,
1632		 const uint8_t * val, int val_len)
1633{
1634    CFDataRef	data;
1635
1636    data = CFDataCreateWithBytesNoCopy(NULL, val, val_len, kCFAllocatorNull);
1637    CFDictionarySetValue(dict, key, data);
1638    CFRelease(data);
1639    return;
1640}
1641
1642STATIC CFDictionaryRef
1643make_props(void)
1644{
1645    CFMutableDictionaryRef	dict;
1646
1647    dict = CFDictionaryCreateMutable(NULL, 0,
1648				     &kCFTypeDictionaryKeyCallBacks,
1649				     &kCFTypeDictionaryValueCallBacks);
1650    CFDictionarySetValue(dict, kEAPClientPropEAPSIMAKAIMSI,
1651			 CFSTR("244070100000001"));
1652    CFDictionarySetValue(dict, kEAPClientPropEAPSIMAKARealm,
1653			 CFSTR("eapaka.foo"));
1654    add_data_to_dict(dict, kEAPClientPropEAPAKARES,
1655		     S_res_static, sizeof(S_res_static));
1656    add_data_to_dict(dict, kEAPClientPropEAPAKACk,
1657		     S_ck_static, sizeof(S_ck_static));
1658    add_data_to_dict(dict, kEAPClientPropEAPAKAIk,
1659		     S_ik_static, sizeof(S_ik_static));
1660    return (dict);
1661}
1662
1663
1664/*
1665  EAP-Request/AKA/AKA-Identity AT_ANY_ID_REQ
1666
1667  01                   ; Code: Request
1668  01                   ; Identifier: 1
1669  00 0c                ; Length: 12 octets
1670  17                   ; Type: EAP-AKA
1671  05                ; subtype: AKA-Identity
1672  00 00             ; (reserved)
1673  0d                ; Attribute type: AT_ANY_ID_REQ (13 = d)
1674  01             ; Attribute length: 4 octets (1*4)
1675  00 00          ; (attribute padding)
1676*/
1677const uint8_t	eap_request_aka_identity_any_id[] = {
1678    0x01,
1679    0x01,
1680    0x00, 0x0c,
1681    0x17,
1682    0x05,
1683    0x00, 0x00,
1684    /* AT_ANY_ID_REQ */
1685    0x0d,
1686    0x01,
1687    0x00, 0x00
1688};
1689
1690/*
1691  EAP-Request/AKA/AKA-Identity AT_FULLAUTH_ID_REQ
1692
1693  01                   ; Code: Request
1694  01                   ; Identifier: 2
1695  00 0c                ; Length: 12 octets
1696  17                   ; Type: EAP-AKA
1697  05                ; subtype: AKA-Identity
1698  00 00             ; (reserved)
1699  0d                ; Attribute type: AT_FULLAUTH_ID_REQ (17 = 0x11)
1700  01             ; Attribute length: 4 octets (1*4)
1701  00 00          ; (attribute padding)
1702*/
1703const uint8_t	eap_request_aka_identity_fullauth_id[] = {
1704    0x01,
1705    0x02,
1706    0x00, 0x0c,
1707    0x17,
1708    0x05,
1709    0x00, 0x00,
1710    /* AT_FULLAUTH_ID_REQ */
1711    0x11,
1712    0x01,
1713    0x00, 0x00
1714};
1715
1716/*
1717  EAP-Request/AKA/AKA-Identity AT_PERMANENT_ID_REQ
1718
1719  01                   ; Code: Request
1720  01                   ; Identifier: 3
1721  00 0c                ; Length: 12 octets
1722  17                   ; Type: EAP-AKA
1723  05                ; subtype: AKA-Identity
1724  00 00             ; (reserved)
1725  0d                ; Attribute type: AT_PERMANENT_ID_REQ (10 = b)
1726  01             ; Attribute length: 4 octets (1*4)
1727  00 00          ; (attribute padding)
1728*/
1729const uint8_t	eap_request_aka_identity_permanent_id[] = {
1730    0x01,
1731    0x03,
1732    0x00, 0x0c,
1733    0x17,
1734    0x05,
1735    0x00, 0x00,
1736    /* AT_PERMANENT_ID_REQ */
1737    0x0a,
1738    0x01,
1739    0x00, 0x00
1740};
1741
1742STATIC const uint8_t *	test_identity_good[] = {
1743    eap_request_aka_identity_any_id,
1744    eap_request_aka_identity_fullauth_id,
1745    eap_request_aka_identity_permanent_id,
1746};
1747STATIC const uint8_t *	test_identity_bad1[] = {
1748    eap_request_aka_identity_any_id,
1749    eap_request_aka_identity_fullauth_id,
1750    eap_request_aka_identity_permanent_id,
1751    eap_request_aka_identity_any_id,
1752};
1753STATIC const uint8_t *	test_identity_bad2[] = {
1754    eap_request_aka_identity_fullauth_id,
1755    eap_request_aka_identity_any_id,
1756};
1757STATIC const uint8_t *	test_identity_bad3[] = {
1758    eap_request_aka_identity_fullauth_id,
1759    eap_request_aka_identity_permanent_id,
1760    eap_request_aka_identity_any_id,
1761};
1762STATIC const uint8_t *	test_identity_good2[] = {
1763    eap_request_aka_identity_any_id,
1764};
1765
1766#define countof(array)	(sizeof(array) / sizeof(array[0]))
1767
1768typedef struct {
1769    const char *	name;
1770    const uint8_t * *	packet_list;
1771    int			packet_count;
1772    bool		expect_failure;
1773} TestPacketList, * TestPacketListRef;
1774
1775STATIC TestPacketList S_tests[] = {
1776    {
1777	"good", test_identity_good, countof(test_identity_good), FALSE
1778    },
1779    {
1780	"bad1", test_identity_bad1, countof(test_identity_bad1), TRUE
1781    },
1782    {
1783	"bad2", test_identity_bad2, countof(test_identity_bad2), TRUE
1784    },
1785    {
1786	"bad3", test_identity_bad3, countof(test_identity_bad3), TRUE
1787    },
1788    {
1789	"good2", test_identity_good2, countof(test_identity_good2), FALSE
1790    }
1791};
1792
1793
1794STATIC bool
1795process_packets(EAPClientPluginDataRef data, TestPacketListRef test)
1796{
1797    EAPClientState			client_state;
1798    EAPClientDomainSpecificError 	error;
1799    bool				got_failure = FALSE;
1800    int					i;
1801    EAPPacketRef			out_pkt;
1802    EAPClientStatus			status;
1803
1804    for (i = 0; i < test->packet_count; i++) {
1805	printf("\nReceive packet:\n");
1806	EAPSIMAKAPacketDump(stdout, (EAPPacketRef)(test->packet_list[i]));
1807	out_pkt = NULL;
1808	client_state
1809	    = eapaka_process(data,
1810			     (EAPPacketRef)(test->packet_list[i]),
1811			     &out_pkt,
1812			     &status,
1813			     &error);
1814	if (client_state == kEAPClientStateFailure) {
1815	    got_failure = TRUE;
1816	}
1817	else {
1818	    if (out_pkt != NULL) {
1819		printf("\nSend packet:\n");
1820		EAPSIMAKAPacketDump(stdout, out_pkt);
1821	    }
1822	}
1823    }
1824    if (got_failure != test->expect_failure) {
1825	fprintf(stderr, "%s: process packet %s unexpectedly\n",
1826		test->name, got_failure ? "failed" : "succeeded");
1827	return (FALSE);
1828    }
1829    return (TRUE);
1830}
1831
1832STATIC char S_packet_buffer[1500];
1833
1834STATIC EAPPacketRef
1835make_request_packet(int identifier,
1836		    EAPSIMAKAPacketSubtype subtype,
1837		    TLVBufferRef tb_p)
1838{
1839    EAPAKAPacketRef	pkt;
1840
1841    pkt = (EAPAKAPacketRef)S_packet_buffer;
1842    TLVBufferInit(tb_p, pkt->attrs,
1843		  sizeof(S_packet_buffer) - offsetof(EAPAKAPacket, attrs));
1844    pkt->code = kEAPCodeRequest;
1845    pkt->identifier = identifier;
1846    pkt->type = kEAPTypeEAPAKA;
1847    pkt->subtype = subtype;
1848    net_uint16_set(pkt->reserved, 0);
1849    return ((EAPPacketRef)pkt);
1850
1851}
1852
1853STATIC void
1854send_challenge(EAPClientPluginDataRef data, CFStringRef identity)
1855
1856{
1857    AT_AUTN *		autn_p;
1858    CFDataRef		identity_data;
1859    EAPSIMAKAKeyInfo	key_info;
1860    AT_MAC *		mac_p;
1861    uint8_t		master_key[CC_SHA1_DIGEST_LENGTH];
1862    EAPPacketRef	pkt;
1863    AT_RAND *		rand_p;
1864    CC_SHA1_CTX		sha1_context;
1865    TestPacketList 	test;
1866    TLVBufferDeclare(	tb_p);
1867
1868    pkt = make_request_packet(4, kEAPSIMAKAPacketSubtypeAKAChallenge, tb_p);
1869    rand_p = (AT_RAND *)
1870	TLVBufferAllocateTLV(tb_p, kAT_RAND, sizeof(AT_RAND));
1871    bcopy(S_rand_static, rand_p->ra_rand, RAND_SIZE);
1872    autn_p = (AT_AUTN *)
1873	TLVBufferAllocateTLV(tb_p, kAT_AUTN, sizeof(AT_AUTN));
1874    bcopy(S_autn_static, autn_p->an_autn, AUTN_SIZE);
1875
1876    /* AT_MAC */
1877    mac_p = (AT_MAC *)TLVBufferAllocateTLV(tb_p, kAT_MAC,
1878					   sizeof(AT_MAC));
1879    if (mac_p == NULL) {
1880	fprintf(stderr, "failed allocating AT_MAC, %s",
1881		TLVBufferErrorString(tb_p));
1882	return;
1883    }
1884    net_uint16_set(mac_p->ma_reserved, 0);
1885
1886    /*
1887     * generate the MK:
1888     * MK = SHA1(Identity|IK|CK)
1889     */
1890    identity_data
1891	= CFStringCreateExternalRepresentation(NULL, identity,
1892					       kCFStringEncodingUTF8, 0);
1893    CC_SHA1_Init(&sha1_context);
1894    CC_SHA1_Update(&sha1_context, CFDataGetBytePtr(identity_data),
1895		   CFDataGetLength(identity_data));
1896    CC_SHA1_Update(&sha1_context, S_ik_static, sizeof(S_ik_static));
1897    CC_SHA1_Update(&sha1_context, S_ck_static, sizeof(S_ck_static));
1898    CC_SHA1_Final(master_key, &sha1_context);
1899    CFRelease(identity_data);
1900
1901    /* now run PRF to generate keying material */
1902    fips186_2prf(master_key, key_info.key);
1903    EAPPacketSetLength(pkt,
1904		       offsetof(EAPAKAPacket, attrs) + TLVBufferUsed(tb_p));
1905
1906    /* set the MAC value */
1907    EAPSIMAKAKeyInfoSetMAC(&key_info, pkt, mac_p->ma_mac, NULL, 0);
1908
1909    test.name = "challenge";
1910    test.packet_list = (const uint8_t * *)&pkt;
1911    test.packet_count = 1;
1912    test.expect_failure = FALSE;
1913
1914    if (process_packets(data, &test)) {
1915	printf("Test challenge: PASSED\n");
1916    }
1917    else {
1918	fprintf(stderr, "Test challenge: FAILED\n");
1919    }
1920    return;
1921}
1922
1923int
1924main()
1925{
1926    EAPClientState			client_state;
1927    EAPClientPluginData			data;
1928    EAPClientDomainSpecificError 	error;
1929    CFStringRef				last_identity = NULL;
1930    int					i;
1931    CFArrayRef				require_props;
1932    EAPClientStatus			status;
1933
1934    bzero(&data, sizeof(data));
1935    *((CFDictionaryRef *)&data.properties) = make_props();
1936    status = eapaka_init(&data, &require_props, &error);
1937    if (status != kEAPClientStatusOK) {
1938	fprintf(stderr, "eapaka_init failed %d\n", status);
1939	exit(1);
1940    }
1941    for (i = 0; i < countof(S_tests); i++) {
1942	/* this flushes the identity state */
1943	my_CFRelease(&last_identity);
1944	last_identity = eapaka_copy_identity(&data);
1945	if (process_packets(&data, S_tests + i) == FALSE) {
1946	    fprintf(stderr, "Test %s: FAILED\n", S_tests[i].name);
1947	}
1948	else {
1949	    printf("Test %s: PASSED\n", S_tests[i].name);
1950	}
1951    }
1952    if (last_identity == NULL) {
1953	fprintf(stderr, "why is last_identity NULL?");
1954	exit(1);
1955    }
1956    send_challenge(&data, last_identity);
1957    exit(0);
1958    return (0);
1959}
1960#endif /* TEST_EAPAKA_PLUGIN */
1961