Coverage Report

Created: 2025-11-16 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensc/src/libopensc/card-edo.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2020 Piotr Majkrzak
3
 *
4
 * This file is part of OpenSC.
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
#ifdef HAVE_CONFIG_H
21
#include "config.h"
22
#endif
23
24
#if defined(ENABLE_SM) && defined(ENABLE_OPENPACE)
25
26
#include "libopensc/internal.h"
27
#include "libopensc/opensc.h"
28
#include "libopensc/pace.h"
29
#include "libopensc/sm.h"
30
#include "libopensc/asn1.h"
31
#include "sm/sm-eac.h"
32
#include <string.h>
33
#include <stdlib.h>
34
35
36
static struct sc_card_operations edo_ops;
37
38
39
static struct sc_card_driver edo_drv = {
40
  "Polish eID card (e-dowód, eDO)",
41
  "edo",
42
  &edo_ops,
43
  NULL, 0, NULL
44
};
45
46
47
static const struct sc_atr_table edo_atrs[] = {
48
  { "3b:84:80:01:47:43:50:43:12", NULL, NULL, SC_CARD_TYPE_EDO, 0, NULL },
49
  { NULL, NULL, NULL, 0, 0, NULL }
50
};
51
52
53
static struct {
54
  int len;
55
  struct sc_object_id oid;
56
} edo_curves[] = {
57
  // secp384r1
58
  {384, {{1, 3, 132, 0, 34, -1}}}
59
};
60
61
62
42.5k
static int edo_match_card(sc_card_t* card) {
63
42.5k
  SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
64
42.5k
  if (_sc_match_atr(card, edo_atrs, &card->type) >= 0) {
65
0
    sc_log(card->ctx, "ATR recognized as Polish eID card.");
66
0
    LOG_FUNC_RETURN(card->ctx, 1);
67
0
  }
68
42.5k
  LOG_FUNC_RETURN(card->ctx, 0);
69
42.5k
}
70
71
72
0
static int edo_get_can(sc_card_t* card, struct establish_pace_channel_input* pace_input) {
73
0
  SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
74
0
  const char* can;
75
76
0
  can = getenv("EDO_CAN");
77
78
0
  if (!can || can[0] != '\0') {
79
0
    for (size_t i = 0; card->ctx->conf_blocks[i]; ++i) {
80
0
      scconf_block** blocks = scconf_find_blocks(card->ctx->conf, card->ctx->conf_blocks[i], "card_driver", "edo");
81
0
      if (!blocks)
82
0
        continue;
83
0
      for (size_t j = 0; blocks[j]; ++j)
84
0
        if ((can = scconf_get_str(blocks[j], "can", NULL)))
85
0
          break;
86
0
      free(blocks);
87
0
    }
88
0
  }
89
90
0
  if (!can || 6 != strlen(can)) {
91
0
    sc_log(card->ctx, "Missing or invalid CAN. 6 digits required.");
92
0
    LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN);
93
0
  }
94
95
0
  pace_input->pin_id = PACE_PIN_ID_CAN;
96
0
  pace_input->pin = (const unsigned char*)can;
97
0
  pace_input->pin_length = 6;
98
99
0
  LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
100
0
}
101
102
103
0
static int edo_unlock(sc_card_t* card) {
104
0
  SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
105
106
0
  struct establish_pace_channel_input pace_input;
107
0
  struct establish_pace_channel_output pace_output;
108
109
0
  memset(&pace_input, 0, sizeof pace_input);
110
0
  memset(&pace_output, 0, sizeof pace_output);
111
112
0
  if (SC_SUCCESS != edo_get_can(card, &pace_input)) {
113
0
    sc_log(card->ctx, "Error reading CAN.");
114
0
    LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN);
115
0
  }
116
117
0
  if (SC_SUCCESS != perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02)) {
118
0
    sc_log(card->ctx, "Error verifying CAN.");
119
0
    LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN);
120
0
  }
121
122
0
  LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
123
0
}
124
125
126
struct edo_buff {
127
  u8 val[SC_MAX_APDU_RESP_SIZE];
128
  size_t len;
129
};
130
131
132
0
static int edo_select_mf(struct sc_card* card, struct edo_buff* buff) {
133
0
  LOG_FUNC_CALLED(card->ctx);
134
0
  struct sc_apdu apdu;
135
0
  sc_format_apdu_ex(&apdu, 00, 0xA4, 0x00, 0x00, NULL, 0, buff->val, sizeof buff->val);
136
0
  LOG_TEST_RET(card->ctx, sc_transmit_apdu(card, &apdu), "APDU transmit failed");
137
0
  LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "SW check failed");
138
0
  buff->len = apdu.resplen;
139
0
  LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
140
0
}
141
142
143
0
static int edo_select_df(struct sc_card* card, const u8 path[2], struct edo_buff* buff) {
144
0
  LOG_FUNC_CALLED(card->ctx);
145
0
  struct sc_apdu apdu;
146
0
  sc_format_apdu_ex(&apdu, 00, 0xA4, 0x01, 0x04, path, 2, buff->val, sizeof buff->val);
147
0
  LOG_TEST_RET(card->ctx, sc_transmit_apdu(card, &apdu), "APDU transmit failed");
148
0
  LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "SW check failed");
149
0
  buff->len = apdu.resplen;
150
0
  LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
151
0
}
152
153
154
0
static int edo_select_ef(struct sc_card* card, const u8 path[2], struct edo_buff* buff) {
155
0
  LOG_FUNC_CALLED(card->ctx);
156
0
  struct sc_apdu apdu;
157
0
  sc_format_apdu_ex(&apdu, 00, 0xA4, 0x02, 0x04, path, 2, buff->val, sizeof buff->val);
158
0
  LOG_TEST_RET(card->ctx, sc_transmit_apdu(card, &apdu), "APDU transmit failed");
159
0
  LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "SW check failed");
160
0
  buff->len = apdu.resplen;
161
0
  LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
162
0
}
163
164
165
0
static int edo_select_name(struct sc_card* card, const u8* name, size_t namelen, struct edo_buff* buff) {
166
0
  LOG_FUNC_CALLED(card->ctx);
167
0
  LOG_FUNC_RETURN(card->ctx, iso7816_select_aid(card, name, namelen, buff->val, &buff->len));
168
0
}
169
170
171
0
static int edo_select_path(struct sc_card* card, const u8* path, size_t pathlen, struct edo_buff* buff) {
172
0
  LOG_FUNC_CALLED(card->ctx);
173
0
  while (pathlen >= 2) {
174
0
    if (path[0] == 0x3F && path[1]  == 0x00)
175
0
      LOG_TEST_RET(card->ctx, edo_select_mf(card, buff), "MF select failed");
176
0
    else if (path[0] == 0xDF)
177
0
      LOG_TEST_RET(card->ctx, edo_select_df(card, path, buff), "DF select failed");
178
0
    else if (pathlen == 2)
179
0
      LOG_TEST_RET(card->ctx, edo_select_ef(card, path, buff), "EF select failed");
180
0
    else
181
0
      LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
182
183
0
    path += 2;
184
0
    pathlen -= 2;
185
0
  }
186
0
  if (pathlen)
187
0
    LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS);
188
0
  LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
189
0
}
190
191
192
/*! Selects file specified by given path.
193
 *
194
 * Card does not support selecting file at once, that's why it have to be done in following way:
195
 * 1. Select AID if provided,
196
 * 2. Select MF if provided,
197
 * 3. Select DF until provided,
198
 * 4. Select EF if provided.
199
 */
200
0
static int edo_select_file(struct sc_card* card, const struct sc_path* in_path, struct sc_file** file_out) {
201
0
  LOG_FUNC_CALLED(card->ctx);
202
0
  struct edo_buff buff;
203
204
0
  switch (in_path->type) {
205
0
    case SC_PATH_TYPE_PATH:
206
0
    case SC_PATH_TYPE_FILE_ID:
207
0
      if (in_path->aid.len)
208
0
        LOG_TEST_RET(card->ctx, edo_select_name(card, in_path->aid.value, in_path->aid.len, &buff), "Select AID failed");
209
0
      if (in_path->len)
210
0
        LOG_TEST_RET(card->ctx, edo_select_path(card, in_path->value, in_path->len, &buff), "Select path failed");
211
0
      break;
212
0
    case SC_PATH_TYPE_DF_NAME:
213
0
      LOG_TEST_RET(card->ctx, edo_select_name(card, in_path->value, in_path->len, &buff), "Select AID failed");
214
0
      break;
215
0
    default:
216
0
      LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
217
0
  }
218
219
0
  if (file_out) {
220
0
    if (buff.len < 2)
221
0
      LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);
222
0
    if (!(*file_out = sc_file_new()))
223
0
      LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
224
0
    (*file_out)->path = *in_path;
225
0
    LOG_TEST_RET(card->ctx, card->ops->process_fci(card, *file_out, buff.val, buff.len), "Process FCI failed");
226
0
  }
227
228
0
  LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
229
0
}
230
231
232
/*! Computes ECDSA signature.
233
 *
234
 * If ECDSA was used, the ASN.1 sequence of integers R,S returned by the
235
 * card needs to be converted to the raw concatenation of R,S for PKCS#11.
236
 */
237
0
static int edo_compute_signature(struct sc_card* card, const u8* data, size_t datalen, u8* out, size_t outlen) {
238
0
  LOG_FUNC_CALLED(card->ctx);
239
0
  u8 sig[SC_MAX_APDU_RESP_SIZE];
240
0
  LOG_TEST_RET(card->ctx, sc_get_iso7816_driver()->ops->compute_signature(card, data, datalen, sig, sizeof sig), "Internal signature failed");
241
0
  LOG_TEST_RET(card->ctx, sc_asn1_sig_value_sequence_to_rs(card->ctx, sig, sizeof sig, out, outlen), "ASN.1 conversion failed");
242
0
  LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
243
0
}
244
245
246
/*! Sets security environment
247
 *
248
 * Card expects key file to be selected first, followed by the
249
 * set security env packet with: 0x80, 0x01, 0xcc, 0x84, 0x01, 0x80|x,
250
 * where x is the key reference byte.
251
 */
252
0
static int edo_set_security_env(struct sc_card* card, const struct sc_security_env* env, int se_num) {
253
0
  LOG_FUNC_CALLED(card->ctx);
254
0
  struct sc_apdu apdu;
255
256
0
  if (env->algorithm == SC_ALGORITHM_EC && env->operation == SC_SEC_OPERATION_SIGN && env->flags & SC_SEC_ENV_KEY_REF_PRESENT) {
257
0
    u8 payload[] = {0x80, 0x01, 0xcc, 0x84, 0x01, 0x80 | env->key_ref[0]};
258
0
    sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x41, 0xB6, payload, sizeof payload, NULL, 0);
259
0
  } else
260
0
    LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
261
262
0
  LOG_TEST_RET(card->ctx, sc_select_file(card, &env->file_ref, NULL), "SELECT file failed");
263
0
  LOG_TEST_RET(card->ctx, sc_transmit_apdu(card, &apdu), "APDU transmit failed");
264
0
  LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "SW check failed");
265
266
0
  LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
267
0
}
268
269
270
/*! Initializes card driver.
271
 *
272
 * Card is known to support only short APDU-s.
273
 * Preinitialized keys are on secp384r1 curve.
274
 * PACE channel have to be established.
275
 */
276
0
static int edo_init(sc_card_t* card) {
277
0
  SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
278
279
0
  memset(&card->sm_ctx, 0, sizeof card->sm_ctx);
280
281
0
  card->max_send_size = SC_MAX_APDU_RESP_SIZE;
282
0
  card->max_recv_size = SC_MAX_APDU_RESP_SIZE;
283
284
0
  for (size_t i = 0; i < sizeof edo_curves / sizeof * edo_curves; ++i) {
285
0
    LOG_TEST_RET(card->ctx, _sc_card_add_ec_alg(
286
0
      card, edo_curves[i].len,
287
0
      SC_ALGORITHM_ECDSA_RAW | SC_ALGORITHM_ECDSA_HASH_NONE,
288
0
      0, &edo_curves[i].oid
289
0
    ), "Add EC alg failed");
290
0
  }
291
292
0
  LOG_TEST_RET(card->ctx, sc_enum_apps(card), "Enumerate apps failed");
293
294
0
  LOG_TEST_RET(card->ctx, edo_unlock(card), "Unlock card failed");
295
296
0
  LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
297
0
}
298
299
300
0
static int edo_logout(sc_card_t* card) {
301
0
  sc_sm_stop(card);
302
0
  return edo_unlock(card);
303
0
}
304
305
306
84.8k
struct sc_card_driver* sc_get_edo_driver(void) {
307
84.8k
  edo_ops = *sc_get_iso7816_driver()->ops;
308
84.8k
  edo_ops.match_card = edo_match_card;
309
84.8k
  edo_ops.init = edo_init;
310
84.8k
  edo_ops.select_file = edo_select_file;
311
84.8k
  edo_ops.set_security_env = edo_set_security_env;
312
84.8k
  edo_ops.compute_signature = edo_compute_signature;
313
84.8k
  edo_ops.logout = edo_logout;
314
315
84.8k
  return &edo_drv;
316
84.8k
}
317
318
#else
319
320
#include "libopensc/opensc.h"
321
322
struct sc_card_driver* sc_get_edo_driver(void) {
323
  return NULL;
324
}
325
326
#endif