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
25typedef 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
37static ASimCardRec  _s_card[1];
38
39ASimCard
40asimcard_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
51void
52asimcard_destroy( ASimCard  card )
53{
54    /* nothing really */
55    card=card;
56}
57
58static __inline__ int
59asimcard_ready( ASimCard  card )
60{
61    return card->status == A_SIM_STATUS_READY;
62}
63
64ASimStatus
65asimcard_get_status( ASimCard  sim )
66{
67    return sim->status;
68}
69
70void
71asimcard_set_status( ASimCard  sim, ASimStatus  status )
72{
73    sim->status = status;
74}
75
76const char*
77asimcard_get_pin( ASimCard  sim )
78{
79    return sim->pin;
80}
81
82const char*
83asimcard_get_puk( ASimCard  sim )
84{
85    return sim->puk;
86}
87
88void
89asimcard_set_pin( ASimCard  sim, const char*  pin )
90{
91    strncpy( sim->pin, pin, A_SIM_PIN_SIZE );
92    sim->pin_retries = 0;
93}
94
95void
96asimcard_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
103int
104asimcard_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
124int
125asimcard_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
144typedef 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
152typedef 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
163typedef struct {
164    SIM_FILE_HEAD
165} SimFileAnyRec, *SimFileAny;
166
167typedef struct {
168    SIM_FILE_HEAD
169    cbytes_t   data;
170    int        length;
171} SimFileEFDedicatedRec, *SimFileEFDedicated;
172
173typedef struct {
174    SIM_FILE_HEAD
175    byte_t     rec_count;
176    byte_t     rec_len;
177    cbytes_t   records;
178} SimFileEFLinearRec, *SimFileEFLinear;
179
180typedef SimFileEFLinearRec   SimFileEFCyclicRec;
181typedef SimFileEFCyclicRec*  SimFileEFCyclic;
182
183typedef 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 */
195static int
196sim_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
290static 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
295static const byte_t  _const_voicemail_cphs[1] = {
296    0x55
297};
298
299static const byte_t  _const_iccid[10] = {
300    0x98, 0x10, 0x14, 0x30, 0x12, 0x11, 0x81, 0x15, 0x70, 0x02
301};
302
303static const byte_t  _const_cff_cphs[1] = {
304    0x55
305};
306
307static 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
325const char*
326asimcard_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