1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
|
From 5110ae3ba33d165c43ea5eca8f929a82d81cb3fe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?=
<ng.hong.quan@gmail.com>
Date: Thu, 11 Apr 2013 11:47:51 +0700
Subject: [PATCH 12/26] OpenPGP: Support write certificate for Gnuk.
---
src/libopensc/card-openpgp.c | 158 +++++++++++++++++++++++++++++++++----------
1 file changed, 123 insertions(+), 35 deletions(-)
Index: opensc-20150513/src/libopensc/card-openpgp.c
===================================================================
--- opensc-20150513.orig/src/libopensc/card-openpgp.c
+++ opensc-20150513/src/libopensc/card-openpgp.c
@@ -735,6 +735,8 @@ pgp_iterate_blobs(struct blob *blob, int
static int
pgp_read_blob(sc_card_t *card, struct blob *blob)
{
+ struct pgp_priv_data *priv = DRVDATA (card);
+
if (blob->data != NULL)
return SC_SUCCESS;
if (blob->info == NULL)
@@ -745,6 +747,11 @@ pgp_read_blob(sc_card_t *card, struct bl
size_t buf_len = (card->caps & SC_CARD_CAP_APDU_EXT)
? sizeof(buffer) : 256;
+ /* Buffer length for certificate */
+ if (blob->id == DO_CERT && priv->max_cert_size > 0) {
+ buf_len = MIN(priv->max_cert_size, sizeof(buffer));
+ }
+
/* Buffer length for Gnuk pubkey */
if (card->type == SC_CARD_TYPE_OPENPGP_GNUK &&
(blob->id == 0xa400 || blob->id == 0xb600 || blob->id == 0xb800
@@ -1200,49 +1207,75 @@ pgp_get_data(sc_card_t *card, unsigned i
LOG_FUNC_RETURN(card->ctx, apdu.resplen);
}
-/* ABI: PUT DATA */
-static int
-pgp_put_data(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len)
+
+/* Internal: Write certificate for Gnuk */
+static int gnuk_write_certificate(sc_card_t *card, const u8 *buf, size_t length)
{
+ sc_context_t *ctx = card->ctx;
+ size_t i = 0;
sc_apdu_t apdu;
+ u8 *part;
+ size_t plen;
+ int r = SC_SUCCESS;
+
+ LOG_FUNC_CALLED(ctx);
+
+ /* If null data is passed, delete certificate */
+ if (buf == NULL || length == 0) {
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xD6, 0x85, 0);
+ r = sc_transmit_apdu(card, &apdu);
+ LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
+ /* Check response */
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+ LOG_FUNC_RETURN(card->ctx, length);
+ }
+
+ /* Ref: gnuk_put_binary_libusb.py and gnuk_token.py in Gnuk source tree */
+ /* Split data to segments of 256 bytes. Send each segment via command chaining,
+ * with particular P1 byte for each segment */
+ while (i*256 < length) {
+ part = (u8 *)buf + i*256;
+ plen = MIN(length - i*256, 256);
+
+ sc_log(card->ctx, "Write part %d from offset 0x%X, len %d", i+1, part, plen);
+
+ if (i == 0) {
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD6, 0x85, 0);
+ }
+ else {
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD6, i, 0);
+ }
+ apdu.flags |= SC_APDU_FLAGS_CHAINING;
+ apdu.data = part;
+ apdu.datalen = apdu.lc = plen;
+
+ r = sc_transmit_apdu(card, &apdu);
+ LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
+ /* Check response */
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+ LOG_TEST_RET(card->ctx, r, "UPDATE BINARY returned error");
+
+ /* To next part */
+ i++;
+ }
+ LOG_FUNC_RETURN(card->ctx, length);
+}
+
+
+/* Internal: Use PUT DATA command to write */
+static int
+pgp_put_data_plain(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len)
+{
struct pgp_priv_data *priv = DRVDATA(card);
- struct blob *affected_blob = NULL;
- struct do_info *dinfo = NULL;
+ sc_context_t *ctx = card->ctx;
+ sc_apdu_t apdu;
u8 ins = 0xDA;
u8 p1 = tag >> 8;
u8 p2 = tag & 0xFF;
u8 apdu_case = SC_APDU_CASE_3;
int r;
- LOG_FUNC_CALLED(card->ctx);
-
- /* Check if the tag is writable */
- affected_blob = pgp_find_blob(card, tag);
-
- /* Non-readable DOs have no represented blob, we have to check from pgp_get_info_by_tag */
- if (affected_blob == NULL)
- dinfo = pgp_get_info_by_tag(card, tag);
- else
- dinfo = affected_blob->info;
-
- if (dinfo == NULL) {
- sc_log(card->ctx, "The DO %04X does not exist.", tag);
- LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
- }
- else if ((dinfo->access & WRITE_MASK) == WRITE_NEVER) {
- sc_log(card->ctx, "DO %04X is not writable.", tag);
- LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ALLOWED);
- }
-
- /* Check data size.
- * We won't check other DOs than 7F21 (certificate), because their capacity
- * is hard-codded and may change in various version of the card. If we check here,
- * the driver may be sticked to a limit version number of card.
- * 7F21 size is soft-coded, so we can check it. */
- if (tag == DO_CERT && buf_len > priv->max_cert_size) {
- sc_log(card->ctx, "Data size %ld exceeds DO size limit %ld.", buf_len, priv->max_cert_size);
- LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_LENGTH);
- }
+ LOG_FUNC_CALLED(ctx);
/* Extended Header list (004D DO) needs a variant of PUT DATA command */
if (tag == 0x004D) {
@@ -1268,15 +1301,70 @@ pgp_put_data(sc_card_t *card, unsigned i
apdu.lc = buf_len;
}
else {
+ /* This case is to empty DO */
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, ins, p1, p2);
}
/* Send APDU to card */
r = sc_transmit_apdu(card, &apdu);
- LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
+ LOG_TEST_RET(ctx, r, "APDU transmit failed");
/* Check response */
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+ if (r < 0)
+ LOG_FUNC_RETURN(ctx, r);
+
+ LOG_FUNC_RETURN(ctx, buf_len);
+}
+
+/* ABI: PUT DATA */
+static int
+pgp_put_data(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len)
+{
+ struct pgp_priv_data *priv = DRVDATA(card);
+ struct blob *affected_blob = NULL;
+ struct do_info *dinfo = NULL;
+ int r;
+
+ LOG_FUNC_CALLED(card->ctx);
+
+ /* Check if the tag is writable */
+ if (priv->current->id != tag)
+ affected_blob = pgp_find_blob(card, tag);
+
+ /* Non-readable DOs have no represented blob, we have to check from pgp_get_info_by_tag */
+ if (affected_blob == NULL)
+ dinfo = pgp_get_info_by_tag(card, tag);
+ else
+ dinfo = affected_blob->info;
+
+ if (dinfo == NULL) {
+ sc_log(card->ctx, "The DO %04X does not exist.", tag);
+ LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
+ }
+ else if ((dinfo->access & WRITE_MASK) == WRITE_NEVER) {
+ sc_log(card->ctx, "DO %04X is not writable.", tag);
+ LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ALLOWED);
+ }
+
+ /* Check data size.
+ * We won't check other DOs than 7F21 (certificate), because their capacity
+ * is hard-codded and may change in various version of the card. If we check here,
+ * the driver may be sticked to a limit version number of card.
+ * 7F21 size is soft-coded, so we can check it. */
+ if (tag == DO_CERT && buf_len > priv->max_cert_size) {
+ sc_log(card->ctx, "Data size %ld exceeds DO size limit %ld.", buf_len, priv->max_cert_size);
+ LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_LENGTH);
+ }
+
+ if (tag == DO_CERT && card->type == SC_CARD_TYPE_OPENPGP_GNUK) {
+ /* Gnuk need a special way to write certificate. */
+ r = gnuk_write_certificate(card, buf, buf_len);
+ }
+ else {
+ r = pgp_put_data_plain(card, tag, buf, buf_len);
+ }
+
/* Instruct more in case of error */
if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Please verify PIN first.");
|