Coverage Report

Created: 2026-05-30 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensc/src/libopensc/card-rtecp.c
Line
Count
Source
1
/*
2
 * card-rtecp.c: Support for Rutoken ECP and Rutoken Lite cards
3
 *
4
 * Copyright (C) 2009  Aleksey Samsonov <samsonov@guardant.ru>
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
 */
20
21
#ifdef HAVE_CONFIG_H
22
#include "config.h"
23
#endif
24
25
#include <stddef.h>
26
#include <stdlib.h>
27
#include <string.h>
28
29
#include "internal.h"
30
#include "asn1.h"
31
#include "cardctl.h"
32
33
static const struct sc_card_operations *iso_ops = NULL;
34
static struct sc_card_operations rtecp_ops;
35
36
static struct sc_card_driver rtecp_drv = {
37
  "Rutoken ECP and Lite driver",
38
  "rutoken_ecp",
39
  &rtecp_ops,
40
  NULL, 0, NULL
41
};
42
43
static const struct sc_atr_table rtecp_atrs[] = {
44
  /* Rutoken ECP */
45
  { "3B:8B:01:52:75:74:6F:6B:65:6E:20:45:43:50:A0",
46
    NULL, "Rutoken ECP", SC_CARD_TYPE_RUTOKEN_ECP, 0, NULL },
47
  /* Rutoken ECP (DS) */
48
  { "3B:8B:01:52:75:74:6F:6B:65:6E:20:44:53:20:C1",
49
    NULL, "Rutoken ECP (DS)", SC_CARD_TYPE_RUTOKEN_ECP, 0, NULL },
50
  /* Rutoken ECP SC T0 */
51
  { "3B:9C:96:00:52:75:74:6F:6B:65:6E:45:43:50:73:63",
52
    "00:00:00:00:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF",
53
    "Rutoken ECP SC", SC_CARD_TYPE_RUTOKEN_ECP_SC, 0, NULL },
54
  /* Rutoken ECP SC T1 */
55
  { "3B:9C:94:80:11:40:52:75:74:6F:6B:65:6E:45:43:50:73:63:C3",
56
    "00:00:00:00:00:00:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:00",
57
    "Rutoken ECP SC", SC_CARD_TYPE_RUTOKEN_ECP_SC, 0, NULL },
58
  /* Rutoken ECP SC NFC */
59
  { "3B:88:80:01:52:74:53:43:77:81:83:20:6A",
60
    "00:00:00:00:FF:FF:FF:FF:00:00:00:00:00",
61
    "Rutoken ECP SC NFC", SC_CARD_TYPE_RUTOKEN_ECP_SC, 0, NULL },
62
  /* Rutoken Lite */
63
  { "3B:8B:01:52:75:74:6F:6B:65:6E:6C:69:74:65:C2",
64
    NULL, "Rutoken Lite", SC_CARD_TYPE_RUTOKEN_LITE, 0, NULL },
65
  /* Rutoken Lite SC*/
66
  { "3B:9E:96:00:52:75:74:6F:6B:65:6E:4C:69:74:65:53:43:32",
67
    "00:00:00:00:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF",
68
    "Rutoken Lite SC", SC_CARD_TYPE_RUTOKEN_LITE_SC, 0, NULL },
69
  { NULL, NULL, NULL, 0, 0, NULL }
70
};
71
72
static int rtecp_match_card(sc_card_t *card)
73
10.3k
{
74
10.3k
  int i = -1;
75
10.3k
  i = _sc_match_atr(card, rtecp_atrs, &card->type);
76
10.3k
  if (i >= 0) {
77
154
    card->name = rtecp_atrs[i].name;
78
154
    LOG_FUNC_RETURN(card->ctx, 1);
79
154
  }
80
10.2k
  LOG_FUNC_RETURN(card->ctx, 0);
81
10.2k
}
82
83
static int rtecp_init(sc_card_t *card)
84
154
{
85
154
  sc_algorithm_info_t info;
86
154
  unsigned long flags;
87
88
154
  if (!card || !card->ctx)
89
0
    return SC_ERROR_INVALID_ARGUMENTS;
90
91
154
  card->cla = 0;
92
93
154
  if (card->type == SC_CARD_TYPE_RUTOKEN_LITE
94
154
      || card->type == SC_CARD_TYPE_RUTOKEN_LITE_SC)
95
4
    SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS);
96
97
150
  card->caps |= SC_CARD_CAP_RNG;
98
99
150
  flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_ONBOARD_KEY_GEN
100
150
    | SC_ALGORITHM_RSA_PAD_NONE | SC_ALGORITHM_RSA_HASH_NONE;
101
102
150
  _sc_card_add_rsa_alg(card, 256, flags, 0);
103
150
  _sc_card_add_rsa_alg(card, 512, flags, 0);
104
150
  _sc_card_add_rsa_alg(card, 768, flags, 0);
105
150
  _sc_card_add_rsa_alg(card, 1024, flags, 0);
106
150
  _sc_card_add_rsa_alg(card, 1280, flags, 0);
107
150
  _sc_card_add_rsa_alg(card, 1536, flags, 0);
108
150
  _sc_card_add_rsa_alg(card, 1792, flags, 0);
109
150
  _sc_card_add_rsa_alg(card, 2048, flags, 0);
110
150
  _sc_card_add_rsa_alg(card, 4096, flags, 0);
111
112
150
  memset(&info, 0, sizeof(info));
113
150
  info.algorithm = SC_ALGORITHM_GOSTR3410;
114
150
  info.key_length = 256;
115
150
  info.flags = SC_ALGORITHM_GOSTR3410_RAW | SC_ALGORITHM_ONBOARD_KEY_GEN
116
150
    | SC_ALGORITHM_GOSTR3410_HASH_NONE;
117
150
  _sc_card_add_algorithm(card, &info);
118
119
150
  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS);
120
150
}
121
122
static void reverse(unsigned char *buf, size_t len)
123
0
{
124
0
  unsigned char tmp;
125
0
  size_t i;
126
127
0
  if (!buf && len != 0)
128
0
    return;
129
130
0
  for (i = 0; i < len / 2; ++i)
131
0
  {
132
0
    tmp = buf[i];
133
0
    buf[i] = buf[len - 1 - i];
134
0
    buf[len - 1 - i] = tmp;
135
0
  }
136
0
}
137
138
static unsigned int sec_attr_to_method(unsigned int attr)
139
81
{
140
81
  if (attr == 0xFF)
141
12
    return SC_AC_NEVER;
142
69
  else if (attr == 0)
143
9
    return SC_AC_NONE;
144
60
  else if (attr & 0x03)
145
42
    return SC_AC_CHV;
146
18
  else
147
18
    return SC_AC_UNKNOWN;
148
81
}
149
150
static unsigned long sec_attr_to_key_ref(unsigned int attr)
151
81
{
152
81
  if (attr == 1 || attr == 2)
153
16
    return attr;
154
65
  return 0;
155
81
}
156
157
static unsigned int to_sec_attr(unsigned int method, unsigned int key_ref)
158
202
{
159
202
  if (method == SC_AC_NEVER || method == SC_AC_NONE)
160
111
    return method;
161
91
  if (method == SC_AC_CHV  &&  (key_ref == 1 || key_ref == 2))
162
0
    return key_ref;
163
91
  return 0;
164
91
}
165
166
static void set_acl_from_sec_attr(sc_card_t *card, sc_file_t *file)
167
42
{
168
42
  unsigned int method;
169
42
  unsigned long key_ref;
170
171
42
  if (!card || !card->ctx || !file || !file->sec_attr
172
42
    || file->sec_attr_len != SC_RTECP_SEC_ATTR_SIZE
173
0
    || 1 + 6 >= SC_RTECP_SEC_ATTR_SIZE)
174
0
  {
175
0
    return;
176
0
  }
177
178
42
  sc_file_add_acl_entry(file, SC_AC_OP_SELECT, SC_AC_NONE, SC_AC_KEY_REF_NONE);
179
42
  if (file->sec_attr[0] & 0x40) /* if AccessMode.6 */
180
26
  {
181
26
    method = sec_attr_to_method(file->sec_attr[1 + 6]);
182
26
    key_ref = sec_attr_to_key_ref(file->sec_attr[1 + 6]);
183
26
    sc_log(card->ctx,
184
26
      "SC_AC_OP_DELETE %i %lu\n",
185
26
      (int)method, key_ref);
186
26
    sc_file_add_acl_entry(file, SC_AC_OP_DELETE, method, key_ref);
187
26
  }
188
42
  if (file->sec_attr[0] & 0x01) /* if AccessMode.0 */
189
29
  {
190
29
    method = sec_attr_to_method(file->sec_attr[1 + 0]);
191
29
    key_ref = sec_attr_to_key_ref(file->sec_attr[1 + 0]);
192
29
    sc_log(card->ctx,
193
29
      (file->type == SC_FILE_TYPE_DF) ?
194
29
        "SC_AC_OP_CREATE %i %lu\n"
195
29
        : "SC_AC_OP_READ %i %lu\n",
196
29
      (int)method, key_ref);
197
29
    sc_file_add_acl_entry(file, (file->type == SC_FILE_TYPE_DF) ?
198
28
        SC_AC_OP_CREATE : SC_AC_OP_READ, method, key_ref);
199
29
  }
200
42
  if (file->type == SC_FILE_TYPE_DF)
201
1
  {
202
1
    sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES,
203
1
        SC_AC_NONE, SC_AC_KEY_REF_NONE);
204
1
  }
205
41
  else
206
41
    if (file->sec_attr[0] & 0x02) /* if AccessMode.1 */
207
26
    {
208
26
      method = sec_attr_to_method(file->sec_attr[1 + 1]);
209
26
      key_ref = sec_attr_to_key_ref(file->sec_attr[1 + 1]);
210
26
      sc_log(card->ctx,
211
26
        "SC_AC_OP_UPDATE %i %lu\n",
212
26
        (int)method, key_ref);
213
26
      sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, method, key_ref);
214
26
      sc_log(card->ctx,
215
26
        "SC_AC_OP_WRITE %i %lu\n",
216
26
        (int)method, key_ref);
217
26
      sc_file_add_acl_entry(file, SC_AC_OP_WRITE, method, key_ref);
218
26
    }
219
42
}
220
221
static int set_sec_attr_from_acl(sc_card_t *card, sc_file_t *file)
222
58
{
223
58
  const sc_acl_entry_t *entry;
224
58
  u8 sec_attr[SC_RTECP_SEC_ATTR_SIZE] = { 0 };
225
58
  int r;
226
227
58
  if (!card || !card->ctx || !file
228
58
    || file->sec_attr || file->sec_attr_len != 0)
229
0
    return SC_ERROR_INVALID_ARGUMENTS;
230
231
58
  entry = sc_file_get_acl_entry(file, SC_AC_OP_DELETE);
232
58
  if (entry)
233
58
  {
234
58
    sec_attr[0] |= 0x40;
235
58
    sec_attr[1 + 6] = to_sec_attr(entry->method, entry->key_ref);
236
58
  }
237
58
  if (file->type == SC_FILE_TYPE_DF)
238
30
  {
239
30
    entry = sc_file_get_acl_entry(file, SC_AC_OP_CREATE);
240
30
    if (entry)
241
30
    {
242
      /* ATTR: Create DF/EF file */
243
30
      sec_attr[0] |= 0x01;
244
30
      sec_attr[1 + 0] = to_sec_attr(entry->method, entry->key_ref);
245
      /* ATTR: Create Internal EF (RSF) file */
246
30
      sec_attr[0] |= 0x02;
247
30
      sec_attr[1 + 1] = to_sec_attr(entry->method, entry->key_ref);
248
30
    }
249
30
  }
250
28
  else
251
28
  {
252
28
    entry = sc_file_get_acl_entry(file, SC_AC_OP_READ);
253
28
    if (entry)
254
28
    {
255
28
      sec_attr[0] |= 0x01;
256
28
      sec_attr[1 + 0] = to_sec_attr(entry->method, entry->key_ref);
257
28
    }
258
28
    entry = sc_file_get_acl_entry(file, SC_AC_OP_WRITE);
259
28
    if (entry)
260
28
    {
261
28
      sec_attr[0] |= 0x02;
262
28
      sec_attr[1 + 1] = to_sec_attr(entry->method, entry->key_ref);
263
28
    }
264
28
    entry = sc_file_get_acl_entry(file, SC_AC_OP_UPDATE);
265
28
    if (entry)
266
28
    {
267
      /* rewrite if sec_attr[1 + 1] already set */
268
28
      sec_attr[0] |= 0x02;
269
28
      sec_attr[1 + 1] = to_sec_attr(entry->method, entry->key_ref);
270
28
    }
271
28
  }
272
  /* FIXME: Find the best solution */
273
58
  if (file->path.len == 2 && !memcmp(file->path.value, "\x3F\x00", 2))
274
1
  {
275
    /* ATTR: Put data */
276
1
    sec_attr[0] |= 0x04;
277
1
    sec_attr[1 + 2] = 1; /* so-pin reference */
278
1
  }
279
58
  r = sc_file_set_sec_attr(file, sec_attr, sizeof(sec_attr));
280
58
  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
281
58
}
282
283
static int rtecp_select_file(sc_card_t *card,
284
    const sc_path_t *in_path, sc_file_t **file_out)
285
695
{
286
695
  sc_file_t *file = NULL;
287
695
  int r = SC_SUCCESS;
288
289
695
  if (!card || !card->ctx || !in_path)
290
0
    return SC_ERROR_INVALID_ARGUMENTS;
291
292
695
  SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
293
294
695
  switch (in_path->type)
295
695
  {
296
201
  case SC_PATH_TYPE_DF_NAME:
297
201
  case SC_PATH_TYPE_FROM_CURRENT:
298
201
  case SC_PATH_TYPE_PARENT:
299
201
    SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED);
300
695
  }
301
302
  // Card Rutoken ECP SC T0 doesn't support SELECT FILE without return a file info.
303
  // So here we request a file and then assign/free it depending on file_out.
304
494
  r = iso_ops->select_file(card, in_path, &file);
305
494
  if (r != SC_SUCCESS)
306
426
    SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
307
308
68
  if (file->sec_attr && file->sec_attr_len == SC_RTECP_SEC_ATTR_SIZE)
309
42
    set_acl_from_sec_attr(card, file);
310
26
  else
311
26
  {
312
26
    sc_file_free(file);
313
26
    SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED);
314
26
  }
315
316
42
  if (file_out)
317
40
    *file_out = file;
318
2
  else
319
2
    sc_file_free(file);
320
321
42
  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
322
42
}
323
324
static int rtecp_verify(sc_card_t *card, unsigned int type, int ref_qualifier,
325
    const u8 *data, size_t data_len, int *tries_left)
326
0
{
327
0
  sc_apdu_t apdu;
328
0
  int r, send_logout = 0;
329
330
0
  (void)type; /* no warning */
331
332
0
  if (!card || !card->ctx || !data)
333
0
    return SC_ERROR_INVALID_ARGUMENTS;
334
335
0
  for (;;)
336
0
  {
337
0
    sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT,
338
0
        0x20, 0, ref_qualifier);
339
0
    apdu.lc = data_len;
340
0
    apdu.data = data;
341
0
    apdu.datalen = data_len;
342
0
    r = sc_transmit_apdu(card, &apdu);
343
0
    LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
344
0
    if (send_logout++ == 0 && apdu.sw1 == 0x6F && apdu.sw2 == 0x86)
345
0
    {
346
0
       r = sc_logout(card);
347
0
       LOG_TEST_RET(card->ctx, r, "Logout failed");
348
0
    }
349
0
    else
350
0
      break;
351
0
  }
352
0
  if (apdu.sw1 == 0x63 && apdu.sw2 == 0)
353
0
  {
354
    /* Verification failed */
355
0
    sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0, ref_qualifier);
356
0
    r = sc_transmit_apdu(card, &apdu);
357
0
    LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
358
0
  }
359
0
  r = sc_check_sw(card, apdu.sw1, apdu.sw2);
360
0
  if (r == SC_ERROR_PIN_CODE_INCORRECT && tries_left)
361
0
    *tries_left = (int)(apdu.sw2 & 0x0F);
362
0
  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
363
0
}
364
365
static int rtecp_logout(sc_card_t *card)
366
0
{
367
0
  sc_apdu_t apdu;
368
0
  int r;
369
370
0
  if (!card || !card->ctx)
371
0
    return SC_ERROR_INVALID_ARGUMENTS;
372
373
0
  sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x40, 0, 0);
374
0
  apdu.cla = 0x80;
375
0
  r = sc_transmit_apdu(card, &apdu);
376
0
  LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
377
0
  r = sc_check_sw(card, apdu.sw1, apdu.sw2);
378
0
  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
379
0
}
380
381
static int rtecp_set_security_env(  struct sc_card *card,
382
                  const struct sc_security_env *env,
383
                  int se_num)
384
0
{
385
0
  struct sc_security_env se_env;
386
0
  if(!env)
387
0
    return SC_ERROR_INVALID_ARGUMENTS;
388
389
0
  se_env= *env;
390
0
  se_env.flags &= ~SC_SEC_ENV_FILE_REF_PRESENT;
391
0
  return iso_ops->set_security_env(card, &se_env, se_num);
392
0
}
393
394
static int rtecp_cipher(sc_card_t *card, const u8 *data, size_t data_len,
395
    u8 *out, size_t out_len, int sign)
396
0
{
397
0
  sc_apdu_t apdu;
398
0
  u8 *buf, *buf_out;
399
0
  size_t i;
400
0
  int r;
401
402
0
  if (!card || !card->ctx || !data || !out)
403
0
    return SC_ERROR_INVALID_ARGUMENTS;
404
405
0
  buf_out = malloc(out_len + 2);
406
0
  buf = malloc(data_len);
407
0
  if (!buf || !buf_out)
408
0
  {
409
0
    free(buf);
410
0
    free(buf_out);
411
0
    LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
412
0
  }
413
414
0
  for (i = 0; i < data_len; ++i)
415
0
    buf[i] = data[data_len - 1 - i];
416
417
0
  if (sign)
418
0
    sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A);
419
0
  else
420
0
    sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86);
421
0
  apdu.lc = data_len;
422
0
  apdu.data = buf;
423
0
  apdu.datalen = data_len;
424
0
  apdu.resp = buf_out;
425
0
  apdu.resplen = out_len + 2;
426
0
  apdu.le = out_len > 256 ? 256 : out_len;
427
0
  if (apdu.lc > 255)
428
0
    apdu.flags |= SC_APDU_FLAGS_CHAINING;
429
0
  r = sc_transmit_apdu(card, &apdu);
430
0
  if (!sign)
431
0
    sc_mem_clear(buf, data_len);
432
433
0
  free(buf);
434
0
  if (r)
435
0
    sc_log(card->ctx,  "APDU transmit failed: %s\n", sc_strerror(r));
436
0
  else
437
0
  {
438
0
    if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00)
439
0
    {
440
0
      if (apdu.resplen > out_len) {
441
0
        free(buf_out);
442
0
        SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,
443
0
            SC_ERROR_BUFFER_TOO_SMALL);
444
0
      }
445
0
      for (i = 0; i < apdu.resplen; ++i)
446
0
        out[i] = buf_out[apdu.resplen - 1 - i];
447
0
      r = (i > 0) ? (int)i : SC_ERROR_INTERNAL;
448
0
    }
449
0
    else
450
0
      r = sc_check_sw(card, apdu.sw1, apdu.sw2);
451
0
  }
452
0
  if (!sign)
453
0
    sc_mem_clear(buf_out, out_len + 2);
454
455
0
  free(buf_out);
456
0
  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
457
458
0
}
459
460
static int rtecp_decipher(sc_card_t *card,
461
    const u8 *data, size_t data_len, u8 *out, size_t out_len)
462
0
{
463
0
  int r;
464
465
0
  if (!card || !card->ctx || !data || !out)
466
0
    return SC_ERROR_INVALID_ARGUMENTS;
467
468
0
  if (card->type == SC_CARD_TYPE_RUTOKEN_LITE
469
0
      || card->type == SC_CARD_TYPE_RUTOKEN_LITE_SC)
470
0
    SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED);
471
472
  /* decipher */
473
0
  r = rtecp_cipher(card, data, data_len, out, out_len, 0);
474
0
  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
475
0
}
476
477
static int rtecp_compute_signature(sc_card_t *card,
478
    const u8 *data, size_t data_len, u8 *out, size_t out_len)
479
0
{
480
0
  int r;
481
482
0
  if (!card || !card->ctx || !data || !out)
483
0
    return SC_ERROR_INVALID_ARGUMENTS;
484
485
0
  if (card->type == SC_CARD_TYPE_RUTOKEN_LITE
486
0
      || card->type == SC_CARD_TYPE_RUTOKEN_LITE_SC)
487
0
    SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED);
488
489
  /* compute digital signature */
490
0
  r = rtecp_cipher(card, data, data_len, out, out_len, 1);
491
0
  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
492
0
}
493
494
static int rtecp_change_reference_data(sc_card_t *card, unsigned int type,
495
    int ref_qualifier, const u8 *old, size_t oldlen,
496
    const u8 *newref, size_t newlen, int *tries_left)
497
0
{
498
0
  sc_apdu_t apdu;
499
0
  u8 rsf_length[2], *buf, *buf_end, *p;
500
0
  size_t val_length, buf_length, max_transmit_length, transmits_num;
501
0
  int r;
502
503
0
  if (!card || !card->ctx || !newref)
504
0
    return SC_ERROR_INVALID_ARGUMENTS;
505
506
0
  sc_log(card->ctx,
507
0
     "newlen = %"SC_FORMAT_LEN_SIZE_T"u\n", newlen);
508
0
  if (newlen > 0xFFFF)
509
0
    LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
510
0
  if (type == SC_AC_CHV && old && oldlen != 0)
511
0
  {
512
0
    r = sc_verify(card, type, ref_qualifier, old, oldlen, tries_left);
513
0
    LOG_TEST_RET(card->ctx, r, "Verify old pin failed");
514
0
  }
515
516
0
  max_transmit_length = sc_get_max_send_size(card);
517
0
  if (max_transmit_length <= 2)
518
0
    LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
519
  /*
520
   * (2 + sizeof(rsf_length) + newlen) - total length of data we need to transfer,
521
   * (max_transmit_length - 2) - amount of useful data we can transfer in one transmit (2 bytes for 0xA5 tag)
522
   */
523
0
  transmits_num = (2 + sizeof(rsf_length) + newlen) / (max_transmit_length - 2) + 1;
524
  /* buffer length = size of 0x80 TLV + size of RSF-file + (size of Tag and Length)*(number of APDUs) */
525
0
  buf_length = (2 + sizeof(rsf_length)) + newlen + 2*(transmits_num);
526
0
  p = buf = (u8 *)malloc(buf_length);
527
0
  if (buf == NULL)
528
0
    LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
529
0
  buf_end = buf + buf_length;
530
531
0
  sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, ref_qualifier);
532
  /* put 0x80 TLV */
533
0
  rsf_length[0] = (newlen >> 8) & 0xFF;
534
0
  rsf_length[1] = newlen & 0xFF;
535
536
0
  if (buf_end - p < (int)(2 + sizeof(rsf_length))) {
537
0
    free(buf);
538
0
    LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
539
0
  }
540
541
0
  sc_asn1_put_tag(0x80, rsf_length, sizeof(rsf_length), p, buf_end - p, &p);
542
  /* put 0xA5 TLVs (one or more); each transmit must begin with 0xA5 TLV */
543
0
  while (newlen)
544
0
  {
545
0
    if (buf_end - p < (int)(newlen + 2)) {
546
0
      free(buf);
547
0
      LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
548
0
    }
549
0
    if ((p - buf) % max_transmit_length + newlen + 2 > max_transmit_length)
550
0
      val_length = max_transmit_length - (p - buf) % max_transmit_length - 2;
551
0
    else
552
0
      val_length = newlen;
553
    /* not using sc_asn1_put_tag(...) because rtecp do not support asn1 properly (when val_length > 127) */
554
0
    *p++ = 0xA5;
555
0
    *p++ = (u8)val_length;
556
0
    if (val_length > newlen) {
557
0
      free(buf);
558
0
      LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
559
0
    }
560
0
    memcpy(p, newref, val_length);
561
0
    p += val_length;
562
0
    newref += val_length;
563
0
    newlen -= val_length;
564
0
    if (newlen)
565
0
      apdu.flags |= SC_APDU_FLAGS_CHAINING;
566
0
  }
567
0
  apdu.lc = p - buf;
568
0
  apdu.data = buf;
569
0
  apdu.datalen = p - buf;
570
571
0
  r = sc_transmit_apdu(card, &apdu);
572
0
  sc_mem_clear(buf, buf_length);
573
0
  free(buf);
574
0
  LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
575
0
  r = sc_check_sw(card, apdu.sw1, apdu.sw2);
576
0
  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
577
0
}
578
579
static int rtecp_reset_retry_counter(sc_card_t *card, unsigned int type,
580
    int ref_qualifier, const u8 *puk, size_t puklen,
581
    const u8 *newref, size_t newlen)
582
0
{
583
0
  sc_apdu_t apdu;
584
0
  int r;
585
586
0
  (void)type, (void)puk, (void)puklen; /* no warning */
587
588
0
  if (!card || !card->ctx)
589
0
    return SC_ERROR_INVALID_ARGUMENTS;
590
591
0
  sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2C, 0x03, ref_qualifier);
592
0
  r = sc_transmit_apdu(card, &apdu);
593
0
  LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
594
0
  r = sc_check_sw(card, apdu.sw1, apdu.sw2);
595
0
  LOG_TEST_RET(card->ctx, r, "Unblock card failed");
596
597
0
  if (newref && newlen)   {
598
0
          u8 tmp[2], buf[SC_MAX_APDU_BUFFER_SIZE];
599
0
    u8 *p = buf;
600
601
0
    tmp[0] = (newlen >> 8) & 0xFF;
602
0
    tmp[1] = newlen & 0xFF;
603
0
    sc_asn1_put_tag(0x80, tmp, sizeof(tmp), p, sizeof(buf) - (p - buf), &p);
604
0
    r = sc_asn1_put_tag(0xA5, newref, newlen, p, sizeof(buf) - (p - buf), &p);
605
0
    LOG_TEST_RET(card->ctx, r, "Invalid new PIN length");
606
607
0
    sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, ref_qualifier);
608
0
    apdu.lc = p - buf;
609
0
    apdu.data = buf;
610
0
    apdu.datalen = p - buf;
611
612
0
    r = sc_transmit_apdu(card, &apdu);
613
0
    LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
614
0
    r = sc_check_sw(card, apdu.sw1, apdu.sw2);
615
0
    LOG_TEST_RET(card->ctx, r, "Set PIN failed");
616
0
  }
617
618
0
  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
619
0
}
620
621
static int rtecp_create_file(sc_card_t *card, sc_file_t *file)
622
58
{
623
58
  int r;
624
625
58
  if (!card || !card->ctx || !file)
626
0
    return SC_ERROR_INVALID_ARGUMENTS;
627
628
58
  if (file->sec_attr_len == 0)
629
58
  {
630
58
    r = set_sec_attr_from_acl(card, file);
631
58
    LOG_TEST_RET(card->ctx, r, "Set sec_attr from ACL failed");
632
58
  }
633
634
58
  r = iso_ops->create_file(card, file);
635
58
  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
636
58
}
637
638
static int rtecp_list_files(sc_card_t *card, u8 *buf, size_t buflen)
639
0
{
640
0
  sc_apdu_t apdu;
641
0
  u8 rbuf[SC_MAX_APDU_RESP_SIZE], previd[2];
642
0
  const u8 *tag;
643
0
  size_t taglen, len = 0;
644
0
  int r;
645
646
0
  if (!card || !card->ctx || !buf)
647
0
    return SC_ERROR_INVALID_ARGUMENTS;
648
649
0
  sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0, 0);
650
0
  for (;;)
651
0
  {
652
0
    apdu.resp = rbuf;
653
0
    apdu.resplen = sizeof(rbuf);
654
0
    apdu.le = sizeof(rbuf);
655
0
    r = sc_transmit_apdu(card, &apdu);
656
0
    LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
657
0
    if (apdu.sw1 == 0x6A  &&  apdu.sw2 == 0x82)
658
0
      break; /* Next file not found */
659
660
0
    r = sc_check_sw(card, apdu.sw1, apdu.sw2);
661
0
    LOG_TEST_RET(card->ctx, r, "");
662
663
0
    if (apdu.resplen <= 2)
664
0
      LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_LENGTH);
665
666
    /* save first file(dir) ID */
667
0
    tag = sc_asn1_find_tag(card->ctx, apdu.resp + 2, apdu.resplen - 2,
668
0
        0x83, &taglen);
669
0
    if (!tag || taglen != sizeof(previd))
670
0
      LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);
671
0
    memcpy(previd, tag, sizeof(previd));
672
673
0
    if (len + sizeof(previd) <= buflen)
674
0
    {
675
0
      memcpy(&buf[len], previd, sizeof(previd));
676
0
      len += sizeof(previd);
677
0
    }
678
679
0
    tag = sc_asn1_find_tag(card->ctx, apdu.resp + 2, apdu.resplen - 2,
680
0
        0x82, &taglen);
681
0
    if (!tag || taglen != 2)
682
0
      LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);
683
0
    if (tag[0] == 0x38)
684
0
    {
685
      /* Select parent DF of the current DF */
686
0
      sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0x03, 0);
687
      /* We should set le and resp buf to actually call Get Response for card on T0. */
688
0
      apdu.resp = rbuf;
689
0
      apdu.resplen = sizeof(rbuf);
690
0
      apdu.le = sizeof(rbuf);
691
0
      r = sc_transmit_apdu(card, &apdu);
692
0
      LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
693
0
      r = sc_check_sw(card, apdu.sw1, apdu.sw2);
694
0
      LOG_TEST_RET(card->ctx, r, "");
695
0
    }
696
0
    sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0x02);
697
0
    apdu.lc = sizeof(previd);
698
0
    apdu.data = previd;
699
0
    apdu.datalen = sizeof(previd);
700
0
  }
701
0
  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)len);
702
0
}
703
704
static int rtecp_card_ctl(sc_card_t *card, unsigned long request, void *data)
705
184
{
706
184
  sc_apdu_t apdu;
707
184
  u8 buf[512];
708
184
  sc_rtecp_genkey_data_t *genkey_data = data;
709
184
  sc_serial_number_t *serial = data;
710
184
  int r;
711
712
184
  const unsigned char rsa_prop[] = {0xA6, 0x06, 0x94, 0x04, 0x01, 0x00, 0x01, 0x00};
713
714
184
  if (!card || !card->ctx)
715
0
    return SC_ERROR_INVALID_ARGUMENTS;
716
717
184
  switch (request)
718
184
  {
719
15
  case SC_CARDCTL_RTECP_INIT:
720
15
    sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x8A, 0, 0);
721
15
    apdu.cla = 0x80;
722
15
    break;
723
15
  case SC_CARDCTL_RTECP_INIT_END:
724
15
    sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x84, 0x4E, 0x19);
725
15
    apdu.cla = 0x80;
726
15
    break;
727
0
  case SC_CARDCTL_GET_SERIALNR:
728
0
    if (!serial)
729
0
      LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
730
0
    sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, 0x81);
731
0
    apdu.resp = buf;
732
0
    apdu.resplen = sizeof(buf);
733
0
    apdu.le = 256;
734
0
    serial->len = sizeof(serial->value);
735
0
    break;
736
0
  case SC_CARDCTL_RTECP_GENERATE_KEY:
737
0
    if (!genkey_data)
738
0
      LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
739
0
    sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x46, 0x80,
740
0
        genkey_data->key_id);
741
0
    apdu.resp = buf;
742
0
    apdu.resplen = sizeof(buf);
743
0
    apdu.le = 256;
744
0
    if (genkey_data->type == SC_ALGORITHM_RSA) {
745
0
      apdu.cse = SC_APDU_CASE_4_SHORT;
746
0
      apdu.data = rsa_prop;
747
0
      apdu.datalen = sizeof(rsa_prop);
748
0
      apdu.lc = sizeof(rsa_prop);
749
0
    }
750
0
    break;
751
154
  case SC_CARDCTL_LIFECYCLE_SET:
752
154
    sc_log(card->ctx,  "%s\n",
753
154
        "SC_CARDCTL_LIFECYCLE_SET not supported");
754
    /* no call sc_debug (SC_FUNC_RETURN) */
755
154
    return SC_ERROR_NOT_SUPPORTED;
756
0
  default:
757
0
    sc_log(card->ctx,
758
0
      "request = 0x%lx\n", request);
759
0
    LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
760
184
  }
761
30
  r = sc_transmit_apdu(card, &apdu);
762
30
  LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
763
27
  r = sc_check_sw(card, apdu.sw1, apdu.sw2);
764
27
  if (!r && request == SC_CARDCTL_RTECP_GENERATE_KEY)
765
0
  {
766
0
    if (genkey_data->type == SC_ALGORITHM_RSA &&
767
0
        genkey_data->u.rsa.modulus_len >= apdu.resplen &&
768
0
        genkey_data->u.rsa.exponent_len >= 3)
769
0
    {
770
0
      memcpy(genkey_data->u.rsa.modulus, apdu.resp, apdu.resplen);
771
0
      genkey_data->u.rsa.modulus_len = apdu.resplen;
772
0
      reverse(genkey_data->u.rsa.modulus,
773
0
          genkey_data->u.rsa.modulus_len);
774
0
      memcpy(genkey_data->u.rsa.exponent, "\x01\x00\x01", 3);
775
0
      genkey_data->u.rsa.exponent_len = 3;
776
0
    }
777
0
    else if (genkey_data->type == SC_ALGORITHM_GOSTR3410 &&
778
0
        genkey_data->u.gostr3410.xy_len >= apdu.resplen)
779
0
    {
780
0
      memcpy(genkey_data->u.gostr3410.xy, apdu.resp, apdu.resplen);
781
0
      genkey_data->u.gostr3410.xy_len = apdu.resplen;
782
0
    }
783
0
    else
784
0
      r = SC_ERROR_BUFFER_TOO_SMALL;
785
0
  }
786
27
  else if (!r && request == SC_CARDCTL_GET_SERIALNR)
787
0
  {
788
0
    if (apdu.resplen <= sizeof(serial->value))
789
0
    {
790
0
      memcpy(serial->value, apdu.resp, apdu.resplen);
791
0
      serial->len = apdu.resplen;
792
0
    }
793
0
    else
794
0
      r = SC_ERROR_BUFFER_TOO_SMALL;
795
0
  }
796
27
  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
797
27
}
798
799
static int rtecp_construct_fci(sc_card_t *card, const sc_file_t *file,
800
    u8 *out, size_t *outlen)
801
58
{
802
58
  u8 buf[64], *p = out;
803
804
58
  if (!card || !card->ctx || !file || !out || !outlen
805
58
    || (*outlen < (size_t)(p - out) + 2))
806
0
    return SC_ERROR_INVALID_ARGUMENTS;
807
808
58
  *p++ = 0x6F; /* FCI template */
809
58
  p++; /* for length */
810
811
  /* 0x80 - Number of data bytes in the file, excluding structural information */
812
58
  buf[0] = (file->size >> 8) & 0xFF;
813
58
  buf[1] = file->size & 0xFF;
814
58
  sc_asn1_put_tag(0x80, buf, 2, p, *outlen - (p - out), &p);
815
816
  /* 0x82 - File descriptor byte */
817
58
  if (file->type_attr_len)
818
0
  {
819
0
    if (sizeof(buf) < file->type_attr_len)
820
0
      LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
821
0
    memcpy(buf, file->type_attr, file->type_attr_len);
822
0
    sc_asn1_put_tag(0x82, buf, file->type_attr_len,
823
0
        p, *outlen - (p - out), &p);
824
0
  }
825
58
  else
826
58
  {
827
58
    switch (file->type)
828
58
    {
829
22
    case SC_FILE_TYPE_WORKING_EF:
830
22
      buf[0] = 0x01;
831
22
      break;
832
30
    case SC_FILE_TYPE_DF:
833
30
      buf[0] = 0x38;
834
30
      break;
835
1
    case SC_FILE_TYPE_INTERNAL_EF:
836
6
    default:
837
6
      LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
838
58
    }
839
52
    buf[1] = 0;
840
52
    sc_asn1_put_tag(0x82, buf, 2, p, *outlen - (p - out), &p);
841
52
  }
842
  /* 0x83 - File identifier */
843
52
  buf[0] = (file->id >> 8) & 0xFF;
844
52
  buf[1] = file->id & 0xFF;
845
52
  sc_asn1_put_tag(0x83, buf, 2, p, *outlen - (p - out), &p);
846
847
52
  if (file->prop_attr_len)
848
2
  {
849
2
    if (sizeof(buf) < file->prop_attr_len)
850
2
      LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
851
0
    memcpy(buf, file->prop_attr, file->prop_attr_len);
852
0
    sc_asn1_put_tag(0x85, buf, file->prop_attr_len,
853
0
        p, *outlen - (p - out), &p);
854
0
  }
855
50
  if (file->sec_attr_len)
856
50
  {
857
50
    if (sizeof(buf) < file->sec_attr_len)
858
50
      LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
859
50
    memcpy(buf, file->sec_attr, file->sec_attr_len);
860
50
    sc_asn1_put_tag(0x86, buf, file->sec_attr_len,
861
50
        p, *outlen - (p - out), &p);
862
50
  }
863
50
  out[1] = p - out - 2; /* length */
864
50
  *outlen = p - out;
865
50
  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 0);
866
50
}
867
868
struct sc_card_driver * sc_get_rtecp_driver(void)
869
15.8k
{
870
15.8k
  if (iso_ops == NULL)
871
1
    iso_ops = sc_get_iso7816_driver()->ops;
872
15.8k
  rtecp_ops = *iso_ops;
873
874
15.8k
  rtecp_ops.match_card = rtecp_match_card;
875
15.8k
  rtecp_ops.init = rtecp_init;
876
  /* read_binary */
877
15.8k
  rtecp_ops.write_binary = NULL;
878
  /* update_binary */
879
15.8k
  rtecp_ops.read_record = NULL;
880
15.8k
  rtecp_ops.write_record = NULL;
881
15.8k
  rtecp_ops.append_record = NULL;
882
15.8k
  rtecp_ops.update_record = NULL;
883
15.8k
  rtecp_ops.select_file = rtecp_select_file;
884
  /* get_response */
885
  /* get_challenge */
886
15.8k
  rtecp_ops.verify = rtecp_verify;
887
15.8k
  rtecp_ops.logout = rtecp_logout;
888
  /* restore_security_env */
889
15.8k
  rtecp_ops.set_security_env = rtecp_set_security_env;
890
15.8k
  rtecp_ops.decipher = rtecp_decipher;
891
15.8k
  rtecp_ops.compute_signature = rtecp_compute_signature;
892
15.8k
  rtecp_ops.change_reference_data = rtecp_change_reference_data;
893
15.8k
  rtecp_ops.reset_retry_counter = rtecp_reset_retry_counter;
894
15.8k
  rtecp_ops.create_file = rtecp_create_file;
895
  /* delete_file */
896
15.8k
  rtecp_ops.list_files = rtecp_list_files;
897
  /* check_sw */
898
15.8k
  rtecp_ops.card_ctl = rtecp_card_ctl;
899
  /* process_fci */
900
15.8k
  rtecp_ops.construct_fci = rtecp_construct_fci;
901
  rtecp_ops.pin_cmd = NULL;
902
15.8k
  return &rtecp_drv;
903
15.8k
}