Coverage Report

Created: 2025-11-16 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensc/src/libopensc/ctbcs.c
Line
Count
Source
1
/*
2
 * ctbcs.c: Extended CTBCS commands, used for pcsc and ct-api readers
3
 *
4
 * Copyright (C) 2002  Olaf Kirch <okir@suse.de>
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 <assert.h>
26
#include <stdlib.h>
27
#include <string.h>
28
29
#include "internal.h"
30
#include "ctbcs.h"
31
32
static void
33
ctbcs_init_apdu(sc_apdu_t *apdu, int cse, int ins, int p1, int p2)
34
0
{
35
0
  memset(apdu, 0, sizeof(*apdu));
36
0
  apdu->cse = cse;
37
0
  apdu->cla = 0x20;
38
0
  apdu->ins = ins;
39
0
  apdu->p1  = p1;
40
0
  apdu->p2  = p2;
41
42
0
  apdu->control = 1;
43
0
}
44
45
static int
46
ctbcs_build_perform_verification_apdu(sc_apdu_t *apdu, struct sc_pin_cmd_data *data)
47
0
{
48
0
  const char *prompt;
49
0
  size_t buflen, count = 0, j = 0, len;
50
0
  static u8 buf[SC_MAX_APDU_BUFFER_SIZE];
51
0
  u8 control;
52
53
0
  ctbcs_init_apdu(apdu,
54
0
      SC_APDU_CASE_3_SHORT,
55
0
      CTBCS_INS_PERFORM_VERIFICATION,
56
0
      CTBCS_P1_INTERFACE1,
57
0
      0);
58
59
0
  buflen = sizeof(buf);
60
0
  prompt = data->pin1.prompt;
61
0
  if (prompt && *prompt) {
62
0
    len = strlen(prompt);
63
0
    if (len + 2 > buflen)
64
0
      return SC_ERROR_BUFFER_TOO_SMALL;
65
0
    buf[count++] = CTBCS_TAG_PROMPT;
66
0
    buf[count++] = len;
67
0
    memcpy(buf + count, prompt, len);
68
0
    count += len;
69
0
  }
70
71
  /* card apdu must be last in packet */
72
0
  if (!data->apdu)
73
0
    return SC_ERROR_INTERNAL;
74
0
  if (count + 12 > buflen)
75
0
    return SC_ERROR_BUFFER_TOO_SMALL;
76
77
0
  j = count;
78
0
  buf[j++] = CTBCS_TAG_VERIFY_CMD;
79
0
  buf[j++] = 0x00;
80
81
  /* Control byte - length of PIN, and encoding */
82
0
  control = 0x00;
83
0
  if (data->pin1.encoding == SC_PIN_ENCODING_ASCII)
84
0
    control |= CTBCS_PIN_CONTROL_ENCODE_ASCII;
85
0
  else if (data->pin1.encoding != SC_PIN_ENCODING_BCD)
86
0
    return SC_ERROR_INVALID_ARGUMENTS;
87
0
  if (data->pin1.min_length == data->pin1.max_length)
88
0
    control |= data->pin1.min_length << CTBCS_PIN_CONTROL_LEN_SHIFT;
89
0
  buf[j++] = control;
90
0
  buf[j++] = data->pin1.offset+1; /* Looks like offset is 1-based in CTBCS */
91
0
  buf[j++] = data->apdu->cla;
92
0
  buf[j++] = data->apdu->ins;
93
0
  buf[j++] = data->apdu->p1;
94
0
  buf[j++] = data->apdu->p2;
95
96
0
  if (data->flags & SC_PIN_CMD_NEED_PADDING) {
97
0
    len = data->pin1.pad_length;
98
0
    if (2 + j + len > buflen)
99
0
      return SC_ERROR_BUFFER_TOO_SMALL;
100
0
    buf[j++] = len;
101
0
    memset(buf+j, data->pin1.pad_char, len);
102
0
    j += len;
103
0
  }
104
105
0
  buf[count+1] = j - count - 2;
106
0
  count = j;
107
108
0
  apdu->lc = apdu->datalen = count;
109
0
  apdu->data = buf;
110
111
0
  return 0;
112
0
}
113
114
static int
115
ctbcs_build_modify_verification_apdu(sc_apdu_t *apdu, struct sc_pin_cmd_data *data)
116
0
{
117
0
  const char *prompt;
118
0
  size_t buflen, count = 0, j = 0, len;
119
0
  static u8 buf[SC_MAX_APDU_BUFFER_SIZE];
120
0
  u8 control;
121
122
0
  ctbcs_init_apdu(apdu,
123
0
      SC_APDU_CASE_3_SHORT,
124
0
      CTBCS_INS_MODIFY_VERIFICATION,
125
0
      CTBCS_P1_INTERFACE1,
126
0
      0);
127
128
0
  buflen = sizeof(buf);
129
0
  prompt = data->pin1.prompt;
130
0
  if (prompt && *prompt) {
131
0
    len = strlen(prompt);
132
0
    if (len + 2 > buflen)
133
0
      return SC_ERROR_BUFFER_TOO_SMALL;
134
0
    buf[count++] = CTBCS_TAG_PROMPT;
135
0
    buf[count++] = len;
136
0
    memcpy(buf + count, prompt, len);
137
0
    count += len;
138
0
  }
139
140
  /* card apdu must be last in packet */
141
0
  if (!data->apdu)
142
0
    return SC_ERROR_INTERNAL;
143
0
  if (count + 12 > buflen)
144
0
    return SC_ERROR_BUFFER_TOO_SMALL;
145
146
0
  j = count;
147
0
  buf[j++] = CTBCS_TAG_VERIFY_CMD;
148
0
  buf[j++] = 0x00;
149
150
  /* Control byte - length of PIN, and encoding */
151
0
  control = 0x00;
152
0
  if (data->pin1.encoding == SC_PIN_ENCODING_ASCII)
153
0
    control |= CTBCS_PIN_CONTROL_ENCODE_ASCII;
154
0
  else if (data->pin1.encoding != SC_PIN_ENCODING_BCD)
155
0
    return SC_ERROR_INVALID_ARGUMENTS;
156
0
  if (data->pin1.min_length == data->pin1.max_length)
157
0
    control |= data->pin1.min_length << CTBCS_PIN_CONTROL_LEN_SHIFT;
158
0
  buf[j++] = control;
159
0
  buf[j++] = data->pin1.offset+1; /* Looks like offset is 1-based in CTBCS */
160
0
  buf[j++] = data->pin2.offset+1;
161
0
  buf[j++] = data->apdu->cla;
162
0
  buf[j++] = data->apdu->ins;
163
0
  buf[j++] = data->apdu->p1;
164
0
  buf[j++] = data->apdu->p2;
165
166
0
  if (data->flags & SC_PIN_CMD_NEED_PADDING) {
167
0
    len = data->pin1.pad_length + data->pin2.pad_length;
168
0
    if (2 + j + len > buflen)
169
0
      return SC_ERROR_BUFFER_TOO_SMALL;
170
0
    buf[j++] = len;
171
0
    memset(buf+j, data->pin1.pad_char, len);
172
0
    j += len;
173
0
  }
174
175
0
  buf[count+1] = j - count - 2;
176
0
  count = j;
177
178
0
  apdu->lc = apdu->datalen = count;
179
0
  apdu->data = buf;
180
181
0
  return 0;
182
0
}
183
184
int
185
ctbcs_pin_cmd(sc_reader_t *reader, struct sc_pin_cmd_data *data)
186
0
{
187
0
  sc_card_t dummy_card, *card;
188
0
  sc_apdu_t apdu;
189
0
  struct sc_card_operations ops;
190
0
  int r, s;
191
192
0
  switch (data->cmd) {
193
0
  case SC_PIN_CMD_VERIFY:
194
0
    r = ctbcs_build_perform_verification_apdu(&apdu, data);
195
0
    if (r != SC_SUCCESS)
196
0
      return r;
197
0
    break;
198
0
  case SC_PIN_CMD_CHANGE:
199
0
  case SC_PIN_CMD_UNBLOCK:
200
0
    r = ctbcs_build_modify_verification_apdu(&apdu, data);
201
0
    if (r != SC_SUCCESS)
202
0
      return r;
203
0
    break;
204
0
  default:
205
0
    sc_log(reader->ctx,  "Unknown PIN command %d", data->cmd);
206
0
    return SC_ERROR_NOT_SUPPORTED;
207
0
  }
208
209
0
  memset(&ops, 0, sizeof(ops));
210
0
  memset(&dummy_card, 0, sizeof(dummy_card));
211
0
  dummy_card.reader = reader;
212
0
  dummy_card.ctx = reader->ctx;
213
0
  r = sc_mutex_create(reader->ctx, &dummy_card.mutex);
214
0
  if (r != SC_SUCCESS)
215
0
    return r;
216
0
  dummy_card.ops   = &ops;
217
0
  card = &dummy_card;
218
219
0
  r = sc_transmit_apdu(card, &apdu);
220
0
  s = sc_mutex_destroy(reader->ctx, card->mutex);
221
0
  if (s != SC_SUCCESS) {
222
0
    sc_log(reader->ctx,  "unable to destroy mutex\n");
223
0
    return s;
224
0
  }
225
0
  LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
226
  
227
  /* Check CTBCS status word */
228
0
  switch (((unsigned int) apdu.sw1 << 8) | apdu.sw2) {
229
0
  case 0x9000:
230
0
    r = 0;
231
0
    break;
232
0
  case 0x6400: /* Input timed out */
233
0
    r = SC_ERROR_KEYPAD_TIMEOUT;
234
0
    break;
235
0
  case 0x6401: /* Input cancelled */
236
0
    r = SC_ERROR_KEYPAD_CANCELLED;
237
0
    break;
238
0
  case 0x6402: /* PINs did not match */
239
0
    r = SC_ERROR_KEYPAD_PIN_MISMATCH;
240
0
    break;
241
0
  case 0x6700: /* message too long */
242
0
    r = SC_ERROR_KEYPAD_MSG_TOO_LONG;
243
0
    break;
244
0
  default:
245
0
    r = SC_ERROR_CARD_CMD_FAILED;
246
0
    break;
247
0
  }
248
0
  LOG_TEST_RET(card->ctx, r, "PIN command failed");
249
250
  /* Calling Function may expect SW1/SW2 in data-apdu set... */
251
0
  if (data->apdu) {
252
0
    data->apdu->sw1 = apdu.sw1;
253
0
    data->apdu->sw2 = apdu.sw2;
254
0
  }
255
256
0
  return 0;
257
0
}