Coverage Report

Created: 2026-08-13 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensc/src/libopensc/pkcs15-cedulauy.c
Line
Count
Source
1
/*
2
 * PKCS#15 emulation for the Uruguayan national eID (cédula de identidad digital),
3
 * driven by card-cedulauy.c.
4
 *
5
 * The card's on-card EF(TokenInfo) encodes one supportedAlgorithms entry with an
6
 * empty SEQUENCE (30 00) where PKCS#15 expects NULL/OID, which the generic binder
7
 * rejects. Instead of parsing the on-card PKCS#15 structure, this emulator builds
8
 * a synthetic view from AGESIC's publicly documented file layout:
9
 *   - IAS application AID  A0 00 00 00 18 40 00 00 01 63 42 00
10
 *   - signing certificate  EF B001
11
 *   - signing key          reference 0x01 (RSA 2048)
12
 *   - Global PIN           reference 0x11 (VERIFY 00 20 00 11)
13
 *   - identity data        EFs 7001/7002/7004/700B under DF 7000
14
 * References: AGESIC "Documentación técnica de la cédula de identidad con chip"
15
 * and the AGESIC reference code at https://github.com/eIDuy/apdu-services .
16
 *
17
 * Copyright (C) 2026 Carlos Andrés Planchón Prestes <carlosandresplanchonprestes@gmail.com>
18
 *
19
 * This library is free software; you can redistribute it and/or
20
 * modify it under the terms of the GNU Lesser General Public
21
 * License as published by the Free Software Foundation; either
22
 * version 2.1 of the License, or (at your option) any later version.
23
 *
24
 * This library is distributed in the hope that it will be useful,
25
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
27
 * Lesser General Public License for more details.
28
 *
29
 * You should have received a copy of the GNU Lesser General Public
30
 * License along with this library; if not, write to the Free Software
31
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
32
 */
33
34
#ifdef HAVE_CONFIG_H
35
#include <config.h>
36
#endif
37
38
#include "common/compat_strlcpy.h"
39
#include "internal.h"
40
#include "log.h"
41
#include "pkcs15.h"
42
#include <stdlib.h>
43
#include <string.h>
44
45
/* IAS application AID (AGESIC, documented) */
46
static const unsigned char cedulauy_aid[] = {
47
    0xA0, 0x00, 0x00, 0x00, 0x18, 0x40, 0x00, 0x00, 0x01, 0x63, 0x42, 0x00};
48
49
67
#define CEDULAUY_CERT_FID "B001" /* signing certificate EF */
50
67
#define CEDULAUY_KEY_REF  0x01   /* signing private key reference */
51
67
#define CEDULAUY_PIN_REF  0x11   /* Global PIN reference */
52
268
#define CEDULAUY_OBJ_ID   0x01   /* links cert <-> prkey */
53
54
/* Public identity data files under DF 7000 (AGESIC layout), all readable
55
 * without PIN.  They are exposed as raw PKCS#15 data objects carrying the
56
 * BER-TLV content documented by AGESIC; callers parse the tags themselves.
57
 * EF 711D (the ICAO SOD) is not exposed: it is absent on earlier batches. */
58
static const struct {
59
  const char *path; /* absolute path; the MF is emulated by the driver */
60
  const char *label;
61
} cedulauy_data_files[] = {
62
    {"3F0070007001", "Numero de documento"},
63
    {"3F0070007002", "Datos biograficos"  },
64
    {"3F0070007004", "Fotografia"      },
65
    {"3F007000700B", "MRZ"          },
66
};
67
68
static int
69
sc_pkcs15emu_cedulauy_init(struct sc_pkcs15_card *p15card)
70
67
{
71
67
  struct sc_context *ctx = p15card->card->ctx;
72
67
  struct sc_aid aid;
73
67
  int r;
74
67
  size_t i;
75
76
67
  struct sc_pkcs15_auth_info pin_info = {0};
77
67
  struct sc_pkcs15_object pin_obj = {0};
78
67
  struct sc_pkcs15_cert_info cert_info = {0};
79
67
  struct sc_pkcs15_object cert_obj = {0};
80
67
  struct sc_pkcs15_prkey_info prkey_info = {0};
81
67
  struct sc_pkcs15_object prkey_obj = {0};
82
67
  struct sc_pkcs15_cert *cert = NULL;
83
67
  struct sc_app_info *appinfo;
84
85
67
  LOG_FUNC_CALLED(ctx);
86
87
67
  memcpy(aid.value, cedulauy_aid, sizeof cedulauy_aid);
88
67
  aid.len = sizeof cedulauy_aid;
89
90
67
  appinfo = calloc(1, sizeof(struct sc_app_info));
91
67
  if (appinfo == NULL) {
92
0
    LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
93
0
  }
94
67
  appinfo->aid = aid;
95
67
  appinfo->ddo.aid = aid;
96
67
  p15card->app = appinfo;
97
98
  /* Global PIN (reference 0x11, ASCII numeric, zero-padded to 12 bytes). */
99
67
  pin_info.auth_id.value[0] = CEDULAUY_OBJ_ID;
100
67
  pin_info.auth_id.len = 1;
101
67
  pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN;
102
67
  pin_info.attrs.pin.reference = CEDULAUY_PIN_REF;
103
67
  pin_info.attrs.pin.flags = SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_NEEDS_PADDING;
104
67
  pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC;
105
67
  pin_info.attrs.pin.min_length = 4;
106
67
  pin_info.attrs.pin.stored_length = 12;
107
67
  pin_info.attrs.pin.max_length = 12;
108
67
  pin_info.attrs.pin.pad_char = 0x00;
109
67
  pin_info.tries_left = -1;
110
67
  pin_info.max_tries = -1;
111
67
  strlcpy(pin_obj.label, "PIN", sizeof pin_obj.label);
112
67
  r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info);
113
67
  LOG_TEST_RET(ctx, r, "Cannot add Global PIN object");
114
115
  /* Signing certificate (EF B001 under the IAS application). */
116
67
  sc_format_path("i" CEDULAUY_CERT_FID, &cert_info.path); /* 'i' => select by file ID */
117
67
  cert_info.path.aid = aid;
118
67
  cert_info.id.value[0] = CEDULAUY_OBJ_ID;
119
67
  cert_info.id.len = 1;
120
67
  strlcpy(cert_obj.label, "Certificado de Firma", sizeof cert_obj.label);
121
67
  r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info);
122
67
  LOG_TEST_RET(ctx, r, "Cannot add signing certificate object");
123
124
  /* Set the token label and serial number from the certificate, when
125
   * readable.  The serial number also keys the file cache. */
126
67
  if (sc_pkcs15_read_certificate(p15card, &cert_info, 0, &cert) == SC_SUCCESS) {
127
0
    static const struct sc_object_id cn_oid = {
128
0
        {2, 5, 4, 3, -1}
129
0
    };
130
0
    u8 *cn = NULL;
131
0
    size_t cn_len = 0;
132
0
    const u8 *serial = cert->serial;
133
0
    size_t serial_len = cert->serial_len;
134
135
0
    sc_pkcs15_get_name_from_dn(ctx, cert->subject, cert->subject_len,
136
0
        &cn_oid, &cn, &cn_len);
137
0
    if (cn_len > 0) {
138
0
      char *label = malloc(cn_len + 1);
139
0
      if (label) {
140
0
        memcpy(label, cn, cn_len);
141
0
        label[cn_len] = '\0';
142
0
        free(p15card->tokeninfo->label);
143
0
        p15card->tokeninfo->label = label;
144
0
      }
145
0
    }
146
0
    free(cn);
147
148
    /* strip the ASN.1 INTEGER header, if present */
149
0
    if (serial_len > 2 && serial[0] == 0x02 && serial[1] == serial_len - 2) {
150
0
      serial += 2;
151
0
      serial_len -= 2;
152
0
    }
153
0
    if (serial_len > 0) {
154
0
      char *sn = malloc(serial_len * 2 + 1);
155
0
      if (sn) {
156
0
        sc_bin_to_hex(serial, serial_len, sn, serial_len * 2 + 1, 0);
157
0
        free(p15card->tokeninfo->serial_number);
158
0
        p15card->tokeninfo->serial_number = sn;
159
0
      }
160
0
    }
161
162
0
    sc_pkcs15_free_certificate(cert);
163
0
  }
164
165
  /* Signing private key (reference 0x01, RSA 2048, PIN-protected). */
166
67
  prkey_info.id.value[0] = CEDULAUY_OBJ_ID;
167
67
  prkey_info.id.len = 1;
168
67
  prkey_info.usage = SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
169
67
  prkey_info.native = 1;
170
67
  prkey_info.key_reference = CEDULAUY_KEY_REF;
171
67
  prkey_info.modulus_length = 2048;
172
67
  prkey_obj.auth_id.value[0] = CEDULAUY_OBJ_ID;
173
67
  prkey_obj.auth_id.len = 1;
174
67
  prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE;
175
67
  strlcpy(prkey_obj.label, "Clave de Firma", sizeof prkey_obj.label);
176
67
  r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info);
177
67
  LOG_TEST_RET(ctx, r, "Cannot add signing private key object");
178
179
  /* Public identity data objects (DF 7000), readable without PIN. */
180
335
  for (i = 0; i < sizeof cedulauy_data_files / sizeof cedulauy_data_files[0]; i++) {
181
268
    struct sc_pkcs15_data_info dinfo = {0};
182
268
    struct sc_pkcs15_object dobj = {0};
183
184
268
    sc_format_path(cedulauy_data_files[i].path, &dinfo.path);
185
268
    strlcpy(dinfo.app_label, cedulauy_data_files[i].label, sizeof dinfo.app_label);
186
268
    strlcpy(dobj.label, cedulauy_data_files[i].label, sizeof dobj.label);
187
268
    r = sc_pkcs15emu_add_data_object(p15card, &dobj, &dinfo);
188
268
    LOG_TEST_RET(ctx, r, "Cannot add identity data object");
189
268
  }
190
191
67
  LOG_FUNC_RETURN(ctx, SC_SUCCESS);
192
67
}
193
194
int
195
sc_pkcs15emu_cedulauy_init_ex(struct sc_pkcs15_card *p15card, struct sc_aid *aid)
196
10.1k
{
197
10.1k
  if (p15card->card->type != SC_CARD_TYPE_CEDULAUY)
198
10.0k
    return SC_ERROR_WRONG_CARD;
199
200
67
  return sc_pkcs15emu_cedulauy_init(p15card);
201
10.1k
}