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