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 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 49 asimcard_destroy( ASimCard card ) 50 { 51 /* nothing really */ 52 card=card; 53 } 54 55 static __inline__ int 56 asimcard_ready( ASimCard card ) 57 { 58 return card->status == A_SIM_STATUS_READY; 59 } 60 61 ASimStatus 62 asimcard_get_status( ASimCard sim ) 63 { 64 return sim->status; 65 } 66 67 void 68 asimcard_set_status( ASimCard sim, ASimStatus status ) 69 { 70 sim->status = status; 71 } 72 73 const char* 74 asimcard_get_pin( ASimCard sim ) 75 { 76 return sim->pin; 77 } 78 79 const char* 80 asimcard_get_puk( ASimCard sim ) 81 { 82 return sim->puk; 83 } 84 85 void 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 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 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 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 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* 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