1/*
2 * Copyright (c) 2008-2013 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 * eapsim_plugin.c
26 * - EAP-SIM client
27 */
28
29/*
30 * Modification History
31 *
32 * December 8, 2008	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_SIM_NAME		"EAP-SIM"
67
68#define kEAPClientPropEAPSIMIMSI			\
69    CFSTR("EAPSIMIMSI") 		/* string */
70#define kEAPClientPropEAPSIMRealm			\
71    CFSTR("EAPSIMRealm") 		/* string */
72#define kEAPClientPropEAPSIMNumberOfRANDs				\
73    CFSTR("EAPSIMNumberOfRANDs") 	/* number 2 or 3, default 3 */
74
75/*
76 * kEAPClientPropEAPSIMKcList
77 * kEAPClientPropEAPSIMSRESList
78 * kEAPClientPropEAPSIMRANDList
79 * - static (Kc, SRES, RAND) triplets used for testing
80 * - these are parallel arrays and must all be defined and have exactly the
81 *   same number of elements of the correct size and type
82 */
83
84#define kEAPClientPropEAPSIMKcList		CFSTR("EAPSIMKcList") /* array[data] */
85#define kEAPClientPropEAPSIMSRESList		CFSTR("EAPSIMSRESList") /* array[data] */
86#define kEAPClientPropEAPSIMRANDList		CFSTR("EAPSIMSRANDList") /* array[data] */
87
88/*
89 * Declare these here to ensure that the compiler
90 * generates appropriate errors/warnings
91 */
92EAPClientPluginFuncIntrospect eapsim_introspect;
93STATIC EAPClientPluginFuncVersion eapsim_version;
94STATIC EAPClientPluginFuncEAPType eapsim_type;
95STATIC EAPClientPluginFuncEAPName eapsim_name;
96STATIC EAPClientPluginFuncInit eapsim_init;
97STATIC EAPClientPluginFuncFree eapsim_free;
98STATIC EAPClientPluginFuncProcess eapsim_process;
99STATIC EAPClientPluginFuncFreePacket eapsim_free_packet;
100STATIC EAPClientPluginFuncSessionKey eapsim_session_key;
101STATIC EAPClientPluginFuncServerKey eapsim_server_key;
102STATIC EAPClientPluginFuncPublishProperties eapsim_publish_props;
103STATIC EAPClientPluginFuncUserName eapsim_user_name_copy;
104STATIC EAPClientPluginFuncCopyIdentity eapsim_copy_identity;
105STATIC EAPClientPluginFuncCopyPacketDescription eapsim_copy_packet_description;
106
107/**
108 ** Protocol-specific defines
109 **/
110
111enum {
112    kEAPSIMClientStateNone = 0,
113    kEAPSIMClientStateStart = 1,
114    kEAPSIMClientStateChallenge = 2,
115    kEAPSIMClientStateReauthentication = 3,
116    kEAPSIMClientStateSuccess = 4,
117    kEAPSIMClientStateFailure = 5
118};
119typedef int	EAPSIMClientState;
120
121typedef struct {
122    CFArrayRef			kc;
123    CFArrayRef			sres;
124    CFArrayRef			rand;
125} SIMStaticTriplets, * SIMStaticTripletsRef;
126
127/*
128 * Type: EAPSIMContext
129 * Purpose:
130 *   Holds the EAP-SIM module's context private data.
131 */
132typedef struct {
133    EAPClientPluginDataRef 	plugin;
134    EAPClientState		plugin_state;
135    EAPSIMClientState		state;
136    int				previous_identifier;
137    int				start_count;
138    int				n_required_rands;
139    EAPSIMAKAAttributeType	last_identity_type;
140    CFDataRef			last_identity;
141    SIMStaticTriplets		sim_static;
142    EAPSIMAKAKeyInfo		key_info;
143    bool			key_info_valid;
144    uint8_t			nonce_mt[NONCE_MT_SIZE];
145    EAPSIMAKAPersistentStateRef	persist;
146    bool			reauth_success;
147    uint16_t *			version_list;
148    int				version_list_count;
149    uint8_t			pkt[1500];
150} EAPSIMContext, *EAPSIMContextRef;
151
152/**
153 ** Identity routines
154 **/
155STATIC CFStringRef
156copy_imsi_identity(CFStringRef imsi, CFStringRef realm)
157{
158
159    if (realm != NULL) {
160	return (CFStringCreateWithFormat(NULL, NULL,
161					 CFSTR("1" "%@" "@" "%@"),
162					 imsi, realm));
163    }
164    return (CFStringCreateWithFormat(NULL, NULL, CFSTR("1" "%@"),
165				     imsi));
166}
167
168STATIC CFStringRef
169copy_static_realm(CFDictionaryRef properties)
170{
171    CFStringRef	realm = NULL;
172
173    if (properties == NULL) {
174	return (NULL);
175    }
176    realm = isA_CFString(CFDictionaryGetValue(properties,
177					      kEAPClientPropEAPSIMRealm));
178    if (realm == NULL) {
179	realm
180	    = isA_CFString(CFDictionaryGetValue(properties,
181						kEAPClientPropEAPSIMAKARealm));
182	if (realm == NULL) {
183	    return (NULL);
184	}
185    }
186    return (CFRetain(realm));
187}
188
189STATIC CFStringRef
190copy_static_imsi(CFDictionaryRef properties)
191{
192    CFStringRef		imsi;
193
194    if (properties == NULL) {
195	return (NULL);
196    }
197    imsi = isA_CFString(CFDictionaryGetValue(properties,
198					     kEAPClientPropEAPSIMIMSI));
199    if (imsi == NULL) {
200	imsi = isA_CFString(CFDictionaryGetValue(properties,
201						 kEAPClientPropEAPSIMAKAIMSI));
202	if (imsi == NULL) {
203	    return (NULL);
204	}
205    }
206    return (CFRetain(imsi));
207}
208
209STATIC CFStringRef
210copy_static_identity(CFDictionaryRef properties)
211{
212    CFStringRef		imsi;
213    CFStringRef		realm;
214    CFStringRef		ret_identity;
215
216    imsi = copy_static_imsi(properties);
217    if (imsi == NULL) {
218	return (NULL);
219    }
220    realm = copy_static_realm(properties);
221    ret_identity = copy_imsi_identity(imsi, realm);
222    my_CFRelease(&imsi);
223    my_CFRelease(&realm);
224    return (ret_identity);
225}
226
227#if TARGET_OS_EMBEDDED
228
229STATIC CFStringRef
230copy_pseudonym_identity(CFStringRef pseudonym, CFStringRef realm)
231{
232    if (realm != NULL) {
233	return (CFStringCreateWithFormat(NULL, NULL,
234					 CFSTR("%@" "@" "%@"),
235					 pseudonym, realm));
236    }
237    return (CFRetain(pseudonym));
238}
239
240STATIC CFStringRef
241create_identity(EAPSIMAKAPersistentStateRef persist,
242		EAPSIMAKAAttributeType requested_type,
243		CFStringRef realm,
244		Boolean * is_reauth_id_p)
245{
246    CFStringRef			ret_identity = NULL;
247
248    if (is_reauth_id_p != NULL) {
249	*is_reauth_id_p = FALSE;
250    }
251    if (persist == NULL) {
252	return (NULL);
253    }
254    if (requested_type == kAT_ANY_ID_REQ
255	|| requested_type == kAT_FULLAUTH_ID_REQ) {
256	CFStringRef		reauth_id;
257	CFStringRef		pseudonym;
258
259	reauth_id = EAPSIMAKAPersistentStateGetReauthID(persist);
260	pseudonym = EAPSIMAKAPersistentStateGetPseudonym(persist);
261	if (requested_type == kAT_ANY_ID_REQ && reauth_id != NULL) {
262	    if (is_reauth_id_p != NULL) {
263		*is_reauth_id_p = TRUE;
264	    }
265	    ret_identity = CFRetain(reauth_id);
266	}
267	else if (pseudonym != NULL) {
268	    ret_identity = copy_pseudonym_identity(pseudonym, realm);
269	}
270    }
271    if (ret_identity == NULL) {
272	/* use permanent id */
273	ret_identity
274	    = copy_imsi_identity(EAPSIMAKAPersistentStateGetIMSI(persist),
275				 realm);
276    }
277    return (ret_identity);
278}
279
280STATIC CFStringRef
281sim_identity_create(EAPSIMAKAPersistentStateRef persist,
282		    CFDictionaryRef properties,
283		    EAPSIMAKAAttributeType identity_type,
284		    Boolean * is_reauth_id_p)
285{
286    CFStringRef		realm = NULL;
287    CFStringRef		ret_identity = NULL;
288
289    if (is_reauth_id_p != NULL) {
290	*is_reauth_id_p = FALSE;
291    }
292    realm = copy_static_realm(properties);
293    if (realm == NULL) {
294	realm = SIMCopyRealm();
295    }
296    ret_identity = create_identity(persist, identity_type, realm,
297				   is_reauth_id_p);
298    my_CFRelease(&realm);
299    return (ret_identity);
300}
301
302#else /* TARGET_OS_EMBEDDED */
303
304STATIC CFStringRef
305sim_identity_create(EAPSIMAKAPersistentStateRef persist,
306		    CFDictionaryRef properties,
307		    EAPSIMAKAAttributeType identity_type,
308		    Boolean * is_reauth_id_p)
309{
310    if (is_reauth_id_p != NULL) {
311	*is_reauth_id_p = FALSE;
312    }
313    return (NULL);
314}
315
316#endif /* TARGET_OS_EMBEDDED */
317
318/**
319 ** Utility Routines
320 **/
321static int
322S_get_plist_int(CFDictionaryRef plist, CFStringRef key, int def)
323{
324    CFNumberRef 	n;
325    int			ret = def;
326
327    n = isA_CFNumber(CFDictionaryGetValue(plist, key));
328    if (n != NULL) {
329	if (CFNumberGetValue(n, kCFNumberIntType, &ret) == FALSE) {
330	    ret = def;
331	}
332    }
333    return (ret);
334}
335
336STATIC bool
337blocks_are_duplicated(const uint8_t * blocks, int n_blocks, int block_size)
338{
339    int 		i;
340    int			j;
341    const uint8_t *	scan;
342
343    for (i = 0, scan = blocks; i < (n_blocks - 1); i++, scan += block_size) {
344	const uint8_t *	scan_j = scan + block_size;
345
346	for (j = i + 1; j < n_blocks; j++, scan_j += block_size) {
347	    if (bcmp(scan, scan_j, block_size) == 0) {
348		return (TRUE);
349	    }
350	}
351    }
352    return (FALSE);
353}
354
355STATIC void
356fill_with_random(uint8_t * buf, int len)
357{
358    int             i;
359    int             n;
360    void *          p;
361    uint32_t        random;
362
363    n = len / sizeof(random);
364    for (i = 0, p = buf; i < n; i++, p += sizeof(random)) {
365        random = arc4random();
366        bcopy(&random, p, sizeof(random));
367    }
368    return;
369}
370
371STATIC EAPSIMAKAAttributeType
372S_get_identity_type(CFDictionaryRef dict)
373{
374    CFStringRef			identity_type_str;
375
376    if (dict == NULL) {
377	identity_type_str = NULL;
378    }
379    else {
380	identity_type_str
381	    = CFDictionaryGetValue(dict, kEAPClientPropEAPSIMAKAIdentityType);
382	identity_type_str = isA_CFString(identity_type_str);
383    }
384    return (EAPSIMAKAIdentityTypeGetAttributeType(identity_type_str));
385}
386
387STATIC void
388EAPSIMContextSetLastIdentity(EAPSIMContextRef context, CFDataRef identity_data)
389{
390    if (identity_data != NULL) {
391	CFRetain(identity_data);
392    }
393    if (context->last_identity != NULL) {
394	CFRelease(context->last_identity);
395    }
396    context->last_identity = identity_data;
397    return;
398}
399
400STATIC void
401EAPSIMContextSetVersionList(EAPSIMContextRef context,
402			    const void * list, int count)
403{
404    if (list != NULL) {
405	int	size = count * sizeof(uint16_t);
406
407	if (context->version_list != NULL
408	    && count == context->version_list_count
409	    && bcmp(context->version_list, list, size) == 0) {
410	    /* current list is the same, no need to copy it */
411	}
412	else {
413	    if (context->version_list != NULL) {
414		free(context->version_list);
415	    }
416	    context->version_list = (uint16_t *)malloc(size);
417	    bcopy(list, context->version_list, size);
418	    context->version_list_count = count;
419	}
420    }
421    else if (context->version_list != NULL) {
422	free(context->version_list);
423	context->version_list = NULL;
424	context->version_list_count = 0;
425    }
426    return;
427}
428
429STATIC void
430EAPSIMContextClear(EAPSIMContextRef context)
431{
432    bzero(context, sizeof(*context));
433    context->plugin_state = kEAPClientStateAuthenticating;
434    context->state = kEAPSIMClientStateNone;
435    context->previous_identifier = -1;
436    return;
437}
438
439STATIC CFArrayRef
440copy_data_array(CFDictionaryRef properties, CFStringRef prop_name,
441		int data_size)
442{
443    int			count;
444    int			i;
445    CFArrayRef		list;
446
447    if (properties == NULL) {
448	return (NULL);
449    }
450    list = CFDictionaryGetValue(properties, prop_name);
451    if (isA_CFArray(list) == NULL) {
452	return (NULL);
453    }
454    count = CFArrayGetCount(list);
455    if (count == 0) {
456	return (NULL);
457    }
458    for (i = 0; i < count; i++) {
459	CFDataRef		data = CFArrayGetValueAtIndex(list, i);
460
461	if (isA_CFData(data) == NULL
462	    || CFDataGetLength(data) != data_size) {
463	    return (NULL);
464	}
465    }
466    return (CFRetain(list));
467}
468
469STATIC bool
470SIMStaticTripletsInitFromProperties(SIMStaticTripletsRef sim_static_p,
471				    CFDictionaryRef properties)
472{
473    int			count;
474    CFArrayRef		kc;
475    CFArrayRef		rand;
476    CFArrayRef		sres;
477
478    my_CFRelease(&sim_static_p->kc);
479    my_CFRelease(&sim_static_p->sres);
480    my_CFRelease(&sim_static_p->rand);
481    if (properties == NULL) {
482	return (FALSE);
483    }
484    kc = copy_data_array(properties,
485			 kEAPClientPropEAPSIMKcList,
486			 SIM_KC_SIZE);
487    sres = copy_data_array(properties,
488			   kEAPClientPropEAPSIMSRESList,
489			   SIM_SRES_SIZE);
490    rand = copy_data_array(properties,
491			   kEAPClientPropEAPSIMRANDList,
492			   SIM_RAND_SIZE);
493
494    /*
495     * If SRES and Kc are specified, use statically defined values.
496     * If RAND is NULL, use the first element of the Kc/SRES arrays.
497     * If RAND is not NULL, RAND, Kc, and SRES must be parallel arrays;
498     * the RAND value provided by the server is looked up in the RAND array
499     * and the corresponding Kc and SRES value is retrieved.
500     */
501    if (kc == NULL || sres == NULL) {
502        goto failed;
503    }
504    count = CFArrayGetCount(kc);
505    if (count != CFArrayGetCount(sres)
506	|| (rand != NULL && count != CFArrayGetCount(rand))) {
507        /* they need to be parallel arrays */
508        goto failed;
509    }
510    sim_static_p->kc = kc;
511    sim_static_p->sres = sres;
512    sim_static_p->rand = rand;
513    return (TRUE);
514
515 failed:
516    my_CFRelease(&kc);
517    my_CFRelease(&sres);
518    my_CFRelease(&rand);
519    return (FALSE);
520}
521
522STATIC void
523EAPSIMContextFree(EAPSIMContextRef context)
524{
525    EAPSIMContextSetVersionList(context, NULL, 0);
526    SIMStaticTripletsInitFromProperties(&context->sim_static, NULL);
527    EAPSIMAKAPersistentStateRelease(context->persist);
528    EAPSIMContextSetLastIdentity(context, NULL);
529    EAPSIMContextClear(context);
530    free(context);
531    return;
532}
533
534STATIC int
535EAPSIMContextLookupStaticRAND(EAPSIMContextRef context,
536			      const uint8_t rand[SIM_RAND_SIZE])
537{
538    int		count;
539    int		i;
540
541    if (context->sim_static.rand == NULL) {
542	return (-1);
543    }
544    count = CFArrayGetCount(context->sim_static.rand);
545    for (i = 0; i < count; i++) {
546	CFDataRef	data;
547
548	data = CFArrayGetValueAtIndex(context->sim_static.rand, i);
549	if (bcmp(rand, CFDataGetBytePtr(data), SIM_RAND_SIZE) == 0) {
550	    return (i);
551	}
552    }
553    return (-1);
554}
555
556STATIC bool
557EAPSIMContextSIMProcessRAND(EAPSIMContextRef context,
558			    const uint8_t * rand_p, int count,
559			    uint8_t * kc_p, uint8_t * sres_p)
560{
561    int			i;
562    uint8_t *		kc_scan;
563    const uint8_t *	rand_scan;
564    uint8_t *		sres_scan;
565
566    if (context->sim_static.kc != NULL) {
567        /* use the static SIM information */
568        rand_scan = rand_p;
569        kc_scan = kc_p;
570        sres_scan = sres_p;
571        for (i = 0; i < count; i++) {
572            CFDataRef	kc_data;
573            CFDataRef	sres_data;
574            int		where;
575
576            if (context->sim_static.rand != NULL) {
577                where = EAPSIMContextLookupStaticRAND(context, rand_scan);
578                if (where == -1) {
579                    EAPLOG(LOG_NOTICE, "eapsim: can't find static RAND value");
580                    return (FALSE);
581                }
582            }
583	    else {
584                /* If RAND is NULL, choose first KC and SRES */
585                where = 0;
586            }
587
588            kc_data = CFArrayGetValueAtIndex(context->sim_static.kc, where);
589            bcopy(CFDataGetBytePtr(kc_data), kc_scan, SIM_KC_SIZE);
590            sres_data = CFArrayGetValueAtIndex(context->sim_static.sres, where);
591            bcopy(CFDataGetBytePtr(sres_data), sres_scan, SIM_SRES_SIZE);
592
593            /* move to the next element */
594            rand_scan += SIM_RAND_SIZE;
595            kc_scan += SIM_KC_SIZE;
596            sres_scan += SIM_SRES_SIZE;
597        }
598    }
599    else {
600        /* ask the SIM to get the (Kc, SRES) pairs from the RAND's */
601        if (SIMAuthenticateGSM(rand_p, count, kc_p, sres_p) == FALSE) {
602            EAPLOG(LOG_NOTICE, "SIMAuthenticateGSM failed");
603            return (FALSE);
604        }
605    }
606    return (TRUE);
607}
608
609/**
610 ** EAP-SIM module functions
611 **/
612STATIC EAPClientStatus
613eapsim_init(EAPClientPluginDataRef plugin, CFArrayRef * require_props,
614	    EAPClientDomainSpecificError * error)
615{
616    EAPSIMContextRef		context = NULL;
617    EAPSIMAKAAttributeType 	identity_type;
618    CFStringRef			imsi = NULL;
619    SIMStaticTriplets		triplets;
620
621    /* for testing, allow static triplets to override a real SIM */
622    bzero(&triplets, sizeof(triplets));
623    if (SIMStaticTripletsInitFromProperties(&triplets,
624					    plugin->properties)) {
625	imsi = copy_static_imsi(plugin->properties);
626	if (imsi == NULL) {
627	    EAPLOG(LOG_NOTICE,
628		   "eapsim: static triplets specified but IMSI missing");
629	    SIMStaticTripletsInitFromProperties(&triplets, NULL);
630	    return (kEAPClientStatusConfigurationInvalid);
631	}
632    }
633    else {
634	/* check for a real SIM module */
635	imsi = SIMCopyIMSI();
636	if (imsi == NULL) {
637	    EAPLOG(LOG_NOTICE, "EAP-SIM: no SIM available");
638	    return (kEAPClientStatusResourceUnavailable);
639	}
640	EAPLOG(LOG_NOTICE, "EAP-SIM: SIM found");
641    }
642
643    /* allocate a context */
644    context = (EAPSIMContextRef)malloc(sizeof(*context));
645    if (context == NULL) {
646	CFRelease(imsi);
647	(void)SIMStaticTripletsInitFromProperties(&triplets, NULL);
648	return (kEAPClientStatusAllocationFailed);
649    }
650    EAPSIMContextClear(context);
651    identity_type = S_get_identity_type(plugin->properties);
652    context->persist
653	= EAPSIMAKAPersistentStateCreate(kEAPTypeEAPSIM,
654					 CC_SHA1_DIGEST_LENGTH,
655					 imsi, identity_type);
656    CFRelease(imsi);
657    context->sim_static = triplets;
658    context->n_required_rands
659	= S_get_plist_int(plugin->properties,
660			  kEAPClientPropEAPSIMNumberOfRANDs,
661			  EAPSIM_MAX_RANDS);
662    if (context->n_required_rands != 2
663	&& context->n_required_rands != 3) {
664	EAPLOG(LOG_NOTICE,
665	       "eapsim: EAPSIMNumberOfRands %d is invalid, using 3 instead",
666	       context->n_required_rands);
667	context->n_required_rands = EAPSIM_MAX_RANDS;
668    }
669    if (EAPSIMAKAPersistentStateGetReauthID(context->persist) != NULL) {
670	/* now run PRF to generate keying material */
671	fips186_2prf(EAPSIMAKAPersistentStateGetMasterKey(context->persist),
672		     context->key_info.key);
673	context->key_info_valid = TRUE;
674    }
675    context->plugin = plugin;
676    plugin->private = context;
677    return (kEAPClientStatusOK);
678}
679
680STATIC void
681eapsim_free(EAPClientPluginDataRef plugin)
682{
683    EAPSIMContextFree(plugin->private);
684    plugin->private = NULL;
685    return;
686}
687
688STATIC void
689eapsim_free_packet(EAPClientPluginDataRef plugin, EAPPacketRef arg)
690{
691    return;
692}
693
694STATIC EAPPacketRef
695eapsim_make_response(EAPSIMContextRef context,
696		     EAPPacketRef in_pkt, EAPSIMAKAPacketSubtype subtype,
697		     TLVBufferRef tb_p)
698{
699    EAPSIMPacketRef	pkt;
700
701    pkt = (EAPSIMPacketRef)context->pkt;
702    TLVBufferInit(tb_p, pkt->attrs,
703		  sizeof(context->pkt) - offsetof(EAPSIMPacket, attrs));
704    pkt->code = kEAPCodeResponse;
705    pkt->identifier = in_pkt->identifier;
706    pkt->type = kEAPTypeEAPSIM;
707    pkt->subtype = subtype;
708    net_uint16_set(pkt->reserved, 0);
709    return ((EAPPacketRef)pkt);
710}
711
712STATIC EAPPacketRef
713eapsim_make_client_error(EAPSIMContextRef context,
714			 EAPPacketRef in_pkt, ClientErrorCode code)
715{
716    AttrUnion			attr;
717    EAPPacketRef		pkt;
718    TLVBufferDeclare(		tb_p);
719
720    pkt = eapsim_make_response(context, in_pkt,
721			       kEAPSIMAKAPacketSubtypeClientError, tb_p);
722    attr.tlv_p = TLVBufferAllocateTLV(tb_p, kAT_CLIENT_ERROR_CODE,
723				      sizeof(AT_CLIENT_ERROR_CODE));
724    if (attr.tlv_p == NULL) {
725	EAPLOG(LOG_NOTICE, "eapsim: failed allocating AT_CLIENT_ERROR_CODE, %s",
726	       TLVBufferErrorString(tb_p));
727	return (NULL);
728    }
729    net_uint16_set(attr.at_client_error_code->ce_client_error_code, code);
730    EAPPacketSetLength(pkt,
731		       offsetof(EAPSIMPacket, attrs) + TLVBufferUsed(tb_p));
732
733    return (pkt);
734}
735
736STATIC void
737save_persistent_state(EAPSIMContextRef context)
738{
739    CFStringRef		ssid = NULL;
740#if TARGET_OS_EMBEDDED
741    CFStringRef		trust_domain;
742
743    trust_domain = CFDictionaryGetValue(context->plugin->properties,
744	    				kEAPClientPropTLSTrustExceptionsDomain);
745    if (my_CFEqual(trust_domain, kEAPTLSTrustExceptionsDomainWirelessSSID)) {
746	ssid = CFDictionaryGetValue(context->plugin->properties,
747				    kEAPClientPropTLSTrustExceptionsID);
748    }
749#endif
750    EAPSIMAKAPersistentStateSave(context->persist, context->key_info_valid,
751				 ssid);
752    return;
753}
754
755STATIC EAPPacketRef
756eapsim_start(EAPSIMContextRef context,
757	     const EAPPacketRef in_pkt,
758	     TLVListRef tlvs_p,
759	     EAPClientStatus * client_status)
760{
761    AttrUnion		attr;
762    int			count;
763    int			i;
764    CFStringRef		identity = NULL;
765    EAPSIMAKAAttributeType identity_req_type;
766    bool		good_version = FALSE;
767    EAPPacketRef	pkt = NULL;
768    const uint8_t *	scan;
769    bool		skip_identity = FALSE;
770    TLVBufferDeclare(	tb_p);
771    AT_VERSION_LIST *	version_list_p;
772
773    version_list_p = (AT_VERSION_LIST *)
774	TLVListLookupAttribute(tlvs_p, kAT_VERSION_LIST);
775    if (version_list_p == NULL) {
776	EAPLOG(LOG_NOTICE, "eapsim: Start is missing AT_VERSION_LIST");
777	*client_status = kEAPClientStatusProtocolError;
778	goto done;
779    }
780    count = net_uint16_get(version_list_p->vl_actual_length) / sizeof(uint16_t);
781    for (i = 0, scan = version_list_p->vl_version_list;
782	 i < count; i++, scan += sizeof(uint16_t)) {
783	uint16_t	this_vers = net_uint16_get(scan);
784
785	/* we only support version 1 */
786	if (this_vers == kEAPSIMVersion1) {
787	    good_version = TRUE;
788	    break;
789	}
790    }
791    if (good_version == FALSE) {
792	pkt = eapsim_make_client_error(context, in_pkt,
793				       kClientErrorCodeUnsupportedVersion);
794	*client_status = kEAPClientStatusProtocolError;
795	goto done;
796    }
797    if (count > 1) {
798	/* if there was more than one version, save the list */
799	EAPSIMContextSetVersionList(context,
800				    version_list_p->vl_version_list, count);
801    }
802    else {
803	EAPSIMContextSetVersionList(context, NULL, 0);
804    }
805    if (context->state != kEAPSIMClientStateStart) {
806	/* starting over */
807	context->plugin_state = kEAPClientStateAuthenticating;
808	context->start_count = 0;
809	context->last_identity_type = 0;
810	context->state = kEAPSIMClientStateStart;
811    }
812    if (context->start_count == 0) {
813	fill_with_random(context->nonce_mt, sizeof(context->nonce_mt));
814    }
815    context->start_count++;
816    if (context->start_count > kEAPSIMAKAIdentityAttributesCount) {
817	EAPLOG(LOG_NOTICE, "eapsim: too many Start packets (%d > %d)",
818	       context->start_count, kEAPSIMAKAIdentityAttributesCount);
819	*client_status = kEAPClientStatusProtocolError;
820	goto done;
821    }
822    identity_req_type = TLVListLookupIdentityAttribute(tlvs_p);
823    switch (identity_req_type) {
824    case kAT_ANY_ID_REQ:
825	if (context->start_count > 1) {
826	    EAPLOG(LOG_NOTICE,
827		   "eapsim: AT_ANY_ID_REQ at Start #%d",
828		   context->start_count);
829	    *client_status = kEAPClientStatusProtocolError;
830	    goto done;
831	}
832	break;
833    case kAT_FULLAUTH_ID_REQ:
834	if (context->start_count > 1
835	    && context->last_identity_type != kAT_ANY_ID_REQ) {
836	    EAPLOG(LOG_NOTICE,
837		   "eapsim: AT_FULLAUTH_ID_REQ follows %s at Start #%d",
838		   EAPSIMAKAAttributeTypeGetString(context->last_identity_type),
839		   context->start_count);
840	    *client_status = kEAPClientStatusProtocolError;
841	    goto done;
842	}
843	break;
844    case kAT_PERMANENT_ID_REQ:
845	if (context->start_count > 1
846	    && context->last_identity_type != kAT_ANY_ID_REQ
847	    && context->last_identity_type != kAT_FULLAUTH_ID_REQ) {
848	    EAPLOG(LOG_NOTICE,
849		   "eapsim: AT_PERMANENT_ID_REQ follows %s at Start #%d",
850		   EAPSIMAKAAttributeTypeGetString(context->last_identity_type),
851		   context->start_count);
852	    *client_status = kEAPClientStatusProtocolError;
853	    goto done;
854	}
855	break;
856    default:
857	if (context->start_count > 1) {
858	    EAPLOG(LOG_NOTICE,
859		   "eapsim: no *ID_REQ follows %s at Start #%d",
860		   EAPSIMAKAAttributeTypeGetString(context->last_identity_type),
861		   context->start_count);
862	    *client_status = kEAPClientStatusProtocolError;
863	    goto done;
864	}
865	/* no need to submit an identity */
866	skip_identity = TRUE;
867	break;
868    }
869
870    /* create our response */
871    context->last_identity_type = identity_req_type;
872    pkt = eapsim_make_response(context, in_pkt, kEAPSIMAKAPacketSubtypeSIMStart,
873			       tb_p);
874
875    if (!skip_identity) {
876	CFDataRef	identity_data = NULL;
877	Boolean		reauth_id_used = FALSE;
878
879	if (context->sim_static.kc != NULL) {
880	    identity = copy_static_identity(context->plugin->properties);
881	}
882	else {
883	    identity = sim_identity_create(context->persist,
884					   context->plugin->properties,
885					   identity_req_type,
886					   &reauth_id_used);
887	}
888	if (identity == NULL) {
889	    EAPLOG(LOG_NOTICE, "eapsim: can't find SIM identity");
890	    *client_status = kEAPClientStatusResourceUnavailable;
891	    pkt = NULL;
892	    goto done;
893	}
894
895	if (!TLVBufferAddIdentityString(tb_p, identity, &identity_data)) {
896	    EAPLOG(LOG_NOTICE, "eapsim: can't add AT_IDENTITY, %s",
897		   TLVBufferErrorString(tb_p));
898	    *client_status = kEAPClientStatusInternalError;
899	    pkt = NULL;
900	    goto done;
901	}
902	EAPSIMContextSetLastIdentity(context, identity_data);
903	my_CFRelease(&identity_data);
904
905	if (reauth_id_used) {
906	    /* packet only contains fast re-auth id */
907	    goto packet_complete;
908	}
909    }
910
911    /* We are now sure that re-auth will not take place */
912    context->key_info_valid = FALSE;
913
914    /* set the AT_SELECTED_VERSION attribute */
915    attr.tlv_p = TLVBufferAllocateTLV(tb_p, kAT_SELECTED_VERSION,
916				      sizeof(AT_SELECTED_VERSION));
917    if (attr.tlv_p == NULL) {
918	EAPLOG(LOG_NOTICE, "eapsim: failed allocating AT_SELECTED_VERSION, %s",
919	       TLVBufferErrorString(tb_p));
920	*client_status = kEAPClientStatusInternalError;
921	pkt = NULL;
922	goto done;
923    }
924    net_uint16_set(attr.at_selected_version->sv_selected_version,
925		   kEAPSIMVersion1);
926
927    /* set the AT_NONCE_MT attribute */
928    attr.tlv_p = TLVBufferAllocateTLV(tb_p, kAT_NONCE_MT,
929				      sizeof(AT_NONCE_MT));
930    if (attr.tlv_p == NULL) {
931	EAPLOG(LOG_NOTICE, "eapsim: failed allocating AT_NONCE_MT, %s",
932	       TLVBufferErrorString(tb_p));
933	pkt = NULL;
934	goto done;
935    }
936    net_uint16_set(attr.at_nonce_mt->nm_reserved, 0);
937    bcopy(context->nonce_mt, attr.at_nonce_mt->nm_nonce_mt,
938	  sizeof(context->nonce_mt));
939
940 packet_complete:
941    /* packet fully formed, set the EAP packet length */
942    EAPPacketSetLength(pkt,
943		       offsetof(EAPSIMPacket, attrs) + TLVBufferUsed(tb_p));
944
945 done:
946    my_CFRelease(&identity);
947    return (pkt);
948}
949
950STATIC bool
951eapsim_challenge_process_encr_data(EAPSIMContextRef context, TLVListRef tlvs_p)
952{
953    uint8_t *		decrypted_buffer = NULL;
954    TLVListDeclare(	decrypted_tlvs_p);
955    AT_ENCR_DATA * 	encr_data_p;
956    AT_IV * 		iv_p;
957    CFStringRef		next_reauth_id;
958    CFStringRef		next_pseudonym;
959
960    TLVListInit(decrypted_tlvs_p);
961    encr_data_p = (AT_ENCR_DATA *)TLVListLookupAttribute(tlvs_p, kAT_ENCR_DATA);
962    if (encr_data_p == NULL) {
963	return (TRUE);
964    }
965    iv_p = (AT_IV *)TLVListLookupAttribute(tlvs_p, kAT_IV);
966    if (iv_p == NULL) {
967	EAPLOG(LOG_NOTICE,
968	       "eapsim: Challenge missing AT_IV");
969	return (FALSE);
970    }
971    decrypted_buffer
972	= EAPSIMAKAKeyInfoDecryptTLVList(&context->key_info, encr_data_p, iv_p,
973					 decrypted_tlvs_p);
974    if (decrypted_buffer == NULL) {
975	EAPLOG(LOG_NOTICE, "eapsim: Challenge decrypt AT_ENCR_DATA failed");
976	return (FALSE);
977    }
978    {
979	CFStringRef	str;
980
981	str = TLVListCopyDescription(decrypted_tlvs_p);
982	EAPLOG(-LOG_DEBUG, "Decrypted TLVs:\n%@", str);
983	CFRelease(str);
984    }
985
986    /* save the next fast re-auth id */
987    next_reauth_id = TLVListCreateStringFromAttribute(decrypted_tlvs_p,
988						      kAT_NEXT_REAUTH_ID);
989    if (next_reauth_id != NULL) {
990	EAPSIMAKAPersistentStateSetReauthID(context->persist,
991					    next_reauth_id);
992	CFRelease(next_reauth_id);
993    }
994    /* save the next pseudonym */
995    next_pseudonym = TLVListCreateStringFromAttribute(decrypted_tlvs_p,
996						      kAT_NEXT_PSEUDONYM);
997    if (next_pseudonym != NULL) {
998	EAPSIMAKAPersistentStateSetPseudonym(context->persist,
999					     next_pseudonym);
1000	CFRelease(next_pseudonym);
1001    }
1002    if (decrypted_buffer != NULL) {
1003	free(decrypted_buffer);
1004    }
1005    TLVListFree(decrypted_tlvs_p);
1006    return (TRUE);
1007}
1008
1009STATIC EAPPacketRef
1010eapsim_challenge(EAPSIMContextRef context,
1011		 const EAPPacketRef in_pkt,
1012		 TLVListRef tlvs_p,
1013		 EAPClientStatus * client_status)
1014{
1015    int			count;
1016    uint8_t		kc[SIM_KC_SIZE * EAPSIM_MAX_RANDS];
1017    AT_MAC *		mac_p;
1018    EAPPacketRef	pkt = NULL;
1019    AT_RAND *		rand_p;
1020    uint16_t		selected_version = htons(kEAPSIMVersion1);
1021    CC_SHA1_CTX		sha1_context;
1022    uint8_t		sres[SIM_SRES_SIZE * EAPSIM_MAX_RANDS];
1023    TLVBufferDeclare(	tb_p);
1024
1025    if (context->state != kEAPSIMClientStateStart) {
1026	EAPLOG(LOG_NOTICE, "eapsim: Challenge sent without Start");
1027	*client_status = kEAPClientStatusProtocolError;
1028	goto done;
1029    }
1030    context->state = kEAPSIMClientStateChallenge;
1031    EAPSIMAKAPersistentStateSetCounter(context->persist, 1);
1032    context->reauth_success = FALSE;
1033    rand_p = (AT_RAND *)TLVListLookupAttribute(tlvs_p, kAT_RAND);
1034    if (rand_p == NULL) {
1035	EAPLOG(LOG_NOTICE, "eapsim: Challenge is missing AT_RAND");
1036	*client_status = kEAPClientStatusProtocolError;
1037	goto done;
1038    }
1039    count = ((rand_p->ra_length * TLV_ALIGNMENT)
1040	     - offsetof(AT_RAND, ra_rand)) / SIM_RAND_SIZE;
1041    if (count < context->n_required_rands) {
1042	EAPLOG(LOG_NOTICE,
1043	       "eapsim: Challenge AT_RAND has %d RANDs, policy requires %d",
1044	       count, context->n_required_rands);
1045	pkt = eapsim_make_client_error(context, in_pkt,
1046				       kClientErrorCodeInsufficientNumberOfChallenges);
1047
1048	*client_status = kEAPClientStatusProtocolError;
1049	goto done;
1050    }
1051    /* check that there aren't more than EAPSIM_MAX_RANDS */
1052    if (count > EAPSIM_MAX_RANDS) {
1053	EAPLOG(LOG_NOTICE,
1054	       "eapsim: Challenge AT_RAND has %d RANDs > %d",
1055	       count, EAPSIM_MAX_RANDS);
1056	*client_status = kEAPClientStatusProtocolError;
1057	goto done;
1058    }
1059    if (blocks_are_duplicated(rand_p->ra_rand, count, SIM_RAND_SIZE)) {
1060	EAPLOG(LOG_NOTICE,
1061	       "eapsim: Challenge AT_RAND has duplicate RANDs");
1062	*client_status = kEAPClientStatusProtocolError;
1063	goto done;
1064    }
1065
1066    /* get the (Kc, SRES) pairs from the SIM */
1067    if (EAPSIMContextSIMProcessRAND(context, rand_p->ra_rand, count, kc, sres)
1068	== FALSE) {
1069	*client_status = kEAPClientStatusInternalError;
1070	goto done;
1071    }
1072
1073    /*
1074     * generate the MK:
1075     * MK = SHA1(Identity|n*Kc| NONCE_MT| Version List| Selected Version)
1076     */
1077    CC_SHA1_Init(&sha1_context);
1078    if (context->last_identity != NULL) {
1079	CC_SHA1_Update(&sha1_context, CFDataGetBytePtr(context->last_identity),
1080		       CFDataGetLength(context->last_identity));
1081    }
1082    else {
1083	CC_SHA1_Update(&sha1_context, context->plugin->username,
1084		       context->plugin->username_length);
1085    }
1086    CC_SHA1_Update(&sha1_context, kc, SIM_KC_SIZE * count);
1087    CC_SHA1_Update(&sha1_context, context->nonce_mt, sizeof(context->nonce_mt));
1088    if (context->version_list != NULL) {
1089	CC_SHA1_Update(&sha1_context, context->version_list,
1090		       (sizeof(*context->version_list)
1091			* context->version_list_count));
1092    }
1093    else {
1094	CC_SHA1_Update(&sha1_context, &selected_version,
1095		       sizeof(selected_version));
1096    }
1097    CC_SHA1_Update(&sha1_context, &selected_version, sizeof(selected_version));
1098    CC_SHA1_Final(EAPSIMAKAPersistentStateGetMasterKey(context->persist),
1099		  &sha1_context);
1100
1101    /* now run PRF to generate keying material */
1102    fips186_2prf(EAPSIMAKAPersistentStateGetMasterKey(context->persist),
1103		 context->key_info.key);
1104
1105    /* validate the MAC */
1106    mac_p = (AT_MAC *)TLVListLookupAttribute(tlvs_p, kAT_MAC);
1107    if (mac_p == NULL) {
1108	EAPLOG(LOG_NOTICE,
1109	       "eapsim: Challenge is missing AT_MAC");
1110	*client_status = kEAPClientStatusProtocolError;
1111	goto done;
1112    }
1113    if (!EAPSIMAKAKeyInfoVerifyMAC(&context->key_info, in_pkt,
1114				   mac_p->ma_mac,
1115				   context->nonce_mt,
1116				   sizeof(context->nonce_mt))) {
1117	EAPLOG(LOG_NOTICE,
1118	       "eapsim: Challenge AT_MAC not valid");
1119	*client_status = kEAPClientStatusProtocolError;
1120	goto done;
1121    }
1122
1123    /* check for and process encrypted data */
1124    if (eapsim_challenge_process_encr_data(context, tlvs_p) == FALSE) {
1125	*client_status = kEAPClientStatusProtocolError;
1126	goto done;
1127    }
1128
1129    /* create our response */
1130    pkt = eapsim_make_response(context, in_pkt,
1131			       kEAPSIMAKAPacketSubtypeSIMChallenge, tb_p);
1132    mac_p = (AT_MAC *)TLVBufferAllocateTLV(tb_p, kAT_MAC,
1133					   sizeof(AT_MAC));
1134    if (mac_p == NULL) {
1135	EAPLOG(LOG_NOTICE, "eapsim: failed allocating AT_MAC, %s",
1136	       TLVBufferErrorString(tb_p));
1137	*client_status = kEAPClientStatusInternalError;
1138	pkt = NULL;
1139	goto done;
1140    }
1141    net_uint16_set(mac_p->ma_reserved, 0);
1142    EAPPacketSetLength(pkt,
1143		       offsetof(EAPSIMPacket, attrs) + TLVBufferUsed(tb_p));
1144    /* compute/set the MAC value */
1145    EAPSIMAKAKeyInfoSetMAC(&context->key_info, pkt, mac_p->ma_mac,
1146			   sres, SIM_SRES_SIZE * count);
1147
1148    /* as far as we're concerned, we're successful */
1149    context->state = kEAPSIMClientStateSuccess;
1150    context->key_info_valid = TRUE;
1151    save_persistent_state(context);
1152
1153 done:
1154    return (pkt);
1155}
1156
1157STATIC void
1158eapsim_compute_reauth_key(EAPSIMContextRef context,
1159			  AT_COUNTER * counter_p,
1160			  AT_NONCE_S * nonce_s_p)
1161
1162{
1163    const void *	identity;
1164    int			identity_length;
1165
1166    if (context->last_identity != NULL) {
1167	identity = CFDataGetBytePtr(context->last_identity);
1168	identity_length = CFDataGetLength(context->last_identity);
1169    }
1170    else {
1171	identity = context->plugin->username;
1172	identity_length = context->plugin->username_length;
1173    }
1174    EAPSIMAKAKeyInfoComputeReauthKey(&context->key_info,
1175				     context->persist,
1176				     identity, identity_length,
1177				     counter_p, nonce_s_p);
1178    return;
1179}
1180
1181#define ENCR_BUFSIZE 	(sizeof(AT_COUNTER) + sizeof(AT_COUNTER_TOO_SMALL))
1182#define ENCR_BUFSIZE_R	AT_ENCR_DATA_ROUNDUP(ENCR_BUFSIZE)
1183
1184STATIC EAPPacketRef
1185eapsim_reauthentication(EAPSIMContextRef context,
1186			const EAPPacketRef in_pkt,
1187			TLVListRef tlvs_p,
1188			EAPClientStatus * client_status)
1189{
1190    uint16_t		at_counter;
1191    AT_COUNTER *	counter_p;
1192    bool		force_fullauth = FALSE;
1193    uint8_t		encr_buffer[ENCR_BUFSIZE_R];
1194    TLVBufferDeclare(	encr_tb_p);
1195    uint8_t *		decrypted_buffer = NULL;
1196    TLVListDeclare(	decrypted_tlvs_p);
1197    AT_ENCR_DATA * 	encr_data_p;
1198    AT_IV * 		iv_p;
1199    AT_MAC *		mac_p;
1200    CFStringRef		next_reauth_id;
1201    AT_NONCE_S *	nonce_s_p;
1202    EAPPacketRef	pkt = NULL;
1203    CFStringRef		reauth_id = NULL;
1204    TLVBufferDeclare(	tb_p);
1205
1206    TLVListInit(decrypted_tlvs_p);
1207    if (context->key_info_valid == FALSE) {
1208	EAPLOG(LOG_NOTICE,
1209	       "eapsim: Reauthentication but no key info available");
1210	*client_status = kEAPClientStatusProtocolError;
1211	goto done;
1212    }
1213    reauth_id = EAPSIMAKAPersistentStateGetReauthID(context->persist);
1214    if (reauth_id == NULL) {
1215	EAPLOG(LOG_NOTICE,
1216	       "eapsim: received Reauthentication but don't have reauth id");
1217	*client_status = kEAPClientStatusProtocolError;
1218	goto done;
1219    }
1220    context->state = kEAPSIMClientStateReauthentication;
1221    context->plugin_state = kEAPClientStateAuthenticating;
1222
1223    /* validate the MAC */
1224    mac_p = (AT_MAC *)TLVListLookupAttribute(tlvs_p, kAT_MAC);
1225    if (mac_p == NULL) {
1226	EAPLOG(LOG_NOTICE,
1227	       "eapsim: Reauthentication is missing AT_MAC");
1228	*client_status = kEAPClientStatusProtocolError;
1229	goto done;
1230    }
1231    if (!EAPSIMAKAKeyInfoVerifyMAC(&context->key_info, in_pkt, mac_p->ma_mac,
1232				   NULL, 0)) {
1233	EAPLOG(LOG_NOTICE,
1234	       "eapsim: Reauthentication AT_MAC not valid");
1235	*client_status = kEAPClientStatusProtocolError;
1236	goto done;
1237    }
1238
1239    /* packet must contain AT_ENCR_DATA, AT_IV */
1240    encr_data_p = (AT_ENCR_DATA *)TLVListLookupAttribute(tlvs_p, kAT_ENCR_DATA);
1241    iv_p = (AT_IV *)TLVListLookupAttribute(tlvs_p, kAT_IV);
1242    if (encr_data_p == NULL || iv_p == NULL) {
1243	if (encr_data_p == NULL) {
1244	    EAPLOG(LOG_NOTICE,
1245		   "eapsim:  Reauthentication missing AT_ENCR_DATA");
1246	}
1247	if (iv_p == NULL) {
1248	    EAPLOG(LOG_NOTICE,
1249		   "eapsim:  Reauthentication missing AT_IV");
1250	}
1251	*client_status = kEAPClientStatusProtocolError;
1252	goto done;
1253    }
1254    decrypted_buffer
1255	= EAPSIMAKAKeyInfoDecryptTLVList(&context->key_info, encr_data_p, iv_p,
1256					 decrypted_tlvs_p);
1257    if (decrypted_buffer == NULL) {
1258	EAPLOG(LOG_NOTICE,
1259	       "eapsim: failed to decrypt Reauthentication AT_ENCR_DATA");
1260	*client_status = kEAPClientStatusProtocolError;
1261	goto done;
1262    }
1263    {
1264	CFStringRef	str;
1265
1266	str = TLVListCopyDescription(decrypted_tlvs_p);
1267	EAPLOG(-LOG_DEBUG, "Decrypted TLVs:\n%@", str);
1268	CFRelease(str);
1269    }
1270
1271    /* Reauthentication must contain AT_NONCE_S, AT_COUNTER */
1272    nonce_s_p
1273	= (AT_NONCE_S *)TLVListLookupAttribute(decrypted_tlvs_p, kAT_NONCE_S);
1274    counter_p
1275	= (AT_COUNTER *)TLVListLookupAttribute(decrypted_tlvs_p, kAT_COUNTER);
1276    if (nonce_s_p == NULL || counter_p == NULL) {
1277	if (nonce_s_p == NULL) {
1278	    EAPLOG(LOG_NOTICE,
1279		   "eapsim:  Reauthentication AT_ENCR_DATA missing AT_NONCE_S");
1280	}
1281	if (counter_p == NULL) {
1282	    EAPLOG(LOG_NOTICE,
1283		   "eapsim:  Reauthentication AT_ENCR_DATA missing AT_COUNTER");
1284	}
1285	*client_status = kEAPClientStatusProtocolError;
1286	goto done;
1287    }
1288
1289    /* check the at_counter */
1290    at_counter = net_uint16_get(counter_p->co_counter);
1291    if (at_counter < EAPSIMAKAPersistentStateGetCounter(context->persist)) {
1292	force_fullauth = TRUE;
1293    }
1294    else {
1295	/* save the next fast re-auth id */
1296	next_reauth_id = TLVListCreateStringFromAttribute(decrypted_tlvs_p,
1297							  kAT_NEXT_REAUTH_ID);
1298	if (next_reauth_id != NULL) {
1299	    EAPSIMAKAPersistentStateSetReauthID(context->persist,
1300						next_reauth_id);
1301	    CFRelease(next_reauth_id);
1302	}
1303	EAPSIMAKAPersistentStateSetCounter(context->persist, at_counter);
1304    }
1305
1306    /* create our response */
1307    pkt = eapsim_make_response(context, in_pkt,
1308			       kEAPSIMAKAPacketSubtypeReauthentication, tb_p);
1309
1310    /*
1311     * create nested attributes containing:
1312     * 	AT_COUNTER
1313     *  AT_COUNTER_TOO_SMALL (if necessary)
1314     */
1315    TLVBufferInit(encr_tb_p, encr_buffer, sizeof(encr_buffer));
1316    if (TLVBufferAddCounter(encr_tb_p, at_counter) == FALSE) {
1317	EAPLOG(LOG_NOTICE, "eapsim: failed allocating AT_COUNTER, %s",
1318	       TLVBufferErrorString(tb_p));
1319	*client_status = kEAPClientStatusInternalError;
1320	pkt = NULL;
1321	goto done;
1322    }
1323    if (force_fullauth
1324	&& TLVBufferAddCounterTooSmall(encr_tb_p) == FALSE) {
1325	EAPLOG(LOG_NOTICE,
1326	       "eapsim: failed allocating AT_COUNTER_TOO_SMALL, %s",
1327	       TLVBufferErrorString(tb_p));
1328	*client_status = kEAPClientStatusInternalError;
1329	pkt = NULL;
1330	goto done;
1331    }
1332
1333    /* AT_IV and AT_ENCR_DATA */
1334    if (!EAPSIMAKAKeyInfoEncryptTLVs(&context->key_info, tb_p, encr_tb_p)) {
1335	*client_status = kEAPClientStatusInternalError;
1336	pkt = NULL;
1337	goto done;
1338    }
1339
1340    /* AT_MAC */
1341    mac_p = (AT_MAC *)TLVBufferAllocateTLV(tb_p, kAT_MAC,
1342					   sizeof(AT_MAC));
1343    if (mac_p == NULL) {
1344	EAPLOG(LOG_NOTICE, "eapsim: failed allocating AT_MAC, %s",
1345	       TLVBufferErrorString(tb_p));
1346	*client_status = kEAPClientStatusInternalError;
1347	pkt = NULL;
1348	goto done;
1349    }
1350    net_uint16_set(mac_p->ma_reserved, 0);
1351
1352    /* set the packet length */
1353    EAPPacketSetLength(pkt,
1354		       offsetof(EAPSIMPacket, attrs) + TLVBufferUsed(tb_p));
1355
1356    /* compute/set the MAC value */
1357    EAPSIMAKAKeyInfoSetMAC(&context->key_info, pkt,
1358			   mac_p->ma_mac, nonce_s_p->nc_nonce_s,
1359			   sizeof(nonce_s_p->nc_nonce_s));
1360
1361    if (force_fullauth == FALSE) {
1362	/* as far as we're concerned, we're successful */
1363	context->state = kEAPSIMClientStateSuccess;
1364	eapsim_compute_reauth_key(context, counter_p, nonce_s_p);
1365	context->key_info_valid = TRUE;
1366	context->reauth_success = TRUE;
1367    }
1368    else {
1369	context->key_info_valid = FALSE;
1370    }
1371    save_persistent_state(context);
1372
1373 done:
1374    if (decrypted_buffer != NULL) {
1375	free(decrypted_buffer);
1376    }
1377    TLVListFree(decrypted_tlvs_p);
1378    return (pkt);
1379}
1380
1381#define ENCR_BUFSIZE_NOTIF 	(sizeof(AT_COUNTER))
1382#define ENCR_BUFSIZE_NOTIF_R	AT_ENCR_DATA_ROUNDUP(ENCR_BUFSIZE_NOTIF)
1383
1384STATIC EAPPacketRef
1385eapsim_notification(EAPSIMContextRef context,
1386		    const EAPPacketRef in_pkt,
1387		    TLVListRef tlvs_p,
1388		    EAPClientStatus * client_status,
1389		    EAPClientDomainSpecificError * error)
1390{
1391    bool		after_auth;
1392    uint16_t		current_at_counter;
1393    bool		do_replay_protection = FALSE;
1394    AT_NOTIFICATION *	notification_p;
1395    AT_MAC *		mac_p;
1396    uint16_t		notification_code = 0;
1397    EAPPacketRef	pkt = NULL;
1398    TLVBufferDeclare(	tb_p);
1399
1400    *client_status = kEAPClientStatusOK;
1401    *error = 0;
1402    notification_p =
1403	(AT_NOTIFICATION *)TLVListLookupAttribute(tlvs_p, kAT_NOTIFICATION);
1404
1405    if (notification_p == NULL) {
1406	EAPLOG(LOG_NOTICE, "eapsim: Notification does not contain "
1407	       "AT_NOTIFICATION attribute");
1408	*client_status = kEAPClientStatusProtocolError;
1409	goto done;
1410    }
1411
1412    notification_code = net_uint16_get(notification_p->nt_notification);
1413    after_auth = ATNotificationPhaseIsAfterAuthentication(notification_code);
1414    if (ATNotificationCodeIsSuccess(notification_code) && after_auth == FALSE) {
1415	EAPLOG(LOG_NOTICE,
1416	       "eapsim: Notification code '%d' indicates "
1417	       "success before authentication", notification_code);
1418	*client_status = kEAPClientStatusProtocolError;
1419	goto done;
1420    }
1421
1422    /* validate the MAC */
1423    mac_p = (AT_MAC *)TLVListLookupAttribute(tlvs_p, kAT_MAC);
1424    if (mac_p == NULL) {
1425	if (after_auth) {
1426	    EAPLOG(LOG_NOTICE, "eapsim: Notification is missing AT_MAC");
1427	    *client_status = kEAPClientStatusProtocolError;
1428	    goto done;
1429	}
1430    }
1431    else {
1432	if (after_auth == FALSE) {
1433	    EAPLOG(LOG_NOTICE,
1434		   "eapsim: Notification incorrectly contains AT_MAC");
1435	    *client_status = kEAPClientStatusProtocolError;
1436	    goto done;
1437	}
1438
1439	if (!EAPSIMAKAKeyInfoVerifyMAC(&context->key_info, in_pkt,
1440				       mac_p->ma_mac, NULL, 0)) {
1441	    EAPLOG(LOG_NOTICE, "eapsim: Notification AT_MAC not valid");
1442	    *client_status = kEAPClientStatusProtocolError;
1443	    goto done;
1444	}
1445    }
1446    current_at_counter = EAPSIMAKAPersistentStateGetCounter(context->persist);
1447    do_replay_protection = context->reauth_success && after_auth;
1448    if (do_replay_protection) {
1449	uint16_t	at_counter;
1450	uint8_t *	decrypted_buffer;
1451	AT_ENCR_DATA *	encr_data_p;
1452	TLVListDeclare(	decrypted_tlvs_p);
1453	bool		has_counter = FALSE;
1454	AT_IV * 	iv_p;
1455
1456	encr_data_p
1457	    = (AT_ENCR_DATA *)TLVListLookupAttribute(tlvs_p, kAT_ENCR_DATA);
1458	iv_p = (AT_IV *)TLVListLookupAttribute(tlvs_p, kAT_IV);
1459	if (encr_data_p == NULL || iv_p == NULL) {
1460	    EAPLOG(LOG_NOTICE,
1461		   "eapsim: Notification after re-auth missing "
1462		   "AT_ENCR_DATA (%p) or AT_IV (%p)", encr_data_p, iv_p);
1463	    *client_status = kEAPClientStatusProtocolError;
1464	    goto done;
1465	}
1466
1467	decrypted_buffer
1468	    = EAPSIMAKAKeyInfoDecryptTLVList(&context->key_info,
1469					     encr_data_p, iv_p,
1470					     decrypted_tlvs_p);
1471	if (decrypted_buffer != NULL) {
1472	    AT_COUNTER *	counter_p;
1473	    CFStringRef		str;
1474
1475	    str = TLVListCopyDescription(decrypted_tlvs_p);
1476	    EAPLOG(-LOG_DEBUG, "Decrypted TLVs:\n%@", str);
1477	    CFRelease(str);
1478
1479	    counter_p = (AT_COUNTER *)TLVListLookupAttribute(decrypted_tlvs_p,
1480							     kAT_COUNTER);
1481	    if (counter_p != NULL) {
1482		at_counter = net_uint16_get(counter_p->co_counter);
1483		has_counter = TRUE;
1484	    }
1485	    free(decrypted_buffer);
1486	    TLVListFree(decrypted_tlvs_p);
1487	}
1488	else {
1489	    EAPLOG(LOG_NOTICE,
1490		   "eapsim: failed to decrypt Notification AT_ENCR_DATA");
1491	    *client_status = kEAPClientStatusInternalError;
1492	    goto done;
1493	}
1494	if (!has_counter) {
1495	    EAPLOG(LOG_NOTICE,
1496		   "eapsim:  Notification AT_ENCR_DATA missing AT_COUNTER");
1497	    *client_status = kEAPClientStatusProtocolError;
1498	    goto done;
1499	}
1500	if (at_counter != current_at_counter) {
1501	    EAPLOG(LOG_NOTICE, "eapsim: Notification AT_COUNTER (%d) does not "
1502		   "match current counter (%d)", at_counter,
1503		   current_at_counter);
1504	    *client_status = kEAPClientStatusProtocolError;
1505	    goto done;
1506	}
1507    }
1508
1509    /* create our response */
1510    pkt = eapsim_make_response(context, in_pkt,
1511			       kEAPSIMAKAPacketSubtypeNotification,
1512			       tb_p);
1513    if (do_replay_protection) {
1514	uint8_t			encr_buffer[ENCR_BUFSIZE_NOTIF_R];
1515	TLVBufferDeclare(	encr_tb_p);
1516
1517	/*
1518	 * create nested attributes containing:
1519	 *    AT_COUNTER
1520	 */
1521	TLVBufferInit(encr_tb_p, encr_buffer, sizeof(encr_buffer));
1522	if (TLVBufferAddCounter(encr_tb_p, current_at_counter) == FALSE) {
1523	    EAPLOG(LOG_NOTICE, "eapsim: failed to allocate AT_COUNTER, %s",
1524		   TLVBufferErrorString(encr_tb_p));
1525	    *client_status = kEAPClientStatusAllocationFailed;
1526	    goto done;
1527	}
1528
1529	/* AT_IV and AT_ENCR_DATA */
1530	if (!EAPSIMAKAKeyInfoEncryptTLVs(&context->key_info, tb_p, encr_tb_p)) {
1531	    *client_status = kEAPClientStatusInternalError;
1532	    pkt = NULL;
1533	    goto done;
1534	}
1535    }
1536    if (mac_p != NULL) {
1537	/* AT_MAC */
1538	mac_p = (AT_MAC *)TLVBufferAllocateTLV(tb_p, kAT_MAC, sizeof(AT_MAC));
1539	if (mac_p == NULL) {
1540	    EAPLOG(LOG_NOTICE, "eapsim: failed allocating AT_MAC, %s",
1541		   TLVBufferErrorString(tb_p));
1542	    *client_status = kEAPClientStatusAllocationFailed;
1543	    pkt = NULL;
1544	    goto done;
1545	}
1546	net_uint16_set(mac_p->ma_reserved, 0);
1547    }
1548
1549    /* set the packet length */
1550    EAPPacketSetLength(pkt,
1551		       offsetof(EAPSIMPacket, attrs) + TLVBufferUsed(tb_p));
1552    if (mac_p != NULL) {
1553	/* compute/set the MAC value */
1554	EAPSIMAKAKeyInfoSetMAC(&context->key_info, pkt, mac_p->ma_mac, NULL, 0);
1555    }
1556    if (ATNotificationCodeIsSuccess(notification_code)) {
1557	context->state = kEAPSIMClientStateSuccess;
1558    }
1559    else {
1560	const char *	str;
1561
1562	context->state = kEAPSIMClientStateFailure;
1563	*client_status = kEAPClientStatusPluginSpecificError;
1564	*error = EAPSIMAKAStatusForATNotificationCode(notification_code);
1565	str = ATNotificationCodeGetString(notification_code);
1566	if (str == NULL) {
1567	    EAPLOG(LOG_NOTICE,
1568		   "eapsim: Notification code '%d' unrecognized failure",
1569		   notification_code);
1570	}
1571	else {
1572	    EAPLOG(LOG_NOTICE, "eapsim: Notification: %s", str);
1573	}
1574    }
1575
1576 done:
1577    return (pkt);
1578}
1579
1580STATIC EAPPacketRef
1581eapsim_request(EAPSIMContextRef context,
1582	       const EAPPacketRef in_pkt,
1583	       EAPClientStatus * client_status,
1584	       EAPClientDomainSpecificError * error)
1585{
1586    EAPSIMPacketRef	eapsim_in = (EAPSIMPacketRef)in_pkt;
1587    EAPPacketRef	eapsim_out = NULL;
1588    uint16_t		in_length = EAPPacketGetLength(in_pkt);
1589    uint8_t		subtype;
1590    TLVListDeclare(	tlvs_p);
1591
1592    TLVListInit(tlvs_p);
1593    if (in_length <= kEAPSIMAKAPacketHeaderLength) {
1594	EAPLOG_FL(LOG_NOTICE, "length %d <= %ld",
1595		  in_length, kEAPSIMAKAPacketHeaderLength);
1596	*client_status = kEAPClientStatusProtocolError;
1597	goto done;
1598    }
1599    if (TLVListParse(tlvs_p, eapsim_in->attrs,
1600		     in_length - kEAPSIMAKAPacketHeaderLength) == FALSE) {
1601	EAPLOG_FL(LOG_NOTICE, "parse failed: %s",
1602		  TLVListErrorString(tlvs_p));
1603	*client_status = kEAPClientStatusProtocolError;
1604	goto done;
1605    }
1606    if (context->state != kEAPSIMClientStateNone
1607	&& context->previous_identifier == in_pkt->identifier) {
1608	/* re-send our previous response */
1609	return ((EAPPacketRef)context->pkt);
1610    }
1611    subtype = eapsim_in->subtype;
1612    switch (subtype) {
1613    case kEAPSIMAKAPacketSubtypeSIMStart:
1614	eapsim_out = eapsim_start(context, in_pkt, tlvs_p, client_status);
1615	break;
1616    case kEAPSIMAKAPacketSubtypeSIMChallenge:
1617	eapsim_out = eapsim_challenge(context, in_pkt, tlvs_p, client_status);
1618	break;
1619    case kEAPSIMAKAPacketSubtypeNotification:
1620	eapsim_out = eapsim_notification(context, in_pkt, tlvs_p,
1621					 client_status, error);
1622	break;
1623    case kEAPSIMAKAPacketSubtypeReauthentication:
1624	eapsim_out
1625	    = eapsim_reauthentication(context, in_pkt, tlvs_p, client_status);
1626	break;
1627    default:
1628	*client_status = kEAPClientStatusProtocolError;
1629	EAPLOG_FL(LOG_NOTICE, "unexpected Subtype %s",
1630		  EAPSIMAKAPacketSubtypeGetString(subtype));
1631	*client_status = kEAPClientStatusProtocolError;
1632	goto done;
1633    }
1634
1635 done:
1636    TLVListFree(tlvs_p);
1637    if (*client_status != kEAPClientStatusOK) {
1638	context->plugin_state = kEAPClientStateFailure;
1639	context->state = kEAPSIMClientStateFailure;
1640    }
1641    if (eapsim_out == NULL
1642	&& *client_status == kEAPClientStatusProtocolError) {
1643	eapsim_out
1644	    = eapsim_make_client_error(context, in_pkt,
1645				       kClientErrorCodeUnableToProcessPacket);
1646    }
1647    if (eapsim_out != NULL) {
1648	context->previous_identifier = in_pkt->identifier;
1649    }
1650    return (eapsim_out);
1651
1652}
1653
1654STATIC EAPClientState
1655eapsim_process(EAPClientPluginDataRef plugin,
1656	       const EAPPacketRef in_pkt,
1657	       EAPPacketRef * out_pkt_p,
1658	       EAPClientStatus * client_status,
1659	       EAPClientDomainSpecificError * error)
1660{
1661    EAPSIMContextRef	context = (EAPSIMContextRef)plugin->private;
1662
1663    *client_status = kEAPClientStatusOK;
1664    *error = 0;
1665    switch (in_pkt->code) {
1666    case kEAPCodeRequest:
1667	*out_pkt_p = eapsim_request(context, in_pkt, client_status, error);
1668	break;
1669    case kEAPCodeSuccess:
1670	context->previous_identifier = -1;
1671	if (context->state == kEAPSIMClientStateSuccess) {
1672	    context->plugin_state = kEAPClientStateSuccess;
1673	}
1674	break;
1675    case kEAPCodeFailure:
1676	context->previous_identifier = -1;
1677	context->plugin_state = kEAPClientStateFailure;
1678	break;
1679    default:
1680	break;
1681    }
1682    return (context->plugin_state);
1683}
1684
1685STATIC const char *
1686eapsim_failure_string(EAPClientPluginDataRef plugin)
1687{
1688    return (NULL);
1689}
1690
1691STATIC void *
1692eapsim_session_key(EAPClientPluginDataRef plugin, int * key_length)
1693{
1694    EAPSIMContextRef	context = (EAPSIMContextRef)plugin->private;
1695
1696    if (context->state == kEAPSIMClientStateSuccess
1697	&& context->key_info_valid) {
1698	*key_length = 32;
1699	return (context->key_info.s.msk);
1700    }
1701    return (NULL);
1702}
1703
1704STATIC void *
1705eapsim_server_key(EAPClientPluginDataRef plugin, int * key_length)
1706{
1707    EAPSIMContextRef	context = (EAPSIMContextRef)plugin->private;
1708
1709    if (context->state == kEAPSIMClientStateSuccess
1710	&& context->key_info_valid) {
1711	*key_length = 32;
1712	return (context->key_info.s.msk + 32);
1713    }
1714    return (NULL);
1715}
1716
1717STATIC CFDictionaryRef
1718eapsim_publish_props(EAPClientPluginDataRef plugin)
1719{
1720    return (NULL);
1721}
1722
1723STATIC CFStringRef
1724eapsim_user_name_copy(CFDictionaryRef properties)
1725{
1726    EAPSIMAKAAttributeType 	identity_type;
1727    CFStringRef			imsi;
1728    EAPSIMAKAPersistentStateRef persist;
1729    CFStringRef			ret_identity;
1730
1731    ret_identity = copy_static_identity(properties);
1732    if (ret_identity != NULL) {
1733	return (ret_identity);
1734    }
1735    imsi = SIMCopyIMSI();
1736    if (imsi == NULL) {
1737	return (NULL);
1738    }
1739    identity_type = S_get_identity_type(properties);
1740    persist = EAPSIMAKAPersistentStateCreate(kEAPTypeEAPSIM,
1741					     CC_SHA1_DIGEST_LENGTH,
1742					     imsi, identity_type);
1743    CFRelease(imsi);
1744    if (persist != NULL) {
1745	ret_identity = sim_identity_create(persist, properties,
1746					   identity_type, NULL);
1747	EAPSIMAKAPersistentStateRelease(persist);
1748    }
1749    return (ret_identity);
1750}
1751
1752/*
1753 * Function: eapsim_copy_identity
1754 * Purpose:
1755 *   Return the current identity we should use in responding to an
1756 *   EAP Request Identity packet.
1757 */
1758STATIC CFStringRef
1759eapsim_copy_identity(EAPClientPluginDataRef plugin)
1760{
1761    EAPSIMContextRef	context = (EAPSIMContextRef)plugin->private;
1762
1763    EAPSIMContextSetLastIdentity(context, NULL);
1764    context->state = kEAPSIMClientStateNone;
1765    context->previous_identifier = -1;
1766    if (context->sim_static.rand != NULL) {
1767	return (copy_static_identity(plugin->properties));
1768    }
1769    return (sim_identity_create(context->persist, plugin->properties,
1770				kAT_ANY_ID_REQ, NULL));
1771}
1772
1773STATIC CFStringRef
1774eapsim_copy_packet_description(const EAPPacketRef pkt, bool * packet_is_valid)
1775{
1776    return (EAPSIMAKAPacketCopyDescription(pkt, packet_is_valid));
1777}
1778
1779STATIC EAPType
1780eapsim_type(void)
1781{
1782    return (kEAPTypeEAPSIM);
1783
1784}
1785
1786STATIC const char *
1787eapsim_name(void)
1788{
1789    return (EAP_SIM_NAME);
1790
1791}
1792
1793STATIC EAPClientPluginVersion
1794eapsim_version(void)
1795{
1796    return (kEAPClientPluginVersion);
1797}
1798
1799STATIC struct func_table_ent {
1800    const char *		name;
1801    void *			func;
1802} func_table[] = {
1803#if 0
1804    { kEAPClientPluginFuncNameIntrospect, eapsim_introspect },
1805#endif /* 0 */
1806    { kEAPClientPluginFuncNameVersion, eapsim_version },
1807    { kEAPClientPluginFuncNameEAPType, eapsim_type },
1808    { kEAPClientPluginFuncNameEAPName, eapsim_name },
1809    { kEAPClientPluginFuncNameInit, eapsim_init },
1810    { kEAPClientPluginFuncNameFree, eapsim_free },
1811    { kEAPClientPluginFuncNameProcess, eapsim_process },
1812    { kEAPClientPluginFuncNameFreePacket, eapsim_free_packet },
1813    { kEAPClientPluginFuncNameFailureString, eapsim_failure_string },
1814    { kEAPClientPluginFuncNameSessionKey, eapsim_session_key },
1815    { kEAPClientPluginFuncNameServerKey, eapsim_server_key },
1816    { kEAPClientPluginFuncNamePublishProperties, eapsim_publish_props },
1817    { kEAPClientPluginFuncNameUserName, eapsim_user_name_copy },
1818    { kEAPClientPluginFuncNameCopyIdentity, eapsim_copy_identity },
1819    { kEAPClientPluginFuncNameCopyPacketDescription,
1820      eapsim_copy_packet_description },
1821    { NULL, NULL},
1822};
1823
1824
1825EAPClientPluginFuncRef
1826eapsim_introspect(EAPClientPluginFuncName name)
1827{
1828    struct func_table_ent * scan;
1829
1830    for (scan = func_table; scan->name != NULL; scan++) {
1831	if (strcmp(name, scan->name) == 0) {
1832	    return (scan->func);
1833	}
1834    }
1835    return (NULL);
1836}
1837
1838
1839#ifdef TEST_RAND_DUPS
1840const uint8_t	randval1[3 * SIM_RAND_SIZE] = {
1841    0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
1842    0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
1843    0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf
1844};
1845
1846const uint8_t	randval2[3 * SIM_RAND_SIZE] = {
1847    0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
1848    0x10, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
1849    0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf
1850};
1851
1852const uint8_t	randval3[3 * SIM_RAND_SIZE] = {
1853    0x10, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
1854    0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
1855    0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf
1856};
1857
1858const uint8_t	randval4[3 * SIM_RAND_SIZE] = {
1859    0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
1860    0x10, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
1861    0x20, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf
1862};
1863
1864struct {
1865    const uint8_t *	block;
1866    int			size;
1867    bool		duplicated;
1868} rands[] = {
1869    { randval1, sizeof(randval1), TRUE },
1870    { randval2, sizeof(randval2), TRUE },
1871    { randval3, sizeof(randval3), TRUE },
1872    { randval4, sizeof(randval4), FALSE },
1873    { NULL, 0 }
1874};
1875
1876int
1877main()
1878{
1879    int		i;
1880
1881    for (i = 0; rands[i].block != NULL; i++) {
1882	bool	duplicated;
1883
1884	duplicated = blocks_are_duplicated(rands[i].block,
1885					   rands[i].size / SIM_RAND_SIZE,
1886					   SIM_RAND_SIZE);
1887	if (duplicated == rands[i].duplicated) {
1888	    printf("Test %d passed (found%sduplicate)\n", i,
1889		   duplicated ? " " : " no ");
1890	}
1891	else {
1892	    printf("Test %d failed\n", i);
1893	}
1894    }
1895    exit(0);
1896    return (0);
1897}
1898
1899#endif /* TEST_RAND_DUPS */
1900
1901#ifdef TEST_SET_VERSION_LIST
1902int
1903main(int argc, char * argv[])
1904{
1905    EAPSIMContext	context;
1906    uint16_t		list1[2] = { 0x1234, 0x5678 };
1907    uint16_t		list2[3] = { 0x1, 0x2, 0x3 };
1908    uint16_t		list3[2] = { 0x4, 0x5 };
1909
1910    EAPSIMContextClear(&context);
1911    EAPSIMContextSetVersionList(&context,
1912				list1, sizeof(list1) / sizeof(list1[0]));
1913    EAPSIMContextSetVersionList(&context,
1914				list1, sizeof(list1) / sizeof(list1[0]));
1915    EAPSIMContextSetVersionList(&context,
1916				list2, sizeof(list2) / sizeof(list2[0]));
1917    EAPSIMContextSetVersionList(&context,
1918				list2, sizeof(list2) / sizeof(list2[0]));
1919    EAPSIMContextSetVersionList(&context,
1920				list3, sizeof(list3) / sizeof(list3[0]));
1921    EAPSIMContextSetVersionList(&context,
1922				list3, sizeof(list3) / sizeof(list3[0]));
1923    EAPSIMContextSetVersionList(&context, NULL, 0);
1924    exit(0);
1925    return (0);
1926}
1927
1928#endif /* TEST_SET_VERSION_LIST */
1929
1930#ifdef TEST_SIM_INFO
1931#if TARGET_OS_EMBEDDED
1932int
1933main()
1934{
1935    CFStringRef	identity;
1936
1937    identity = eapsim_user_name_copy(NULL);
1938    if (identity != NULL) {
1939	CFShow(identity);
1940	CFRelease(identity);
1941    }
1942    exit(0);
1943    return (0);
1944}
1945#endif /* TARGET_OS_EMBEDDED */
1946#endif /* TEST_SIM_INFO */
1947