1 /* Copyright (C) 2007-2008 The Android Open Source Project
2 **
3 ** This software is licensed under the terms of the GNU General Public
4 ** License version 2, as published by the Free Software Foundation, and
5 ** may be copied, distributed, and modified under those terms.
6 **
7 ** This program is distributed in the hope that it will be useful,
8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 ** GNU General Public License for more details.
11 */
12 #include "sim_card.h"
13 #include <string.h>
14 #include <assert.h>
15
16 /* set ENABLE_DYNAMIC_RECORDS to 1 to enable dynamic records
17 * for now, this is an experimental feature that needs more testing
18 */
19 #define ENABLE_DYNAMIC_RECORDS 0
20
21 #define A_SIM_PIN_SIZE 4
22 #define A_SIM_PUK_SIZE 8
23
24 typedef struct ASimCardRec_ {
25 ASimStatus status;
26 char pin[ A_SIM_PIN_SIZE+1 ];
27 char puk[ A_SIM_PUK_SIZE+1 ];
28 int pin_retries;
29
30 char out_buff[ 256 ];
31 int out_size;
32
33 } ASimCardRec;
34
35 static ASimCardRec _s_card[1];
36
37 ASimCard
asimcard_create(void)38 asimcard_create( void )
39 {
40 ASimCard card = _s_card;
41 card->status = A_SIM_STATUS_READY;
42 card->pin_retries = 0;
43 strncpy( card->pin, "0000", sizeof(card->pin) );
44 strncpy( card->puk, "12345678", sizeof(card->puk) );
45 return card;
46 }
47
48 void
asimcard_destroy(ASimCard card)49 asimcard_destroy( ASimCard card )
50 {
51 /* nothing really */
52 card=card;
53 }
54
55 static __inline__ int
asimcard_ready(ASimCard card)56 asimcard_ready( ASimCard card )
57 {
58 return card->status == A_SIM_STATUS_READY;
59 }
60
61 ASimStatus
asimcard_get_status(ASimCard sim)62 asimcard_get_status( ASimCard sim )
63 {
64 return sim->status;
65 }
66
67 void
asimcard_set_status(ASimCard sim,ASimStatus status)68 asimcard_set_status( ASimCard sim, ASimStatus status )
69 {
70 sim->status = status;
71 }
72
73 const char*
asimcard_get_pin(ASimCard sim)74 asimcard_get_pin( ASimCard sim )
75 {
76 return sim->pin;
77 }
78
79 const char*
asimcard_get_puk(ASimCard sim)80 asimcard_get_puk( ASimCard sim )
81 {
82 return sim->puk;
83 }
84
85 void
asimcard_set_pin(ASimCard sim,const char * pin)86 asimcard_set_pin( ASimCard sim, const char* pin )
87 {
88 strncpy( sim->pin, pin, A_SIM_PIN_SIZE );
89 sim->pin_retries = 0;
90 }
91
92 void
asimcard_set_puk(ASimCard sim,const char * puk)93 asimcard_set_puk( ASimCard sim, const char* puk )
94 {
95 strncpy( sim->puk, puk, A_SIM_PUK_SIZE );
96 sim->pin_retries = 0;
97 }
98
99
100 int
asimcard_check_pin(ASimCard sim,const char * pin)101 asimcard_check_pin( ASimCard sim, const char* pin )
102 {
103 if (sim->status != A_SIM_STATUS_PIN &&
104 sim->status != A_SIM_STATUS_READY )
105 return 0;
106
107 if ( !strcmp( sim->pin, pin ) ) {
108 sim->status = A_SIM_STATUS_READY;
109 sim->pin_retries = 0;
110 return 1;
111 }
112
113 if (sim->status != A_SIM_STATUS_READY) {
114 if (++sim->pin_retries == 3)
115 sim->status = A_SIM_STATUS_PUK;
116 }
117 return 0;
118 }
119
120
121 int
asimcard_check_puk(ASimCard sim,const char * puk,const char * pin)122 asimcard_check_puk( ASimCard sim, const char* puk, const char* pin )
123 {
124 if (sim->status != A_SIM_STATUS_PUK)
125 return 0;
126
127 if ( !strcmp( sim->puk, puk ) ) {
128 strncpy( sim->puk, puk, A_SIM_PUK_SIZE );
129 strncpy( sim->pin, pin, A_SIM_PIN_SIZE );
130 sim->status = A_SIM_STATUS_READY;
131 sim->pin_retries = 0;
132 return 1;
133 }
134
135 if ( ++sim->pin_retries == 6 ) {
136 sim->status = A_SIM_STATUS_ABSENT;
137 }
138 return 0;
139 }
140
141 typedef enum {
142 SIM_FILE_DM = 0,
143 SIM_FILE_DF,
144 SIM_FILE_EF_DEDICATED,
145 SIM_FILE_EF_LINEAR,
146 SIM_FILE_EF_CYCLIC
147 } SimFileType;
148
149 typedef enum {
150 SIM_FILE_READ_ONLY = (1 << 0),
151 SIM_FILE_NEED_PIN = (1 << 1),
152 } SimFileFlags;
153
154 /* descriptor for a known SIM File */
155 #define SIM_FILE_HEAD \
156 SimFileType type; \
157 unsigned short id; \
158 unsigned short flags;
159
160 typedef struct {
161 SIM_FILE_HEAD
162 } SimFileAnyRec, *SimFileAny;
163
164 typedef struct {
165 SIM_FILE_HEAD
166 cbytes_t data;
167 int length;
168 } SimFileEFDedicatedRec, *SimFileEFDedicated;
169
170 typedef struct {
171 SIM_FILE_HEAD
172 byte_t rec_count;
173 byte_t rec_len;
174 cbytes_t records;
175 } SimFileEFLinearRec, *SimFileEFLinear;
176
177 typedef SimFileEFLinearRec SimFileEFCyclicRec;
178 typedef SimFileEFCyclicRec* SimFileEFCyclic;
179
180 typedef union {
181 SimFileAnyRec any;
182 SimFileEFDedicatedRec dedicated;
183 SimFileEFLinearRec linear;
184 SimFileEFCyclicRec cyclic;
185 } SimFileRec, *SimFile;
186
187
188 #if ENABLE_DYNAMIC_RECORDS
189 /* convert a SIM File descriptor into an ASCII string,
190 assumes 'dst' is NULL or properly sized.
191 return the number of chars, or -1 on error */
192 static int
sim_file_to_hex(SimFile file,bytes_t dst)193 sim_file_to_hex( SimFile file, bytes_t dst )
194 {
195 SimFileType type = file->any.type;
196 int result = 0;
197
198 /* see 9.2.1 in TS 51.011 */
199 switch (type) {
200 case SIM_FILE_EF_DEDICATED:
201 case SIM_FILE_EF_LINEAR:
202 case SIM_FILE_EF_CYCLIC:
203 {
204 if (dst) {
205 int file_size, perm;
206
207 memcpy(dst, "0000", 4); /* bytes 1-2 are RFU */
208 dst += 4;
209
210 /* bytes 3-4 are the file size */
211 if (type == SIM_FILE_EF_DEDICATED)
212 file_size = file->dedicated.length;
213 else
214 file_size = file->linear.rec_count * file->linear.rec_len;
215
216 gsm_hex_from_short( dst, file_size );
217 dst += 4;
218
219 /* bytes 5-6 are the file id */
220 gsm_hex_from_short( dst, file->any.id );
221 dst += 4;
222
223 /* byte 7 is the file type - always EF, i.e. 0x04 */
224 dst[0] = '0';
225 dst[1] = '4';
226 dst += 2;
227
228 /* byte 8 is RFU, except bit 7 for cyclic files, which indicates
229 that INCREASE is allowed. Since we don't support this yet... */
230 dst[0] = '0';
231 dst[1] = '0';
232 dst += 2;
233
234 /* byte 9-11 are access conditions */
235 if (file->any.flags & SIM_FILE_READ_ONLY) {
236 if (file->any.flags & SIM_FILE_NEED_PIN)
237 perm = 0x1a;
238 else
239 perm = 0x0a;
240 } else {
241 if (file->any.flags & SIM_FILE_NEED_PIN)
242 perm = 0x11;
243 else
244 perm = 0x00;
245 }
246 gsm_hex_from_byte(dst, perm);
247 memcpy( dst+2, "a0aa", 4 );
248 dst += 6;
249
250 /* byte 12 is file status, we don't support invalidation */
251 dst[0] = '0';
252 dst[1] = '0';
253 dst += 2;
254
255 /* byte 13 is length of the following data, always 2 */
256 dst[0] = '0';
257 dst[1] = '2';
258 dst += 2;
259
260 /* byte 14 is struct of EF */
261 dst[0] = '0';
262 if (type == SIM_FILE_EF_DEDICATED)
263 dst[1] = '0';
264 else if (type == SIM_FILE_EF_LINEAR)
265 dst[1] = '1';
266 else
267 dst[1] = '3';
268
269 /* byte 15 is lenght of record, or 0 */
270 if (type == SIM_FILE_EF_DEDICATED) {
271 dst[0] = '0';
272 dst[1] = '0';
273 } else
274 gsm_hex_from_byte( dst, file->linear.rec_len );
275 }
276 result = 30;
277 }
278 break;
279
280 default:
281 result = -1;
282 }
283 return result;
284 }
285
286
287 static const byte_t _const_spn_cphs[20] = {
288 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0xff, 0xff, 0xff,
289 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
290 };
291
292 static const byte_t _const_voicemail_cphs[1] = {
293 0x55
294 };
295
296 static const byte_t _const_iccid[10] = {
297 0x98, 0x10, 0x14, 0x30, 0x12, 0x11, 0x81, 0x15, 0x70, 0x02
298 };
299
300 static const byte_t _const_cff_cphs[1] = {
301 0x55
302 };
303
304 static SimFileEFDedicatedRec _const_files_dedicated[] =
305 {
306 { SIM_FILE_EF_DEDICATED, 0x6f14, SIM_FILE_READ_ONLY | SIM_FILE_NEED_PIN,
307 _const_spn_cphs, sizeof(_const_spn_cphs) },
308
309 { SIM_FILE_EF_DEDICATED, 0x6f11, SIM_FILE_NEED_PIN,
310 _const_voicemail_cphs, sizeof(_const_voicemail_cphs) },
311
312 { SIM_FILE_EF_DEDICATED, 0x2fe2, SIM_FILE_READ_ONLY,
313 _const_iccid, sizeof(_const_iccid) },
314
315 { SIM_FILE_EF_DEDICATED, 0x6f13, SIM_FILE_NEED_PIN,
316 _const_cff_cphs, sizeof(_const_cff_cphs) },
317
318 { 0, 0, 0, NULL, 0 } /* end of list */
319 };
320 #endif /* ENABLE_DYNAMIC_RECORDS */
321
322 const char*
asimcard_io(ASimCard sim,const char * cmd)323 asimcard_io( ASimCard sim, const char* cmd )
324 {
325 int nn;
326 #if ENABLE_DYNAMIC_RECORDS
327 int command, id, p1, p2, p3;
328 #endif
329 static const struct { const char* cmd; const char* answer; } answers[] =
330 {
331 { "+CRSM=192,28436,0,0,15", "+CRSM: 144,0,000000146f1404001aa0aa01020000" },
332 { "+CRSM=176,28436,0,0,20", "+CRSM: 144,0,416e64726f6964ffffffffffffffffffffffffff" },
333
334 { "+CRSM=192,28433,0,0,15", "+CRSM: 144,0,000000016f11040011a0aa01020000" },
335 { "+CRSM=176,28433,0,0,1", "+CRSM: 144,0,55" },
336
337 { "+CRSM=192,12258,0,0,15", "+CRSM: 144,0,0000000a2fe204000fa0aa01020000" },
338 { "+CRSM=176,12258,0,0,10", "+CRSM: 144,0,98101430121181157002" },
339
340 { "+CRSM=192,28435,0,0,15", "+CRSM: 144,0,000000016f13040011a0aa01020000" },
341 { "+CRSM=176,28435,0,0,1", "+CRSM: 144,0,55" },
342
343 { "+CRSM=192,28472,0,0,15", "+CRSM: 144,0,0000000f6f3804001aa0aa01020000" },
344 { "+CRSM=176,28472,0,0,15", "+CRSM: 144,0,ff30ffff3c003c03000c0000f03f00" },
345
346 { "+CRSM=192,28617,0,0,15", "+CRSM: 144,0,000000086fc9040011a0aa01020104" },
347 { "+CRSM=178,28617,1,4,4", "+CRSM: 144,0,01000000" },
348
349 { "+CRSM=192,28618,0,0,15", "+CRSM: 144,0,0000000a6fca040011a0aa01020105" },
350 { "+CRSM=178,28618,1,4,5", "+CRSM: 144,0,0000000000" },
351
352 { "+CRSM=192,28589,0,0,15", "+CRSM: 144,0,000000046fad04000aa0aa01020000" },
353 { "+CRSM=176,28589,0,0,4", "+CRSM: 144,0,00000003" },
354
355 { "+CRSM=192,28438,0,0,15", "+CRSM: 144,0,000000026f1604001aa0aa01020000" },
356 { "+CRSM=176,28438,0,0,2", "+CRSM: 144,0,0233" },
357
358 { "+CRSM=192,28486,0,0,15", "+CRSM: 148,4" },
359 { "+CRSM=192,28621,0,0,15", "+CRSM: 148,4" },
360
361 { "+CRSM=192,28613,0,0,15", "+CRSM: 144,0,000000f06fc504000aa0aa01020118" },
362 { "+CRSM=178,28613,1,4,24", "+CRSM: 144,0,43058441aa890affffffffffffffffffffffffffffffffff" },
363
364 { "+CRSM=192,28480,0,0,15", "+CRSM: 144,0,000000806f40040011a0aa01020120" },
365 { "+CRSM=178,28480,1,4,32", "+CRSM: 144,0,ffffffffffffffffffffffffffffffffffff07815155258131f5ffffffffffff" },
366
367 { "+CRSM=192,28615,0,0,15", "+CRSM: 144,0,000000406fc7040011a0aa01020120" },
368 { "+CRSM=178,28615,1,4,32", "+CRSM: 144,0,566f6963656d61696cffffffffffffffffff07915155125740f9ffffffffffff" },
369
370 { NULL, NULL }
371 };
372
373 assert( memcmp( cmd, "+CRSM=", 6 ) == 0 );
374
375 #if ENABLE_DYNAMIC_RECORDS
376 if ( sscanf(cmd, "+CRSM=%d,%d,%d,%d,%d", &command, &id, &p1, &p2, &p3) == 5 ) {
377 switch (command) {
378 case A_SIM_CMD_GET_RESPONSE:
379 {
380 const SimFileEFDedicatedRec* file = _const_files_dedicated;
381
382 assert(p1 == 0 && p2 == 0 && p3 == 15);
383
384 for ( ; file->id != 0; file++ ) {
385 if (file->id == id) {
386 int count;
387 char* out = sim->out_buff;
388 strcpy( out, "+CRSM: 144,0," );
389 out += strlen(out);
390 count = sim_file_to_hex( (SimFile) file, out );
391 if (count < 0)
392 return "ERROR: INTERNAL SIM ERROR";
393 out[count] = 0;
394 return sim->out_buff;
395 }
396 }
397 break;
398 }
399
400 case A_SIM_CMD_READ_BINARY:
401 {
402 const SimFileEFDedicatedRec* file = _const_files_dedicated;
403
404 assert(p1 == 0 && p2 == 0);
405
406 for ( ; file->id != 0; file++ ) {
407 if (file->id == id) {
408 char* out = sim->out_buff;
409
410 if (p3 > file->length)
411 return "ERROR: BINARY LENGTH IS TOO LONG";
412
413 strcpy( out, "+CRSM: 144,0," );
414 out += strlen(out);
415 gsm_hex_from_bytes( out, file->data, p3 );
416 out[p3*2] = 0;
417 return sim->out_buff;
418 }
419 }
420 break;
421 }
422
423 case A_SIM_CMD_READ_RECORD:
424 break;
425
426 default:
427 return "ERROR: UNSUPPORTED SIM COMMAND";
428 }
429 }
430 #endif
431
432 for (nn = 0; answers[nn].cmd != NULL; nn++) {
433 if ( !strcmp( answers[nn].cmd, cmd ) ) {
434 return answers[nn].answer;
435 }
436 }
437 return "ERROR: BAD COMMAND";
438 }
439
440