Coverage Report

Created: 2025-11-24 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensc/src/libopensc/card-esteid2025.c
Line
Count
Source
1
/*
2
 * Driver for EstEID card issued from December 2025.
3
 *
4
 * Copyright (C) 2025, Raul Metsma <raul@metsma.ee>
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 <string.h>
26
27
#include "asn1.h"
28
#include "gp.h"
29
#include "internal.h"
30
31
0
#define SIGNATURE_PAYLOAD_SIZE 0x30
32
33
/*
34
 * EstEID: https://www.id.ee/wp-content/uploads/2025/11/ged24035_015_tdc_est_eid_developer_guide.pdf
35
 * FinEID:
36
 * * App 4.0: https://dvv.fi/documents/16079645/17324992/S1v40+(1).pdf/56a167fe-9f26-1fda-7d76-cfbbb29d184e/S1v40+(1).pdf
37
 * * Imp 4.0: https://dvv.fi/documents/16079645/17324992/S4-1v40.pdf/55bddc08-6893-b4b4-73fa-24dced600198/S4-1v40.pdf
38
 * * Imp 4.1: https://dvv.fi/documents/16079645/17324992/S4-1v41.pdf/63afc997-ed16-a00b-bdca-e9971b164137/S4-1v41.pdf
39
 */
40
static const struct sc_atr_table esteid_atrs[] = {
41
    {"3b:ff:96:00:00:80:31:fe:43:80:31:b8:53:65:49:44:64:b0:85:05:10:12:23:3f:1d", NULL, "EstEID 2025", SC_CARD_TYPE_ESTEID_2025, 0, NULL},
42
    {"3b:7f:96:00:00:80:31:b8:65:b0:85:05:00:11:12:24:60:82:90:00",   NULL, "FinEID 2022", SC_CARD_TYPE_FINEID_2022, 0, NULL},
43
    {"3b:7f:96:00:00:80:31:b8:65:b0:85:05:10:24:12:24:60:82:90:00",   NULL, "FinEID 2025", SC_CARD_TYPE_FINEID_2025, 0, NULL},
44
    {NULL,                   NULL, NULL,     0,           0, NULL}
45
};
46
47
static const struct sc_aid THALES_AID = {
48
    {0xA0, 0x00, 0x00, 0x00, 0x63, 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35},
49
    12
50
};
51
52
static const struct sc_card_operations *iso_ops = NULL;
53
static struct sc_card_operations esteid_ops;
54
55
static struct sc_card_driver esteid2025_driver = {"EstEID 2025", "esteid2025", &esteid_ops, NULL, 0, NULL};
56
57
#define SC_TRANSMIT_TEST_RET(card, apdu, text) \
58
0
  do { \
59
0
    LOG_TEST_RET(card->ctx, sc_transmit_apdu(card, &apdu), "APDU transmit failed"); \
60
0
    LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), text); \
61
0
  } while (0)
62
63
static int
64
esteid_match_card(sc_card_t *card)
65
5.54k
{
66
5.54k
  int i = _sc_match_atr(card, esteid_atrs, &card->type);
67
68
5.54k
  if (i >= 0 && gp_select_aid(card, &THALES_AID) == SC_SUCCESS) {
69
0
    card->name = esteid_atrs[i].name;
70
0
    return 1;
71
0
  }
72
5.54k
  return 0;
73
5.54k
}
74
75
static int
76
esteid_select_file(struct sc_card *card, const struct sc_path *in_path, struct sc_file **file_out)
77
0
{
78
0
  u8 resp[SC_MAX_APDU_RESP_SIZE];
79
0
  size_t resplen = sizeof(resp);
80
0
  int r;
81
0
  struct sc_file *file = NULL;
82
0
  struct sc_apdu apdu;
83
84
0
  LOG_FUNC_CALLED(card->ctx);
85
86
  // Only support full paths
87
0
  if (in_path->type != SC_PATH_TYPE_PATH) {
88
0
    LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
89
0
  }
90
91
0
  sc_format_apdu_ex(&apdu, card->cla, 0xA4, 0x08, 0x04, in_path->value, in_path->len, resp, resplen);
92
0
  SC_TRANSMIT_TEST_RET(card, apdu, "SELECT failed");
93
0
  if (file_out != NULL) {
94
0
    file = sc_file_new();
95
0
    if (file == NULL)
96
0
      LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
97
0
    r = iso_ops->process_fci(card, file, resp, resplen);
98
0
    if (r != SC_SUCCESS) {
99
0
      sc_file_free(file);
100
0
    }
101
0
    LOG_TEST_RET(card->ctx, r, "Process fci failed");
102
0
    *file_out = file;
103
0
  }
104
0
  LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
105
0
}
106
107
static int
108
esteid_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num)
109
0
{
110
0
  struct sc_apdu apdu;
111
0
  u8 cse_crt_sig[] = {0x80, 0x01, 0x54, 0x84, 0x01, 0x00};
112
0
  u8 cse_crt_der[] = {0x84, 0x01, 0x00};
113
114
0
  LOG_FUNC_CALLED(card->ctx);
115
116
0
  if (env == NULL || env->key_ref_len != 1)
117
0
    LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
118
119
0
  sc_log(card->ctx, "algo: %lu operation: %d keyref: %d", env->algorithm, env->operation, env->key_ref[0]);
120
121
0
  if (env->algorithm == SC_ALGORITHM_EC && env->operation == SC_SEC_OPERATION_SIGN) {
122
0
    cse_crt_sig[5] = env->key_ref[0];
123
0
    sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x41, 0xB6, cse_crt_sig, sizeof(cse_crt_sig), NULL, 0);
124
0
  } else if (env->algorithm == SC_ALGORITHM_EC && env->operation == SC_SEC_OPERATION_DERIVE) {
125
0
    cse_crt_der[2] = env->key_ref[0];
126
0
    sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x41, 0xB8, cse_crt_der, sizeof(cse_crt_der), NULL, 0);
127
0
  } else {
128
0
    LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
129
0
  }
130
0
  SC_TRANSMIT_TEST_RET(card, apdu, "SET SECURITY ENV failed");
131
132
0
  LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
133
0
}
134
135
static int
136
esteid_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, u8 *out, size_t outlen)
137
0
{
138
0
  struct sc_apdu apdu;
139
0
  u8 sbuf[SIGNATURE_PAYLOAD_SIZE + 2] = {0x90, SIGNATURE_PAYLOAD_SIZE};
140
0
  size_t le = MIN(SC_MAX_APDU_RESP_SIZE, MIN(SIGNATURE_PAYLOAD_SIZE * 2, outlen));
141
142
0
  LOG_FUNC_CALLED(card->ctx);
143
0
  if (data == NULL || out == NULL || datalen > SIGNATURE_PAYLOAD_SIZE)
144
0
    LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
145
146
  // left-pad if necessary
147
0
  memcpy(&sbuf[SIGNATURE_PAYLOAD_SIZE + 2 - datalen], data, MIN(datalen, SIGNATURE_PAYLOAD_SIZE));
148
0
  datalen = SIGNATURE_PAYLOAD_SIZE + 2;
149
150
0
  sc_format_apdu_ex(&apdu, 0x00, 0x2A, 0x90, 0xA0, sbuf, datalen, NULL, 0);
151
0
  SC_TRANSMIT_TEST_RET(card, apdu, "PSO Set Hash failed");
152
153
0
  sc_format_apdu_ex(&apdu, 0x00, 0x2A, 0x9E, 0x9A, NULL, 0, out, le);
154
0
  SC_TRANSMIT_TEST_RET(card, apdu, "PSO Compute Digital Signature failed");
155
156
0
  LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen);
157
0
}
158
159
static int
160
esteid_get_pin_info(sc_card_t *card, struct sc_pin_cmd_data *data)
161
0
{
162
0
  const u8 get_pin_info[] = {0xA0, 0x03, 0x83, 0x01, data->pin_reference};
163
0
  struct sc_apdu apdu;
164
0
  u8 apdu_resp[SC_MAX_APDU_RESP_SIZE];
165
0
  size_t taglen;
166
0
  LOG_FUNC_CALLED(card->ctx);
167
168
0
  sc_format_apdu_ex(&apdu, 0x00, 0xCB, 0x00, 0xFF, get_pin_info, sizeof(get_pin_info), apdu_resp, sizeof(apdu_resp));
169
0
  SC_TRANSMIT_TEST_RET(card, apdu, "GET DATA(pin info) failed");
170
0
  if (apdu.resplen < 3 || apdu.resp[0] != 0xA0)
171
0
    LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
172
173
0
  const u8 *tag = sc_asn1_find_tag(card->ctx, apdu_resp + 2, apdu.resplen - 2, 0xDF21, &taglen);
174
0
  if (tag == NULL || taglen == 0)
175
0
    LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
176
0
  data->pin1.tries_left = tag[0];
177
0
  data->pin1.max_tries = -1; // "no support, which means the one set in PKCS#15 emulation sticks
178
0
  data->pin1.logged_in = SC_PIN_STATE_UNKNOWN;
179
0
  tag += taglen;
180
0
  tag = sc_asn1_find_tag(card->ctx, tag, apdu.resplen - (tag - apdu_resp), 0xDF2F, &taglen);
181
0
  if (tag != NULL && taglen == 1 && tag[0] == 0x00) {
182
0
    data->pin1.logged_in |= SC_PIN_STATE_NEEDS_CHANGE;
183
0
  }
184
0
  LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
185
0
}
186
187
static int
188
esteid_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left)
189
0
{
190
0
  LOG_FUNC_CALLED(card->ctx);
191
0
  sc_log(card->ctx, "PIN CMD is %d", data->cmd);
192
0
  if (data->cmd == SC_PIN_CMD_GET_INFO) {
193
0
    sc_log(card->ctx, "SC_PIN_CMD_GET_INFO for %d", data->pin_reference);
194
0
    LOG_FUNC_RETURN(card->ctx, esteid_get_pin_info(card, data));
195
0
  }
196
0
  LOG_FUNC_RETURN(card->ctx, iso_ops->pin_cmd(card, data, tries_left));
197
0
}
198
199
static int
200
esteid_init(sc_card_t *card)
201
0
{
202
0
  unsigned long flags, ext_flags;
203
204
0
  flags = SC_ALGORITHM_ECDSA_RAW | SC_ALGORITHM_ECDH_CDH_RAW | SC_ALGORITHM_ECDSA_HASH_NONE;
205
0
  ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE | SC_ALGORITHM_EXT_EC_UNCOMPRESES;
206
207
0
  _sc_card_add_ec_alg(card, 384, flags, ext_flags, NULL);
208
209
0
  LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
210
0
}
211
212
static int
213
esteid_logout(sc_card_t *card)
214
0
{
215
0
  return gp_select_aid(card, &THALES_AID);
216
0
}
217
218
struct sc_card_driver *
219
sc_get_esteid2025_driver(void)
220
8.53k
{
221
8.53k
  struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
222
223
8.53k
  if (iso_ops == NULL)
224
1
    iso_ops = iso_drv->ops;
225
226
8.53k
  esteid_ops = *iso_drv->ops;
227
8.53k
  esteid_ops.match_card = esteid_match_card;
228
8.53k
  esteid_ops.init = esteid_init;
229
230
8.53k
  esteid_ops.select_file = esteid_select_file;
231
232
8.53k
  esteid_ops.set_security_env = esteid_set_security_env;
233
8.53k
  esteid_ops.compute_signature = esteid_compute_signature;
234
8.53k
  esteid_ops.pin_cmd = esteid_pin_cmd;
235
8.53k
  esteid_ops.logout = esteid_logout;
236
237
8.53k
  return &esteid2025_driver;
238
8.53k
}