Coverage Report

Created: 2025-10-10 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensc/src/libopensc/card-skeid.c
Line
Count
Source
1
/*
2
 * card-skeid.c: Support for (CardOS based) cards issued as identity documents in Slovakia
3
 *
4
 * Copyright (C) 2022 Juraj Ĺ arinay <juraj@sarinay.com>
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
 * based on card-cardos.c
21
 */
22
23
#ifdef HAVE_CONFIG_H
24
#include "config.h"
25
#endif
26
27
#include <string.h>
28
#include "internal.h"
29
30
167
#define SKEID_KNOWN_URL_LEN 46
31
32
static const struct sc_card_operations *iso_ops = NULL;
33
34
static struct sc_card_operations skeid_ops;
35
static struct sc_card_driver skeid_drv = {
36
  "Slovak eID card",
37
  "skeid",
38
  &skeid_ops,
39
  NULL, 0, NULL
40
};
41
42
static const struct sc_atr_table skeid_atrs[] = {
43
  /* Slovak eID v3 - CardOS 5.4
44
   *
45
   * The ATR was intentionally omitted from minidriver_registration[] within win32/customactions.cpp
46
   * as it is identical to that of CardOS v5.4 and therefore already included.
47
   * Any new ATR may need an entry in minidriver_registration[]. */
48
  {"3b:d2:18:00:81:31:fe:58:c9:04:11", NULL, NULL, SC_CARD_TYPE_SKEID_V3, 0, NULL},
49
  {NULL, NULL, NULL, 0, 0, NULL}
50
};
51
52
static int skeid_known_url(sc_card_t * card)
53
90
{
54
90
  const struct sc_aid skeid_aid_eid = {{0xE8, 0x07, 0x04, 0x00, 0x7F, 0x00, 0x07, 0x03, 0x02}, 9};
55
90
  const char *known_url = "\x80\x01\x00\x5F\x50\x28http://www.minv.sk/cif/cif-sk-eid-v3.xml";
56
90
  u8 buf[SKEID_KNOWN_URL_LEN];
57
58
90
  sc_path_t url_path;
59
60
90
  int r = SC_ERROR_WRONG_CARD;
61
62
90
  sc_path_set(&url_path, SC_PATH_TYPE_DF_NAME, skeid_aid_eid.value, skeid_aid_eid.len, 0, 0);
63
64
90
  if (sc_select_file(card, &url_path, NULL) == SC_SUCCESS
65
31
    && sc_get_data(card, 0x7F62, buf, SKEID_KNOWN_URL_LEN) == SKEID_KNOWN_URL_LEN
66
15
    && !memcmp(buf, known_url, SKEID_KNOWN_URL_LEN))
67
8
    r = SC_SUCCESS;
68
69
90
  return r;
70
90
}
71
72
static int skeid_match_card(sc_card_t *card)
73
14.1k
{
74
14.1k
  if (_sc_match_atr(card, skeid_atrs, &card->type) < 0 || skeid_known_url(card) != SC_SUCCESS)
75
14.1k
    return 0;
76
77
8
  sc_log(card->ctx,  "Slovak eID card v3 (CardOS 5.4)");
78
79
8
  return 1;
80
14.1k
}
81
82
static int skeid_get_serialnr(sc_card_t *card)
83
8
{
84
8
  int r;
85
8
  sc_apdu_t apdu;
86
8
  u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
87
88
8
  sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x81);
89
8
  apdu.resp = rbuf;
90
8
  apdu.resplen = sizeof(rbuf);
91
8
  apdu.le = 256;
92
8
  r = sc_transmit_apdu(card, &apdu);
93
8
  LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
94
7
  if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
95
4
    return SC_ERROR_INTERNAL;
96
3
  if (apdu.resplen == 8) {
97
    /* cache serial number */
98
1
    memcpy(card->serialnr.value, rbuf, 8);
99
1
    card->serialnr.len = 8;
100
2
  } else {
101
2
    sc_log(card->ctx, "unexpected response to GET DATA serial number");
102
2
    return SC_ERROR_INTERNAL;
103
2
  }
104
1
  return SC_SUCCESS;
105
3
}
106
107
static int skeid_init(sc_card_t *card)
108
8
{
109
8
  const unsigned long flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE;
110
8
  const size_t data_field_length = 437;
111
8
  int r;
112
113
8
  SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
114
115
8
  card->name = "Slovak eID (CardOS)";
116
8
  card->type = SC_CARD_TYPE_SKEID_V3;
117
8
  card->cla = 0x00;
118
119
8
  r = skeid_get_serialnr(card);
120
8
  LOG_TEST_RET(card->ctx, r, "Error reading serial number.");
121
122
1
  card->caps |= SC_CARD_CAP_APDU_EXT | SC_CARD_CAP_ISO7816_PIN_INFO;
123
124
1
  card->max_send_size = data_field_length - 6;
125
#ifdef _WIN32
126
  /* see card-cardos.c */
127
  if (card->reader->max_send_size == 255 && card->reader->max_recv_size == 256) {
128
    sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "resetting reader to use data_field_length");
129
    card->reader->max_send_size = data_field_length - 6;
130
    card->reader->max_recv_size = data_field_length - 3;
131
  }
132
#endif
133
134
1
  card->max_send_size = sc_get_max_send_size(card); /* see card-cardos.c */
135
1
  card->max_recv_size = data_field_length - 2;
136
1
  card->max_recv_size = sc_get_max_recv_size(card);
137
138
1
  r = _sc_card_add_rsa_alg(card, 3072, flags, 0);
139
140
1
  LOG_FUNC_RETURN(card->ctx, r);
141
1
}
142
143
static int skeid_set_security_env(sc_card_t *card,
144
    const sc_security_env_t *env,
145
    int se_num)
146
0
{
147
0
  int key_id;
148
0
  int r;
149
150
0
  assert(card != NULL && env != NULL);
151
152
0
  if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || env->key_ref_len != 1) {
153
0
    sc_log(card->ctx, "No or invalid key reference");
154
0
    return SC_ERROR_INVALID_ARGUMENTS;
155
0
  }
156
157
  /* here we follow the behaviour of the proprietary driver accompanying the card
158
   * where security operations are preceded by MSE RESTORE rather than MSE SET
159
   */
160
0
  key_id = env->key_ref[0];
161
0
  r = sc_restore_security_env(card, key_id);
162
163
0
  return r;
164
0
}
165
166
static int skeid_logout(sc_card_t *card)
167
0
{
168
0
  int r;
169
0
  sc_path_t path;
170
171
0
  sc_format_path("3F00", &path);
172
0
  r = sc_select_file(card, &path, NULL);
173
0
  return r;
174
175
0
}
176
177
struct sc_card_driver * sc_get_skeid_driver(void)
178
14.6k
{
179
14.6k
  if (iso_ops == NULL) iso_ops = sc_get_iso7816_driver()->ops;
180
14.6k
  skeid_ops = *iso_ops;
181
14.6k
  skeid_ops.match_card = skeid_match_card;
182
14.6k
  skeid_ops.init = skeid_init;
183
14.6k
  skeid_ops.set_security_env = skeid_set_security_env;
184
14.6k
  skeid_ops.logout = skeid_logout;
185
14.6k
  return &skeid_drv;
186
14.6k
}