Coverage Report

Created: 2025-12-14 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensc/src/libopensc/card-tcos.c
Line
Count
Source
1
/*
2
 * card-tcos.c: Support for TCOS cards
3
 *
4
 * Copyright (C) 2011  Peter Koch <pk@opensc-project.org>
5
 * Copyright (C) 2002  g10 Code GmbH
6
 * Copyright (C) 2001  Juha Yrjölä <juha.yrjola@iki.fi>
7
 *
8
 * This library is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * This library is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with this library; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
 */
22
23
#ifdef HAVE_CONFIG_H
24
#include "config.h"
25
#endif
26
27
#include <string.h>
28
#include <ctype.h>
29
#include <time.h>
30
#include <stdlib.h>
31
32
#include "internal.h"
33
#include "asn1.h"
34
#include "cardctl.h"
35
36
static const struct sc_atr_table tcos_atrs[] = {
37
  /* Infineon SLE44 */
38
  { "3B:BA:13:00:81:31:86:5D:00:64:05:0A:02:01:31:80:90:00:8B", NULL, NULL, SC_CARD_TYPE_TCOS_V2, 0, NULL },
39
  /* Infineon SLE66S */
40
  { "3B:BA:14:00:81:31:86:5D:00:64:05:14:02:02:31:80:90:00:91", NULL, NULL, SC_CARD_TYPE_TCOS_V2, 0, NULL },
41
  /* Infineon SLE66CX320P */
42
  { "3B:BA:96:00:81:31:86:5D:00:64:05:60:02:03:31:80:90:00:66", NULL, NULL, SC_CARD_TYPE_TCOS_V2, 0, NULL },
43
  /* Infineon SLE66CX322P */
44
  { "3B:BA:96:00:81:31:86:5D:00:64:05:7B:02:03:31:80:90:00:7D", NULL, NULL, SC_CARD_TYPE_TCOS_V2, 0, NULL },
45
  /* Philips P5CT072 */
46
  { "3B:BF:96:00:81:31:FE:5D:00:64:04:11:03:01:31:C0:73:F7:01:D0:00:90:00:7D", NULL, NULL, SC_CARD_TYPE_TCOS_V3, 0, NULL },
47
  { "3B:BF:96:00:81:31:FE:5D:00:64:04:11:04:0F:31:C0:73:F7:01:D0:00:90:00:74", NULL, NULL, SC_CARD_TYPE_TCOS_V3, 0, NULL },
48
  /* Philips P5CT080 */
49
  { "3B:BF:B6:00:81:31:FE:5D:00:64:04:28:03:02:31:C0:73:F7:01:D0:00:90:00:67", NULL, NULL, SC_CARD_TYPE_TCOS_V3, 0, NULL },
50
  { NULL, NULL, NULL, 0, 0, NULL }
51
};
52
53
static struct sc_card_operations tcos_ops;
54
static struct sc_card_driver tcos_drv = {
55
  "TCOS 3.0",
56
  "tcos",
57
  &tcos_ops,
58
  NULL, 0, NULL
59
};
60
61
static const struct sc_card_operations *iso_ops = NULL;
62
63
typedef struct tcos_data_st {
64
  unsigned long pad_flags;
65
  unsigned int next_sign;
66
} tcos_data;
67
68
69
static int tcos_finish(sc_card_t *card)
70
1.02k
{
71
1.02k
  free(card->drv_data);
72
1.02k
  return 0;
73
1.02k
}
74
75
76
static int tcos_match_card(sc_card_t *card)
77
12.2k
{
78
12.2k
  int i;
79
80
12.2k
  i = _sc_match_atr(card, tcos_atrs, &card->type);
81
12.2k
  if (i < 0)
82
11.2k
    return 0;
83
1.02k
  return 1;
84
12.2k
}
85
86
87
static int tcos_init(sc_card_t *card)
88
1.02k
{
89
1.02k
  unsigned long flags;
90
91
1.02k
  tcos_data *data = malloc(sizeof(tcos_data));
92
1.02k
  if (!data) return SC_ERROR_OUT_OF_MEMORY;
93
94
1.02k
  card->name = "TCOS";
95
1.02k
  card->drv_data = (void *)data;
96
1.02k
  card->cla = 0x00;
97
98
1.02k
  flags = SC_ALGORITHM_RSA_RAW;
99
1.02k
  flags |= SC_ALGORITHM_RSA_PAD_PKCS1;
100
1.02k
  flags |= SC_ALGORITHM_RSA_HASH_NONE;
101
102
1.02k
  _sc_card_add_rsa_alg(card, 512, flags, 0);
103
1.02k
  _sc_card_add_rsa_alg(card, 768, flags, 0);
104
1.02k
  _sc_card_add_rsa_alg(card, 1024, flags, 0);
105
106
1.02k
  if (card->type == SC_CARD_TYPE_TCOS_V3) {
107
436
    card->caps |= SC_CARD_CAP_APDU_EXT;
108
436
    _sc_card_add_rsa_alg(card, 1280, flags, 0);
109
436
    _sc_card_add_rsa_alg(card, 1536, flags, 0);
110
436
    _sc_card_add_rsa_alg(card, 1792, flags, 0);
111
436
    _sc_card_add_rsa_alg(card, 2048, flags, 0);
112
436
  }
113
114
1.02k
  return 0;
115
1.02k
}
116
117
118
/* Hmmm, I don't know what to do.  It seems that the ACL design of
119
   OpenSC should be enhanced to allow for the command based security
120
   attributes of TCOS.  FIXME: This just allows to create a very basic
121
   file. */
122
static int tcos_construct_fci(const sc_file_t *file,
123
                              u8 *out, size_t *outlen)
124
0
{
125
0
  u8 *p = out;
126
0
  u8 buf[64];
127
0
  size_t n;
128
129
  /* FIXME: possible buffer overflow */
130
131
0
  *p++ = 0x6F; /* FCI */
132
0
  p++;
133
134
  /* File size */
135
0
  buf[0] = (file->size >> 8) & 0xFF;
136
0
  buf[1] = file->size & 0xFF;
137
0
  sc_asn1_put_tag(0x81, buf, 2, p, 16, &p);
138
139
  /* File descriptor */
140
0
  n = 0;
141
0
  buf[n] = file->shareable ? 0x40 : 0;
142
0
  switch (file->type) {
143
0
  case SC_FILE_TYPE_WORKING_EF:
144
0
    break;
145
0
  case SC_FILE_TYPE_DF:
146
0
    buf[0] |= 0x38;
147
0
    break;
148
0
  default:
149
0
    return SC_ERROR_NOT_SUPPORTED;
150
0
  }
151
0
  buf[n++] |= file->ef_structure & 7;
152
0
  if ( (file->ef_structure & 7) > 1) {
153
    /* record structured file */
154
0
    buf[n++] = 0x41; /* indicate 3rd byte */
155
0
    buf[n++] = file->record_length;
156
0
  }
157
0
  sc_asn1_put_tag(0x82, buf, n, p, 8, &p);
158
159
  /* File identifier */
160
0
  buf[0] = (file->id >> 8) & 0xFF;
161
0
  buf[1] = file->id & 0xFF;
162
0
  sc_asn1_put_tag(0x83, buf, 2, p, 16, &p);
163
164
  /* Directory name */
165
0
  if (file->type == SC_FILE_TYPE_DF) {
166
0
    if (file->namelen) {
167
0
      sc_asn1_put_tag(0x84, file->name, file->namelen,
168
0
                                        p, 16, &p);
169
0
    } else {
170
      /* TCOS needs one, so we use a faked one */
171
0
      snprintf ((char *) buf, sizeof(buf)-1, "foo-%lu",
172
0
                                  (unsigned long) time (NULL));
173
0
      sc_asn1_put_tag(0x84, buf, strlen ((char *) buf), p, 16, &p);
174
0
    }
175
0
  }
176
177
  /* File descriptor extension */
178
0
  if (file->prop_attr_len && file->prop_attr) {
179
0
    n = file->prop_attr_len;
180
0
    memcpy(buf, file->prop_attr, n);
181
0
  } else {
182
0
    n = 0;
183
0
    buf[n++] = 0x01; /* not invalidated, permanent */
184
0
    if (file->type == SC_FILE_TYPE_WORKING_EF)
185
0
      buf[n++] = 0x00; /* generic data file */
186
0
  }
187
0
  sc_asn1_put_tag(0x85, buf, n, p, 16, &p);
188
189
  /* Security attributes */
190
0
  if (file->sec_attr_len && file->sec_attr) {
191
0
    memcpy(buf, file->sec_attr, file->sec_attr_len);
192
0
    n = file->sec_attr_len;
193
0
  } else {
194
    /* no attributes given - fall back to default one */
195
0
    memcpy (buf+ 0, "\xa4\x00\x00\x00\xff\xff", 6); /* select */
196
0
    memcpy (buf+ 6, "\xb0\x00\x00\x00\xff\xff", 6); /* read bin */
197
0
    memcpy (buf+12, "\xd6\x00\x00\x00\xff\xff", 6); /* upd bin */
198
0
    memcpy (buf+18, "\x60\x00\x00\x00\xff\xff", 6); /* admin grp*/
199
0
    n = 24;
200
0
  }
201
0
  sc_asn1_put_tag(0x86, buf, n, p, sizeof (buf), &p);
202
203
204
  /* fixup length of FCI */
205
0
  out[1] = p - out - 2;
206
207
0
  *outlen = p - out;
208
0
  return 0;
209
0
}
210
211
212
static int tcos_create_file(sc_card_t *card, sc_file_t *file)
213
0
{
214
0
  int r;
215
0
  size_t len;
216
0
  u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
217
0
  sc_apdu_t apdu;
218
219
0
  len = SC_MAX_APDU_BUFFER_SIZE;
220
0
  r = tcos_construct_fci(file, sbuf, &len);
221
0
  LOG_TEST_RET(card->ctx, r, "tcos_construct_fci() failed");
222
223
0
  sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00);
224
0
  apdu.cla |= 0x80;  /* this is an proprietary extension */
225
0
  apdu.lc = len;
226
0
  apdu.datalen = len;
227
0
  apdu.data = sbuf;
228
229
0
  r = sc_transmit_apdu(card, &apdu);
230
0
  LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
231
0
  return sc_check_sw(card, apdu.sw1, apdu.sw2);
232
0
}
233
234
235
static unsigned int map_operations (int commandbyte)
236
1.88k
{
237
1.88k
  unsigned int op = (unsigned int)-1;
238
239
1.88k
  switch ( (commandbyte & 0xfe) ) {
240
67
    case 0xe2: /* append record */   op = SC_AC_OP_UPDATE; break;
241
91
    case 0x24: /* change password */ op = SC_AC_OP_UPDATE; break;
242
79
    case 0xe0: /* create */          op = SC_AC_OP_CREATE; break;
243
80
    case 0xe4: /* delete */          op = SC_AC_OP_DELETE; break;
244
66
    case 0xe8: /* exclude sfi */     op = SC_AC_OP_WRITE; break;
245
130
    case 0x82: /* external auth */   op = SC_AC_OP_READ; break;
246
68
    case 0xe6: /* include sfi */     op = SC_AC_OP_WRITE; break;
247
73
    case 0x88: /* internal auth */   op = SC_AC_OP_READ; break;
248
119
    case 0x04: /* invalidate */      op = SC_AC_OP_INVALIDATE; break;
249
72
    case 0x2a: /* perform sec. op */ op = SC_AC_OP_SELECT; break;
250
72
    case 0xb0: /* read binary */     op = SC_AC_OP_READ; break;
251
67
    case 0xb2: /* read record */     op = SC_AC_OP_READ; break;
252
73
    case 0x44: /* rehabilitate */    op = SC_AC_OP_REHABILITATE; break;
253
77
    case 0xa4: /* select */          op = SC_AC_OP_SELECT; break;
254
74
    case 0xee: /* set permanent */   op = SC_AC_OP_CREATE; break;
255
74
    case 0x2c: /* unblock password */op = SC_AC_OP_WRITE; break;
256
77
    case 0xd6: /* update binary */   op = SC_AC_OP_WRITE; break;
257
69
    case 0xdc: /* update record */   op = SC_AC_OP_WRITE; break;
258
66
    case 0x20: /* verify password */ op = SC_AC_OP_SELECT; break;
259
106
    case 0x60: /* admin group */     op = SC_AC_OP_CREATE; break;
260
1.88k
  }
261
1.88k
  return op;
262
1.88k
}
263
264
265
/* Hmmm, I don't know what to do.  It seems that the ACL design of
266
   OpenSC should be enhanced to allow for the command based security
267
   attributes of TCOS.  FIXME: This just allows to create a very basic
268
   file. */
269
static void parse_sec_attr(sc_card_t *card,
270
                           sc_file_t *file, const u8 *buf, size_t len)
271
1.34k
{
272
1.34k
  unsigned int op;
273
274
  /* list directory is not covered by ACLs - so always add an entry */
275
1.34k
  sc_file_add_acl_entry (file, SC_AC_OP_LIST_FILES,
276
1.34k
                               SC_AC_NONE, SC_AC_KEY_REF_NONE);
277
  /* FIXME: check for what LOCK is used */
278
1.34k
  sc_file_add_acl_entry (file, SC_AC_OP_LOCK,
279
1.34k
                               SC_AC_NONE, SC_AC_KEY_REF_NONE);
280
3.32k
  for (; len >= 6; len -= 6, buf += 6) {
281
    /* FIXME: temporary hacks */
282
1.97k
    if (!memcmp(buf, "\xa4\x00\x00\x00\xff\xff", 6)) {/* select */
283
18
      sc_file_add_acl_entry (file, SC_AC_OP_SELECT,
284
18
                                               SC_AC_NONE, SC_AC_KEY_REF_NONE);
285
1.96k
    } else if (!memcmp(buf, "\xb0\x00\x00\x00\xff\xff", 6)) {/*read*/
286
10
      sc_file_add_acl_entry (file, SC_AC_OP_READ,
287
10
                                               SC_AC_NONE, SC_AC_KEY_REF_NONE);
288
1.95k
    } else if (!memcmp(buf, "\xd6\x00\x00\x00\xff\xff", 6)) {/*upd*/
289
44
      sc_file_add_acl_entry (file, SC_AC_OP_UPDATE,
290
44
                                               SC_AC_NONE, SC_AC_KEY_REF_NONE);
291
1.90k
    } else if (!memcmp(buf, "\x60\x00\x00\x00\xff\xff", 6)) {/*adm */
292
20
      sc_file_add_acl_entry (file, SC_AC_OP_WRITE,
293
20
                                               SC_AC_NONE, SC_AC_KEY_REF_NONE);
294
20
      sc_file_add_acl_entry (file, SC_AC_OP_CREATE,
295
20
                                               SC_AC_NONE, SC_AC_KEY_REF_NONE);
296
20
      sc_file_add_acl_entry (file, SC_AC_OP_INVALIDATE,
297
20
                                               SC_AC_NONE, SC_AC_KEY_REF_NONE);
298
20
      sc_file_add_acl_entry (file, SC_AC_OP_REHABILITATE,
299
20
                                               SC_AC_NONE, SC_AC_KEY_REF_NONE);
300
1.88k
    } else {
301
      /* the first byte tells use the command or the
302
         command group.  We have to mask bit 0
303
         because this one distinguish between AND/OR
304
         combination of PINs*/
305
1.88k
      op = map_operations (buf[0]);
306
1.88k
      if (op == (unsigned int)-1) {
307
286
        sc_log(card->ctx,
308
286
          "Unknown security command byte %02x\n",
309
286
          buf[0]);
310
286
        continue;
311
286
      }
312
1.60k
      if (!buf[1])
313
122
        sc_file_add_acl_entry (file, op,
314
122
                                                       SC_AC_NONE,
315
122
                                                       SC_AC_KEY_REF_NONE);
316
1.47k
      else
317
1.47k
        sc_file_add_acl_entry (file, op,
318
1.47k
                                                       SC_AC_CHV, buf[1]);
319
320
1.60k
      if (!buf[2] && !buf[3])
321
63
        sc_file_add_acl_entry (file, op,
322
63
                                                       SC_AC_NONE,
323
63
                                                       SC_AC_KEY_REF_NONE);
324
1.53k
      else
325
1.53k
        sc_file_add_acl_entry (file, op,
326
1.53k
                                                       SC_AC_TERM,
327
1.53k
                                                       (buf[2]<<8)|buf[3]);
328
1.60k
    }
329
1.97k
  }
330
1.34k
}
331
332
333
static int tcos_select_file(sc_card_t *card,
334
                            const sc_path_t *in_path,
335
                            sc_file_t **file_out)
336
15.4k
{
337
15.4k
  sc_context_t *ctx;
338
15.4k
  sc_apdu_t apdu;
339
15.4k
  sc_file_t *file=NULL;
340
15.4k
  u8 buf[SC_MAX_APDU_BUFFER_SIZE], pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf;
341
15.4k
  int r;
342
15.4k
  size_t pathlen;
343
344
15.4k
  assert(card != NULL && in_path != NULL);
345
15.4k
  ctx=card->ctx;
346
15.4k
  memcpy(path, in_path->value, in_path->len);
347
15.4k
  pathlen = in_path->len;
348
349
15.4k
  sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0x04);
350
351
15.4k
  switch (in_path->type) {
352
0
  case SC_PATH_TYPE_FILE_ID:
353
0
    if (pathlen != 2) return SC_ERROR_INVALID_ARGUMENTS;
354
    /* fall through */
355
0
  case SC_PATH_TYPE_FROM_CURRENT:
356
0
    apdu.p1 = 9;
357
0
    break;
358
2.20k
  case SC_PATH_TYPE_DF_NAME:
359
2.20k
    apdu.p1 = 4;
360
2.20k
    break;
361
13.2k
  case SC_PATH_TYPE_PATH:
362
13.2k
    apdu.p1 = 8;
363
13.2k
    if (pathlen >= 2 && memcmp(path, "\x3F\x00", 2) == 0) path += 2, pathlen -= 2;
364
13.2k
    if (pathlen == 0) apdu.p1 = 0;
365
13.2k
    break;
366
0
  case SC_PATH_TYPE_PARENT:
367
0
    apdu.p1 = 3;
368
0
    pathlen = 0;
369
0
    break;
370
0
  default:
371
0
    SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS);
372
15.4k
  }
373
15.4k
  if( pathlen == 0 ) apdu.cse = SC_APDU_CASE_2_SHORT;
374
375
15.4k
  apdu.lc = pathlen;
376
15.4k
  apdu.data = path;
377
15.4k
  apdu.datalen = pathlen;
378
379
15.4k
  if (file_out != NULL) {
380
4.65k
    apdu.resp = buf;
381
4.65k
    apdu.resplen = sizeof(buf);
382
4.65k
    apdu.le = 256;
383
10.7k
  } else {
384
10.7k
    apdu.resplen = 0;
385
10.7k
    apdu.le = 0;
386
10.7k
    apdu.p2 = 0x0C;
387
10.7k
    apdu.cse = (pathlen == 0) ? SC_APDU_CASE_1 : SC_APDU_CASE_3_SHORT;
388
10.7k
  }
389
390
15.4k
  r = sc_transmit_apdu(card, &apdu);
391
15.4k
  LOG_TEST_RET(ctx, r, "APDU transmit failed");
392
15.3k
  r = sc_check_sw(card, apdu.sw1, apdu.sw2);
393
15.3k
  if (r || file_out == NULL) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r);
394
395
1.60k
  if (apdu.resplen < 1 || apdu.resp[0] != 0x62) {
396
258
    sc_log(ctx, "received invalid template %02X\n", apdu.resp[0]);
397
258
    SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED);
398
258
  }
399
400
1.34k
  file = sc_file_new();
401
1.34k
  if (file == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
402
1.34k
  *file_out = file;
403
1.34k
  file->path = *in_path;
404
405
1.34k
  iso_ops->process_fci(card, file, apdu.resp, apdu.resplen);
406
407
1.34k
  parse_sec_attr(card, file, file->sec_attr, file->sec_attr_len);
408
409
1.34k
  return 0;
410
1.34k
}
411
412
413
static int tcos_list_files(sc_card_t *card, u8 *buf, size_t buflen)
414
0
{
415
0
  sc_context_t *ctx;
416
0
  sc_apdu_t apdu;
417
0
  u8 rbuf[SC_MAX_APDU_BUFFER_SIZE], p1;
418
0
  int r, count = 0;
419
420
0
  assert(card != NULL);
421
0
  ctx = card->ctx;
422
423
0
  for (p1=1; p1<=2; p1++) {
424
0
    sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xAA, p1, 0);
425
0
    apdu.cla = 0x80;
426
0
    apdu.resp = rbuf;
427
0
    apdu.resplen = sizeof(rbuf);
428
0
    apdu.le = 256;
429
0
    r = sc_transmit_apdu(card, &apdu);
430
0
    LOG_TEST_RET(ctx, r, "APDU transmit failed");
431
0
    if (apdu.sw1==0x6A && (apdu.sw2==0x82 || apdu.sw2==0x88)) continue;
432
0
    r = sc_check_sw(card, apdu.sw1, apdu.sw2);
433
0
    LOG_TEST_RET(ctx, r, "List Dir failed");
434
0
    if (apdu.resplen > buflen) return SC_ERROR_BUFFER_TOO_SMALL;
435
0
    sc_log(ctx,
436
0
      "got %"SC_FORMAT_LEN_SIZE_T"u %s-FileIDs\n",
437
0
      apdu.resplen / 2, p1 == 1 ? "DF" : "EF");
438
439
0
    memcpy(buf, apdu.resp, apdu.resplen);
440
0
    buf += apdu.resplen;
441
0
    buflen -= apdu.resplen;
442
0
    count += apdu.resplen;
443
0
  }
444
0
  return count;
445
0
}
446
447
448
static int tcos_delete_file(sc_card_t *card, const sc_path_t *path)
449
0
{
450
0
  int r;
451
0
  u8 sbuf[2];
452
0
  sc_apdu_t apdu;
453
454
0
  SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
455
0
  if (path->type != SC_PATH_TYPE_FILE_ID && path->len != 2) {
456
0
    sc_log(card->ctx, "File type has to be SC_PATH_TYPE_FILE_ID\n");
457
0
    LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
458
0
  }
459
0
  sbuf[0] = path->value[0];
460
0
  sbuf[1] = path->value[1];
461
0
  sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00);
462
0
  apdu.cla |= 0x80;
463
0
  apdu.lc = 2;
464
0
  apdu.datalen = 2;
465
0
  apdu.data = sbuf;
466
467
0
  r = sc_transmit_apdu(card, &apdu);
468
0
  LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
469
0
  return sc_check_sw(card, apdu.sw1, apdu.sw2);
470
0
}
471
472
473
static int tcos_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num)
474
279
{
475
279
  sc_context_t *ctx;
476
279
  sc_apdu_t apdu;
477
279
  u8 sbuf[SC_MAX_APDU_BUFFER_SIZE], *p;
478
279
  int r, default_key, tcos3;
479
279
  tcos_data *data;
480
481
279
  assert(card != NULL && env != NULL);
482
279
  ctx = card->ctx;
483
279
  tcos3=(card->type==SC_CARD_TYPE_TCOS_V3);
484
279
  data=(tcos_data *)card->drv_data;
485
486
279
  if (se_num || (env->operation!=SC_SEC_OPERATION_DECIPHER && env->operation!=SC_SEC_OPERATION_SIGN)) {
487
0
    LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
488
0
  }
489
279
  if(!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT))
490
0
    sc_log(ctx,
491
279
      "No Key-Reference in SecEnvironment\n");
492
279
  else
493
279
    sc_log(ctx,
494
279
      "Key-Reference %02X (len=%"SC_FORMAT_LEN_SIZE_T"u)\n",
495
279
      env->key_ref[0], env->key_ref_len);
496
  /* Key-Reference 0x80 ?? */
497
279
  default_key= !(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || (env->key_ref_len==1 && env->key_ref[0]==0x80);
498
558
  sc_log(ctx, "TCOS3:%d PKCS1 type 01:%d PKCS1 type 02: %d\n", tcos3,
499
558
      !!(env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01),
500
558
      !!(env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02));
501
502
279
  data->pad_flags = env->algorithm_flags;
503
279
  data->next_sign = default_key;
504
505
279
  sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, tcos3 ? 0x41 : 0xC1, 0xB8);
506
279
  p = sbuf;
507
279
  if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) {
508
279
    *p++ = (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC) ? 0x83 : 0x84;
509
279
    *p++ = env->key_ref_len;
510
279
    memcpy(p, env->key_ref, env->key_ref_len);
511
279
    p += env->key_ref_len;
512
279
  }
513
279
  apdu.data = sbuf;
514
279
  apdu.lc = apdu.datalen = (p - sbuf);
515
516
279
  r=sc_transmit_apdu(card, &apdu);
517
279
  if (r) {
518
18
    sc_log(ctx,
519
18
      "%s: APDU transmit failed", sc_strerror(r));
520
18
    return r;
521
18
  }
522
261
  if (apdu.sw1==0x6A && (apdu.sw2==0x81 || apdu.sw2==0x88)) {
523
6
    sc_log(ctx,
524
6
      "Detected Signature-Only key\n");
525
6
    if (env->operation==SC_SEC_OPERATION_SIGN && default_key) return SC_SUCCESS;
526
6
  }
527
260
  SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
528
260
}
529
530
531
static int tcos_restore_security_env(sc_card_t *card, int se_num)
532
0
{
533
0
  return 0;
534
0
}
535
536
537
static int tcos_compute_signature(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen)
538
106
{
539
106
  size_t dlen = datalen;
540
106
  sc_apdu_t apdu;
541
106
  u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
542
106
  u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
543
106
  int tcos3, r;
544
545
106
  if (card == NULL || data == NULL || out == NULL) {
546
0
    return SC_ERROR_INVALID_ARGUMENTS;
547
0
  }
548
549
106
  tcos3 = (card->type == SC_CARD_TYPE_TCOS_V3);
550
551
  // We can sign (key length / 8) bytes
552
106
  if (datalen > 256) {
553
5
    SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS);
554
5
  }
555
556
101
  if (((tcos_data *)card->drv_data)->next_sign) {
557
15
    if (datalen > 48) {
558
13
      sc_log(card->ctx, "Data to be signed is too long (TCOS supports max. 48 bytes)\n");
559
13
      SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS);
560
13
    }
561
2
    sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A);
562
2
    memcpy(sbuf, data, datalen);
563
2
    dlen = datalen;
564
86
  } else {
565
86
    size_t keylen = tcos3 ? 256 : 128;
566
567
86
    if (datalen > keylen) {
568
3
      SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS);
569
3
    }
570
571
83
    sc_format_apdu(card, &apdu, keylen > 255 ? SC_APDU_CASE_4_EXT : SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86);
572
83
    memset(sbuf, 0xff, sizeof(sbuf));
573
83
    sbuf[0] = 0x02;
574
83
    sbuf[1] = 0x00;
575
83
    sbuf[2] = 0x01;
576
83
    sbuf[keylen - datalen] = 0x00;
577
83
    memcpy(sbuf + keylen - datalen + 1, data, datalen);
578
83
    dlen = keylen + 1;
579
83
  }
580
85
  apdu.resp = rbuf;
581
85
  apdu.resplen = sizeof(rbuf);
582
85
  apdu.le = tcos3 ? 256 : 128;
583
85
  apdu.data = sbuf;
584
85
  apdu.lc = apdu.datalen = dlen;
585
586
85
  r = sc_transmit_apdu(card, &apdu);
587
85
  LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
588
74
  if (tcos3 && apdu.p1 == 0x80 && apdu.sw1 == 0x6A && apdu.sw2 == 0x87) {
589
2
    size_t keylen = 128;
590
591
2
    if (datalen > keylen) {
592
1
      SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS);
593
1
    }
594
595
1
    sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86);
596
1
    memset(sbuf, 0xff, sizeof(sbuf));
597
1
    sbuf[0] = 0x02;
598
1
    sbuf[1] = 0x00;
599
1
    sbuf[2] = 0x01;
600
1
    sbuf[keylen - datalen] = 0x00;
601
1
    memcpy(sbuf + keylen - datalen + 1, data, datalen);
602
1
    dlen = keylen + 1;
603
604
1
    apdu.resp = rbuf;
605
1
    apdu.resplen = sizeof(rbuf);
606
1
    apdu.le = 128;
607
1
    apdu.data = sbuf;
608
1
    apdu.lc = apdu.datalen = dlen;
609
1
    r = sc_transmit_apdu(card, &apdu);
610
1
    LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
611
1
  }
612
73
  if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
613
33
    size_t len = apdu.resplen>outlen ? outlen : apdu.resplen;
614
33
    memcpy(out, apdu.resp, len);
615
33
    SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)len);
616
33
  }
617
40
  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
618
40
}
619
620
621
static int tcos_decipher(sc_card_t *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen)
622
95
{
623
95
  sc_context_t *ctx;
624
95
  sc_apdu_t apdu;
625
95
  u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
626
95
  u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
627
95
  tcos_data *data;
628
95
  int tcos3, r;
629
630
95
  if (card == NULL || crgram == NULL || out == NULL) {
631
0
    return SC_ERROR_INVALID_ARGUMENTS;
632
0
  }
633
95
  ctx = card->ctx;
634
95
  tcos3 = (card->type == SC_CARD_TYPE_TCOS_V3);
635
95
  data = (tcos_data *)card->drv_data;
636
637
95
  LOG_FUNC_CALLED(ctx);
638
95
  sc_log(ctx, "TCOS3:%d PKCS1 type 02:%d\n", tcos3,
639
95
      !!(data->pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02));
640
641
95
  sc_format_apdu(card, &apdu, crgram_len > 255 ? SC_APDU_CASE_4_EXT : SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86);
642
95
  apdu.resp = rbuf;
643
95
  apdu.resplen = sizeof(rbuf);
644
95
  apdu.le = crgram_len;
645
646
95
  apdu.data = sbuf;
647
95
  apdu.lc = apdu.datalen = crgram_len + 1;
648
95
  sbuf[0] = tcos3 ? 0x00 : ((data->pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02) ? 0x81 : 0x02);
649
95
  if (sizeof sbuf - 1 < crgram_len)
650
13
    return SC_ERROR_INVALID_ARGUMENTS;
651
82
  memcpy(sbuf + 1, crgram, crgram_len);
652
653
82
  r = sc_transmit_apdu(card, &apdu);
654
82
  LOG_TEST_RET(ctx, r, "APDU transmit failed");
655
656
62
  if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
657
39
    size_t len = (apdu.resplen > outlen) ? outlen : apdu.resplen;
658
39
    unsigned int offset = 0;
659
660
39
    if (tcos3 && (data->pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02)
661
0
        && len > 2 && apdu.resp[0] == 0 && apdu.resp[1] == 2) {
662
0
      offset = 2;
663
0
      while (offset < len && apdu.resp[offset] != 0)
664
0
        ++offset;
665
0
      offset = (offset < len - 1) ? offset + 1 : 0;
666
0
    }
667
39
    if (offset < len)
668
25
        memcpy(out, apdu.resp + offset, len - offset);
669
39
    SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, (int)(len - offset));
670
39
  }
671
23
  SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
672
23
}
673
674
675
/* Issue the SET PERMANENT command.  With ENABLE_NULLPIN set the
676
   NullPIN method will be activated, otherwise the permanent operation
677
   will be done on the active file. */
678
static int tcos_setperm(sc_card_t *card, int enable_nullpin)
679
0
{
680
0
  int r;
681
0
  sc_apdu_t apdu;
682
683
0
  SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
684
0
  sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xEE, 0x00, 0x00);
685
0
  apdu.cla |= 0x80;
686
0
  apdu.lc = 0;
687
0
  apdu.datalen = 0;
688
0
  apdu.data = NULL;
689
690
0
  r = sc_transmit_apdu(card, &apdu);
691
0
  LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
692
0
  return sc_check_sw(card, apdu.sw1, apdu.sw2);
693
0
}
694
695
696
static int tcos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial)
697
1.02k
{
698
1.02k
  int r;
699
700
1.02k
  if (!serial)
701
0
    return SC_ERROR_INVALID_ARGUMENTS;
702
703
  /* see if we have cached serial number */
704
1.02k
  if (card->serialnr.len) {
705
1
    memcpy(serial, &card->serialnr, sizeof(*serial));
706
1
    return SC_SUCCESS;
707
1
  }
708
709
1.02k
  card->serialnr.len = sizeof card->serialnr.value;
710
1.02k
  r = sc_parse_ef_gdo(card, card->serialnr.value, &card->serialnr.len, NULL, 0);
711
1.02k
  if (r < 0) {
712
465
    card->serialnr.len = 0;
713
465
    return r;
714
465
  }
715
716
  /* copy and return serial number */
717
561
  memcpy(serial, &card->serialnr, sizeof(*serial));
718
719
561
  return SC_SUCCESS;
720
1.02k
}
721
722
723
static int tcos_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
724
1.02k
{
725
1.02k
  switch (cmd) {
726
0
  case SC_CARDCTL_TCOS_SETPERM:
727
0
    return tcos_setperm(card, !!ptr);
728
1.02k
  case SC_CARDCTL_GET_SERIALNR:
729
1.02k
    return tcos_get_serialnr(card, (sc_serial_number_t *)ptr);
730
1.02k
  }
731
0
  return SC_ERROR_NOT_SUPPORTED;
732
1.02k
}
733
734
735
struct sc_card_driver * sc_get_tcos_driver(void)
736
15.5k
{
737
15.5k
  struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
738
739
15.5k
  if (iso_ops == NULL) iso_ops = iso_drv->ops;
740
15.5k
  tcos_ops = *iso_drv->ops;
741
742
15.5k
  tcos_ops.match_card           = tcos_match_card;
743
15.5k
  tcos_ops.init                 = tcos_init;
744
15.5k
  tcos_ops.finish               = tcos_finish;
745
15.5k
  tcos_ops.create_file          = tcos_create_file;
746
15.5k
  tcos_ops.set_security_env     = tcos_set_security_env;
747
15.5k
  tcos_ops.select_file          = tcos_select_file;
748
15.5k
  tcos_ops.list_files           = tcos_list_files;
749
15.5k
  tcos_ops.delete_file          = tcos_delete_file;
750
15.5k
  tcos_ops.compute_signature    = tcos_compute_signature;
751
15.5k
  tcos_ops.decipher             = tcos_decipher;
752
15.5k
  tcos_ops.restore_security_env = tcos_restore_security_env;
753
15.5k
  tcos_ops.card_ctl             = tcos_card_ctl;
754
755
15.5k
  return &tcos_drv;
756
15.5k
}