|
1 Upstream fixes already included in the latest community updates to coolkey v1.1.0 |
|
2 |
|
3 Addresses various known APDU handling issues. |
|
4 |
|
5 --- ORIGINAL/./src/libckyapplet/cky_applet.c 2016-06-24 16:08:03.920676358 -0400 |
|
6 +++ ././src/libckyapplet/cky_applet.c 2016-06-24 12:37:17.144225159 -0400 |
|
7 @@ -41,7 +41,13 @@ |
|
8 CKYStatus |
|
9 CKYAppletFactory_SelectFile(CKYAPDU *apdu, const void *param) |
|
10 { |
|
11 - return CKYAPDUFactory_SelectFile(apdu,(const CKYBuffer *)param); |
|
12 + return CKYAPDUFactory_SelectFile(apdu, 4, 0, (const CKYBuffer *)param); |
|
13 +} |
|
14 + |
|
15 +CKYStatus |
|
16 +CACAppletFactory_SelectFile(CKYAPDU *apdu, const void *param) |
|
17 +{ |
|
18 + return CKYAPDUFactory_SelectFile(apdu, 2, 12, (const CKYBuffer *)param); |
|
19 } |
|
20 |
|
21 CKYStatus |
|
22 @@ -97,6 +103,22 @@ |
|
23 } |
|
24 |
|
25 CKYStatus |
|
26 +CKYAppletFactory_ComputeECCSignatureOneStep(CKYAPDU *apdu, const void *param) |
|
27 +{ |
|
28 + const CKYAppletArgComputeECCSignature *ccs=(const CKYAppletArgComputeECCSignature *)param; |
|
29 + return CKYAPDUFactory_ComputeECCSignatureOneStep(apdu, ccs->keyNumber, |
|
30 + ccs->location, ccs->data, ccs->sig); |
|
31 +} |
|
32 + |
|
33 +CKYStatus |
|
34 +CKYAppletFactory_ComputeECCKeyAgreementOneStep(CKYAPDU *apdu, const void *param) |
|
35 +{ |
|
36 + |
|
37 + const CKYAppletArgComputeECCKeyAgreement *ccs=(const CKYAppletArgComputeECCKeyAgreement *)param; |
|
38 + return CKYAPDUFactory_ComputeECCKeyAgreementOneStep(apdu, ccs->keyNumber, ccs->location, ccs->publicValue, ccs->secretKey); |
|
39 +} |
|
40 + |
|
41 +CKYStatus |
|
42 CKYAppletFactory_CreatePIN(CKYAPDU *apdu, const void *param) |
|
43 { |
|
44 const CKYAppletArgCreatePIN *cps = (const CKYAppletArgCreatePIN *)param; |
|
45 @@ -134,6 +156,13 @@ |
|
46 /* Future add WriteObject */ |
|
47 |
|
48 CKYStatus |
|
49 +CKYAppletFactory_WriteObject(CKYAPDU *apdu, const void *param) |
|
50 +{ |
|
51 + const CKYAppletArgWriteObject *wos = (const CKYAppletArgWriteObject *)param; |
|
52 + return CKYAPDUFactory_WriteObject(apdu,wos->objectID,wos->offset,wos->size,wos->data); |
|
53 +} |
|
54 + |
|
55 +CKYStatus |
|
56 CKYAppletFactory_CreateObject(CKYAPDU *apdu, const void *param) |
|
57 { |
|
58 const CKYAppletArgCreateObject *cos=(const CKYAppletArgCreateObject *)param; |
|
59 @@ -192,7 +221,6 @@ |
|
60 { |
|
61 return CKYAPDUFactory_GetLifeCycleV2(apdu); |
|
62 } |
|
63 - |
|
64 CKYStatus |
|
65 CKYAppletFactory_GetRandom(CKYAPDU *apdu, const void *param) |
|
66 { |
|
67 @@ -219,17 +247,39 @@ |
|
68 } |
|
69 |
|
70 CKYStatus |
|
71 -CACAppletFactory_SignDecrypt(CKYAPDU *apdu, const void *param) |
|
72 +CACAppletFactory_SignDecryptStep(CKYAPDU *apdu, const void *param) |
|
73 { |
|
74 const CKYBuffer *buf=(CKYBuffer *)param; |
|
75 - return CACAPDUFactory_SignDecrypt(apdu, buf); |
|
76 + return CACAPDUFactory_SignDecrypt(apdu, CAC_P1_STEP, buf); |
|
77 +} |
|
78 + |
|
79 +CKYStatus |
|
80 +CACAppletFactory_SignDecryptFinal(CKYAPDU *apdu, const void *param) |
|
81 +{ |
|
82 + const CKYBuffer *buf=(CKYBuffer *)param; |
|
83 + return CACAPDUFactory_SignDecrypt(apdu, CAC_P1_FINAL, buf); |
|
84 +} |
|
85 + |
|
86 +CKYStatus |
|
87 +PIVAppletFactory_SignDecrypt(CKYAPDU *apdu, const void *param) |
|
88 +{ |
|
89 + const PIVAppletArgSignDecrypt *psd = (const PIVAppletArgSignDecrypt *)param; |
|
90 + return PIVAPDUFactory_SignDecrypt(apdu, psd->chain, psd->alg, psd->key, |
|
91 + psd->len, psd->buf); |
|
92 } |
|
93 |
|
94 CKYStatus |
|
95 CACAppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param) |
|
96 { |
|
97 const char *pin=(const char *)param; |
|
98 - return CACAPDUFactory_VerifyPIN(apdu, pin); |
|
99 + return CACAPDUFactory_VerifyPIN(apdu, CAC_LOGIN_GLOBAL, pin); |
|
100 +} |
|
101 + |
|
102 +CKYStatus |
|
103 +PIVAppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param) |
|
104 +{ |
|
105 + const char *pin=(const char *)param; |
|
106 + return CACAPDUFactory_VerifyPIN(apdu, PIV_LOGIN_LOCAL, pin); |
|
107 } |
|
108 |
|
109 CKYStatus |
|
110 @@ -240,6 +290,20 @@ |
|
111 } |
|
112 |
|
113 CKYStatus |
|
114 +PIVAppletFactory_GetCertificate(CKYAPDU *apdu, const void *param) |
|
115 +{ |
|
116 + CKYBuffer *tag =(CKYBuffer*)param; |
|
117 + return PIVAPDUFactory_GetData(apdu, tag, 0); |
|
118 +} |
|
119 + |
|
120 +CKYStatus |
|
121 +CACAppletFactory_ReadFile(CKYAPDU *apdu, const void *param) |
|
122 +{ |
|
123 + const CACAppletArgReadFile *rfs = (const CACAppletArgReadFile *)param; |
|
124 + return CACAPDUFactory_ReadFile(apdu, rfs->offset, rfs->type, rfs->count); |
|
125 +} |
|
126 + |
|
127 +CKYStatus |
|
128 CACAppletFactory_GetProperties(CKYAPDU *apdu, const void *param) |
|
129 { |
|
130 return CACAPDUFactory_GetProperties(apdu); |
|
131 @@ -299,6 +363,7 @@ |
|
132 CKYBuffer_Size(response) -2); |
|
133 } |
|
134 |
|
135 + |
|
136 CKYStatus |
|
137 CKYAppletFill_Byte(const CKYBuffer *response, CKYSize size, void *param) |
|
138 { |
|
139 @@ -451,7 +516,7 @@ |
|
140 CKYISOStatus *apduRC) |
|
141 { |
|
142 return CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, AID, NULL, |
|
143 - 0, CKYAppletFill_Null, NULL, apduRC); |
|
144 + CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC); |
|
145 } |
|
146 |
|
147 static CKYByte coolkeyid[] = {0x62, 0x76, 0x01, 0xff, 0x00, 0x00, 0x00 }; |
|
148 @@ -471,22 +536,23 @@ |
|
149 return ret; |
|
150 } |
|
151 |
|
152 -static CKYByte CACPKIid[] = {0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00 }; |
|
153 +static CKYByte CACPKIid[] = { 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01 }; |
|
154 /* |
|
155 * Select the CoolKey applet. Must happen after we start a transaction and |
|
156 * before we issue any applet specific command. |
|
157 */ |
|
158 CKYStatus |
|
159 -CACApplet_SelectPKI(CKYCardConnection *conn, CKYByte instance, |
|
160 - CKYISOStatus *apduRC) |
|
161 +CACApplet_SelectPKI(CKYCardConnection *conn, CKYBuffer *cacAID, |
|
162 + CKYByte instance, CKYISOStatus *apduRC) |
|
163 { |
|
164 CKYStatus ret; |
|
165 - CKYBuffer CACPKIAID; |
|
166 - CKYBuffer_InitFromData(&CACPKIAID, CACPKIid, sizeof(CACPKIid)); |
|
167 - CKYBuffer_SetChar(&CACPKIAID, 6, instance); |
|
168 - ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, &CACPKIAID, |
|
169 + CKYBuffer_AppendData(cacAID, CACPKIid, sizeof(CACPKIid)); |
|
170 + CKYBuffer_AppendChar(cacAID, instance); |
|
171 + ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, cacAID, |
|
172 NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC); |
|
173 - CKYBuffer_FreeData(&CACPKIAID); |
|
174 + if (ret != CKYSUCCESS) { |
|
175 + CKYBuffer_Resize(cacAID, 0); |
|
176 + } |
|
177 return ret; |
|
178 } |
|
179 |
|
180 @@ -509,11 +575,38 @@ |
|
181 CKYBuffer CAC_CM_AID; |
|
182 CKYBuffer_InitFromData(&CAC_CM_AID, cacmgrid, sizeof(cacmgrid)); |
|
183 ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, &CAC_CM_AID, |
|
184 - NULL, 0, CKYAppletFill_Null, NULL, apduRC); |
|
185 + NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC); |
|
186 CKYBuffer_FreeData(&CAC_CM_AID); |
|
187 return ret; |
|
188 } |
|
189 |
|
190 +static CKYByte cacCCCid[] = {0xa0, 0x00, 0x00, 0x01, 0x16, 0xdb, 0x00 }; |
|
191 +CKYStatus |
|
192 +CACApplet_SelectCCC(CKYCardConnection *conn, CKYISOStatus *apduRC) |
|
193 +{ |
|
194 + CKYStatus ret; |
|
195 + CKYBuffer CAC_CM_AID; |
|
196 + CKYBuffer_InitFromData(&CAC_CM_AID, cacCCCid, sizeof(cacCCCid)); |
|
197 + ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, &CAC_CM_AID, |
|
198 + NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC); |
|
199 + CKYBuffer_FreeData(&CAC_CM_AID); |
|
200 + return ret; |
|
201 +} |
|
202 + |
|
203 +CKYStatus |
|
204 +CACApplet_SelectFile(CKYCardConnection *conn, unsigned short ef, |
|
205 + CKYISOStatus *apduRC) |
|
206 +{ |
|
207 + CKYStatus ret; |
|
208 + CKYBuffer efBuf; |
|
209 + CKYBuffer_InitEmpty(&efBuf); |
|
210 + CKYBuffer_AppendShortLE(&efBuf, ef); |
|
211 + ret = CKYApplet_HandleAPDU(conn, CACAppletFactory_SelectFile, &efBuf, |
|
212 + NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC); |
|
213 + CKYBuffer_FreeData(&efBuf); |
|
214 + return ret; |
|
215 +} |
|
216 + |
|
217 /* |
|
218 * GetCPLC cluster -- must be called with CM selected |
|
219 */ |
|
220 @@ -667,8 +760,34 @@ |
|
221 ccd.keyNumber = keyNumber; |
|
222 ccd.location = location; |
|
223 ccd.data = data; |
|
224 - return CKYApplet_HandleAPDU(conn, CKYAppletFactory_ComputeCryptProcess, &ccd, |
|
225 - nonce, 0, CKYAppletFill_Null, NULL, apduRC); |
|
226 + return CKYApplet_HandleAPDU(conn, CKYAppletFactory_ComputeCryptProcess, |
|
227 + &ccd, nonce, 0, CKYAppletFill_Null, NULL, apduRC); |
|
228 +} |
|
229 + |
|
230 +/* computeECCValue returns data in the form : |
|
231 + * len: short |
|
232 + * data: byte[len] |
|
233 + * This fill routine returns A buffer with a copy of data and a length of len */ |
|
234 +static CKYStatus |
|
235 +ckyAppletFill_ComputeECCValueFinal(const CKYBuffer *response, |
|
236 + CKYSize size, void *param) |
|
237 +{ |
|
238 + CKYBuffer *cbuf = (CKYBuffer *)param; |
|
239 + CKYSize respSize = CKYBuffer_Size(response); |
|
240 + CKYSize dataLen; |
|
241 + |
|
242 + if (cbuf == 0) { |
|
243 + return CKYSUCCESS; /* app didn't want the result */ |
|
244 + } |
|
245 + /* data response code + length code */ |
|
246 + if (respSize < 4) { |
|
247 + return CKYAPDUFAIL; |
|
248 + } |
|
249 + dataLen = CKYBuffer_GetShort(response, 0); |
|
250 + if (dataLen > (respSize-4)) { |
|
251 + return CKYAPDUFAIL; |
|
252 + } |
|
253 + return CKYBuffer_Replace(cbuf, 0, CKYBuffer_Data(response)+2, dataLen); |
|
254 } |
|
255 |
|
256 /* computeCrypt returns data in the form : |
|
257 @@ -725,24 +844,48 @@ |
|
258 CKYAppletArgComputeCrypt ccd; |
|
259 CKYBuffer empty; |
|
260 CKYISOStatus status; |
|
261 + short dataSize = 0; |
|
262 int use2APDUs = 0; |
|
263 + int use_dl_object = CKYBuffer_Size(data) > 200 ; |
|
264 |
|
265 CKYBuffer_InitEmpty(&empty); |
|
266 ccd.keyNumber = keyNumber; |
|
267 ccd.mode = mode; |
|
268 ccd.direction = direction; |
|
269 - ccd.location = CKY_DL_APDU; |
|
270 + ccd.location = use_dl_object ? CKY_DL_OBJECT : CKY_DL_APDU; |
|
271 |
|
272 if (!apduRC) |
|
273 apduRC = &status; |
|
274 |
|
275 + if (use_dl_object) { |
|
276 + CKYBuffer sizeBuf; |
|
277 + |
|
278 + CKYBuffer_InitEmpty(&sizeBuf); |
|
279 + CKYBuffer_AppendShort(&sizeBuf, CKYBuffer_Size(data)); |
|
280 + |
|
281 + ret = CKYApplet_WriteObjectFull(conn, 0xffffffff, |
|
282 + 0, CKYBuffer_Size(&sizeBuf), nonce, |
|
283 + &sizeBuf, apduRC); |
|
284 + |
|
285 + CKYBuffer_FreeData(&sizeBuf); |
|
286 + if( ret != CKYSUCCESS) |
|
287 + goto fail; |
|
288 + |
|
289 + ret = CKYApplet_WriteObjectFull(conn, 0xffffffff, |
|
290 + 2, CKYBuffer_Size(data), nonce, |
|
291 + data, apduRC); |
|
292 + |
|
293 + if(ret != CKYSUCCESS) |
|
294 + goto fail; |
|
295 + } |
|
296 + |
|
297 if (mode == CKY_RSA_NO_PAD) { |
|
298 - ccd.data = data; |
|
299 + ccd.data = use_dl_object ? &empty : data; |
|
300 ccd.sig = sig; |
|
301 ret = CKYApplet_HandleAPDU(conn, |
|
302 CKYAppletFactory_ComputeCryptOneStep, &ccd, nonce, |
|
303 CKY_SIZE_UNKNOWN, ckyAppletFill_ComputeCryptFinal, |
|
304 - result, apduRC); |
|
305 + use_dl_object ? NULL : result, apduRC); |
|
306 if (ret == CKYAPDUFAIL && *apduRC == CKYISO_INCORRECT_P2) { |
|
307 use2APDUs = 1; /* maybe it's an old applet */ |
|
308 } |
|
309 @@ -759,13 +902,109 @@ |
|
310 CKYAppletFactory_ComputeCryptInit, &ccd, nonce, |
|
311 0, CKYAppletFill_Null, NULL, apduRC); |
|
312 if (ret == CKYSUCCESS) { |
|
313 - ccd.data = data; |
|
314 + ccd.data = use_dl_object ? &empty : data; |
|
315 ret = CKYApplet_HandleAPDU(conn, |
|
316 CKYAppletFactory_ComputeCryptFinal, &ccd, nonce, |
|
317 CKY_SIZE_UNKNOWN, ckyAppletFill_ComputeCryptFinal, |
|
318 - result, apduRC); |
|
319 + use_dl_object ? NULL : result, apduRC); |
|
320 } |
|
321 } |
|
322 + |
|
323 + if (use_dl_object && ret == CKYSUCCESS) { |
|
324 + CKYBuffer sizeOutBuf; |
|
325 + CKYBuffer_InitEmpty(&sizeOutBuf); |
|
326 + |
|
327 + ret = CKYApplet_ReadObjectFull(conn,0xffffffff, |
|
328 + 0, 2, |
|
329 + nonce,&sizeOutBuf,apduRC); |
|
330 + |
|
331 + if(ret != CKYSUCCESS) { |
|
332 + CKYBuffer_FreeData(&sizeOutBuf); |
|
333 + goto fail; |
|
334 + } |
|
335 + |
|
336 + dataSize = CKYBuffer_GetShort(&sizeOutBuf, 0); |
|
337 + |
|
338 + CKYBuffer_FreeData(&sizeOutBuf); |
|
339 + |
|
340 + ret = CKYApplet_ReadObjectFull(conn,0xffffffff, |
|
341 + 2, dataSize, |
|
342 + nonce,result,apduRC); |
|
343 + } |
|
344 + |
|
345 +fail: |
|
346 + |
|
347 + return ret; |
|
348 +} |
|
349 + |
|
350 +CKYStatus |
|
351 +CKYApplet_ComputeECCKeyAgreement(CKYCardConnection *conn, CKYByte keyNumber, |
|
352 + const CKYBuffer *publicValue, CKYBuffer *sharedSecret, |
|
353 + CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC) |
|
354 +{ |
|
355 + CKYStatus ret = CKYAPDUFAIL; |
|
356 + CKYAppletArgComputeECCKeyAgreement ccd; |
|
357 + CKYBuffer empty; |
|
358 + CKYISOStatus status; |
|
359 + /* Routine creates a sym key, should easily fit in one apdu */ |
|
360 + |
|
361 + CKYBuffer_InitEmpty(&empty); |
|
362 + ccd.keyNumber = keyNumber; |
|
363 + ccd.location = CKY_DL_APDU; |
|
364 + |
|
365 + if (!apduRC) |
|
366 + apduRC = &status; |
|
367 + |
|
368 + if (ccd.location == CKY_DL_APDU) { |
|
369 + ccd.publicValue = publicValue; |
|
370 + ccd.secretKey = sharedSecret; |
|
371 + ret = CKYApplet_HandleAPDU(conn, |
|
372 + CKYAppletFactory_ComputeECCKeyAgreementOneStep, &ccd, nonce, |
|
373 + CKY_SIZE_UNKNOWN, ckyAppletFill_ComputeECCValueFinal, |
|
374 + result, apduRC); |
|
375 + if (ret == CKYAPDUFAIL && *apduRC == CKYISO_INCORRECT_P2) { |
|
376 + return ret; |
|
377 + } |
|
378 + } |
|
379 + |
|
380 + return ret; |
|
381 +} |
|
382 + |
|
383 +CKYStatus |
|
384 +CKYApplet_ComputeECCSignature(CKYCardConnection *conn, CKYByte keyNumber, |
|
385 + const CKYBuffer *data, CKYBuffer *sig, |
|
386 + CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC) |
|
387 +{ |
|
388 + int use2APDUs = 0; |
|
389 + int use_dl_object = 0; |
|
390 + short dataSize = 0; |
|
391 + CKYStatus ret = CKYAPDUFAIL; |
|
392 + CKYAppletArgComputeECCSignature ccd; |
|
393 + CKYBuffer empty; |
|
394 + CKYISOStatus status; |
|
395 + |
|
396 + CKYBuffer_InitEmpty(&empty); |
|
397 + ccd.keyNumber = keyNumber; |
|
398 + |
|
399 + /* Assume APDU, the signature can only get so big with our key sizes, ~ 130 for 521 bit key. */ |
|
400 + ccd.location = CKY_DL_APDU; |
|
401 + |
|
402 + if (!apduRC) |
|
403 + apduRC = &status; |
|
404 + |
|
405 + if (ccd.location == CKY_DL_APDU) { |
|
406 + ccd.data = data; |
|
407 + ccd.sig = sig; |
|
408 + ret = CKYApplet_HandleAPDU(conn, |
|
409 + CKYAppletFactory_ComputeECCSignatureOneStep, &ccd, nonce, |
|
410 + CKY_SIZE_UNKNOWN, ckyAppletFill_ComputeECCValueFinal, |
|
411 + result, apduRC); |
|
412 + if (ret == CKYAPDUFAIL && *apduRC == CKYISO_INCORRECT_P2) { |
|
413 + return ret; |
|
414 + } |
|
415 + |
|
416 + } |
|
417 + |
|
418 return ret; |
|
419 } |
|
420 |
|
421 @@ -777,11 +1016,39 @@ |
|
422 CKYBuffer *result, CKYISOStatus *apduRC) |
|
423 { |
|
424 CKYStatus ret; |
|
425 - |
|
426 - ret = CKYApplet_HandleAPDU(conn, |
|
427 - CACAppletFactory_SignDecrypt, data, NULL, |
|
428 - CKYBuffer_Size(data), CKYAppletFill_ReplaceBuffer, |
|
429 + CKYSize dataSize = CKYBuffer_Size(data); |
|
430 + CKYOffset offset = 0; |
|
431 + CKYBuffer tmp; |
|
432 + |
|
433 + CKYBuffer_InitEmpty(&tmp); |
|
434 + |
|
435 + CKYBuffer_Resize(result, 0); |
|
436 + for(offset = 0; (dataSize-offset) > CKY_MAX_WRITE_CHUNK_SIZE; |
|
437 + offset += CKY_MAX_WRITE_CHUNK_SIZE) { |
|
438 + CKYBuffer_Resize(&tmp,0); |
|
439 + CKYBuffer_AppendBuffer(&tmp, data, offset, CKY_MAX_WRITE_CHUNK_SIZE); |
|
440 + ret = CKYApplet_HandleAPDU(conn, CACAppletFactory_SignDecryptStep, |
|
441 + &tmp, NULL, CKY_SIZE_UNKNOWN, |
|
442 + CKYAppletFill_AppendBuffer, |
|
443 + result, apduRC); |
|
444 + if (ret != CKYSUCCESS) { |
|
445 + goto done; |
|
446 + } |
|
447 + } |
|
448 + CKYBuffer_Resize(&tmp,0); |
|
449 + CKYBuffer_AppendBuffer(&tmp, data, offset, dataSize - offset); |
|
450 + ret = CKYApplet_HandleAPDU(conn, CACAppletFactory_SignDecryptFinal, |
|
451 + &tmp, NULL, CKY_SIZE_UNKNOWN, |
|
452 + CKYAppletFill_AppendBuffer, |
|
453 result, apduRC); |
|
454 + |
|
455 + if ((ret == CKYSUCCESS) && (CKYBuffer_Size(result) != dataSize)) { |
|
456 + /* RSA returns the same data size as input, didn't happen, so |
|
457 + * something is wrong. */ |
|
458 + } |
|
459 + |
|
460 +done: |
|
461 + CKYBuffer_FreeData(&tmp); |
|
462 return ret; |
|
463 } |
|
464 |
|
465 @@ -789,7 +1056,7 @@ |
|
466 * do a CAC VerifyPIN |
|
467 */ |
|
468 CKYStatus |
|
469 -CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin, |
|
470 +CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin, int local, |
|
471 CKYISOStatus *apduRC) |
|
472 { |
|
473 CKYStatus ret; |
|
474 @@ -798,7 +1065,7 @@ |
|
475 apduRC = &status; |
|
476 } |
|
477 |
|
478 - ret = CKYApplet_HandleAPDU(conn, |
|
479 + ret = CKYApplet_HandleAPDU(conn, local ? PIVAppletFactory_VerifyPIN : |
|
480 CACAppletFactory_VerifyPIN, pin, NULL, |
|
481 0, CKYAppletFill_Null, |
|
482 NULL, apduRC); |
|
483 @@ -811,6 +1078,7 @@ |
|
484 return ret; |
|
485 } |
|
486 |
|
487 + |
|
488 /* |
|
489 * Get a CAC Certificate |
|
490 */ |
|
491 @@ -840,6 +1108,63 @@ |
|
492 } |
|
493 return ret; |
|
494 } |
|
495 + |
|
496 +/* |
|
497 + * Read a CAC Tag/Value file |
|
498 + */ |
|
499 +CKYStatus |
|
500 +CACApplet_ReadFile(CKYCardConnection *conn, CKYByte type, CKYBuffer *buffer, |
|
501 + CKYISOStatus *apduRC) |
|
502 +{ |
|
503 + CKYStatus ret; |
|
504 + CKYISOStatus status; |
|
505 + CKYByte maxtransfer; |
|
506 + unsigned short offset = 0; |
|
507 + unsigned short size; |
|
508 + CACAppletArgReadFile rfs; |
|
509 + |
|
510 + CKYBuffer_Resize(buffer,0); |
|
511 + if (apduRC == NULL) { |
|
512 + apduRC = &status; |
|
513 + } |
|
514 + rfs.offset = 0; |
|
515 + rfs.count = 2; |
|
516 + rfs.type = type; |
|
517 + |
|
518 + /* APDU's are expensive, Grab a big chunk of the file first if possible */ |
|
519 + ret = CKYApplet_HandleAPDU(conn, |
|
520 + CACAppletFactory_ReadFile, &rfs, NULL, |
|
521 + rfs.count, CKYAppletFill_AppendBuffer, |
|
522 + buffer, apduRC); |
|
523 + /* file is probably smaller than 100 bytes, get the actual size first */ |
|
524 + if (ret != CKYSUCCESS) { |
|
525 + return ret; |
|
526 + } |
|
527 + size = CKYBuffer_GetShortLE(buffer, 0) + 2 /* include the length itself */; |
|
528 + maxtransfer = CKY_MAX_READ_CHUNK_SIZE; |
|
529 + /* get the rest of the buffer if necessary */ |
|
530 + for (offset = CKYBuffer_Size(buffer); size > offset; |
|
531 + offset = CKYBuffer_Size(buffer)) { |
|
532 + rfs.offset = offset; |
|
533 + rfs.count = MIN(size - offset, maxtransfer); |
|
534 + ret = CKYApplet_HandleAPDU(conn, |
|
535 + CACAppletFactory_ReadFile, &rfs, NULL, |
|
536 + rfs.count, CKYAppletFill_AppendBuffer, |
|
537 + buffer, apduRC); |
|
538 + if (ret != CKYSUCCESS) { |
|
539 + if (*apduRC == CAC_INVALID_PARAMS) { |
|
540 + maxtransfer = maxtransfer/2; |
|
541 + if (maxtransfer == 0) { |
|
542 + return ret; |
|
543 + } |
|
544 + } else { |
|
545 + return ret; |
|
546 + } |
|
547 + } |
|
548 + } |
|
549 + return ret; |
|
550 +} |
|
551 + |
|
552 CKYStatus |
|
553 CACApplet_GetCertificateFirst(CKYCardConnection *conn, CKYBuffer *cert, |
|
554 CKYSize *nextSize, CKYISOStatus *apduRC) |
|
555 @@ -890,6 +1215,278 @@ |
|
556 return ret; |
|
557 } |
|
558 |
|
559 +/* Select the PIV applet */ |
|
560 +static CKYByte pivAid[] = {0xa0, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, |
|
561 + 0x10, 0x00}; |
|
562 +CKYStatus |
|
563 +PIVApplet_Select(CKYCardConnection *conn, CKYISOStatus *apduRC) |
|
564 +{ |
|
565 + CKYStatus ret; |
|
566 + CKYBuffer PIV_Applet_AID,return_AID; |
|
567 + |
|
568 + CKYBuffer_InitEmpty(&return_AID); |
|
569 + CKYBuffer_InitFromData(&PIV_Applet_AID, pivAid, sizeof(pivAid)); |
|
570 + ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, |
|
571 + &PIV_Applet_AID, |
|
572 + NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_AppendBuffer, |
|
573 + &return_AID, apduRC); |
|
574 + /* Some cards return OK, but don't switch to our applet */ |
|
575 + /* PIV has a well defined return for it's select, check to see if we have |
|
576 + * a PIV card here */ |
|
577 + if (CKYBuffer_GetChar(&return_AID,0) != 0x61) { |
|
578 + /* not an application property template, so not a PIV. We could |
|
579 + * check that the aid tag (0x4f) and theallocation authority tag (0x79) |
|
580 + * are present, but what we are really avoiding is broken cards that |
|
581 + * lie about being able to switch to a particular applet, so the first |
|
582 + * tag should be sufficient */ |
|
583 + ret = CKYAPDUFAIL; /* what we should have gotten */ |
|
584 + } |
|
585 + CKYBuffer_FreeData(&PIV_Applet_AID); |
|
586 + CKYBuffer_FreeData(&return_AID); |
|
587 + return ret; |
|
588 +} |
|
589 + |
|
590 +/* |
|
591 + * Get a PIV Certificate |
|
592 + */ |
|
593 +CKYStatus |
|
594 +PIVApplet_GetCertificate(CKYCardConnection *conn, CKYBuffer *cert, int tag, |
|
595 + CKYISOStatus *apduRC) |
|
596 +{ |
|
597 + CKYStatus ret; |
|
598 + CKYISOStatus status; |
|
599 + CKYBuffer tagBuf; |
|
600 + |
|
601 + CKYBuffer_InitEmpty(&tagBuf); |
|
602 + CKYBuffer_Reserve(&tagBuf,4); /* can be up to 4 bytes */ |
|
603 + |
|
604 + CKYBuffer_Resize(cert,0); |
|
605 + if (apduRC == NULL) { |
|
606 + apduRC = &status; |
|
607 + } |
|
608 + if (tag >= 0x01000000) { |
|
609 + ret = CKYBuffer_AppendChar(&tagBuf, (tag >> 24) & 0xff); |
|
610 + if (ret != CKYSUCCESS) { goto loser; } |
|
611 + } |
|
612 + if (tag >= 0x010000) { |
|
613 + ret = CKYBuffer_AppendChar(&tagBuf, (tag >> 16) & 0xff); |
|
614 + if (ret != CKYSUCCESS) { goto loser; } |
|
615 + } |
|
616 + if (tag >= 0x0100) { |
|
617 + ret =CKYBuffer_AppendChar(&tagBuf, (tag >> 8) & 0xff); |
|
618 + if (ret != CKYSUCCESS) { goto loser; } |
|
619 + } |
|
620 + ret = CKYBuffer_AppendChar(&tagBuf, tag & 0xff); |
|
621 + if (ret != CKYSUCCESS) { goto loser; } |
|
622 + |
|
623 + |
|
624 + ret = CKYApplet_HandleAPDU(conn, |
|
625 + PIVAppletFactory_GetCertificate, &tagBuf, NULL, |
|
626 + CKY_SIZE_UNKNOWN, CKYAppletFill_AppendBuffer, cert, |
|
627 + apduRC); |
|
628 +loser: |
|
629 + CKYBuffer_FreeData(&tagBuf); |
|
630 + |
|
631 + return ret; |
|
632 +} |
|
633 + |
|
634 + |
|
635 +/* |
|
636 + * record the next ber tag and length. NOTE: this is a state machine. |
|
637 + * we can handle the case where we are passed the data just one byte |
|
638 + * at a time. |
|
639 + */ |
|
640 +static CKYStatus |
|
641 +pivUnwrap(const CKYBuffer *buf, CKYOffset *offset, |
|
642 + CKYSize *dataSize, PIVUnwrapState *unwrap) |
|
643 +{ |
|
644 + if (unwrap->tag == 0) { |
|
645 + unwrap->tag = CKYBuffer_GetChar(buf, *offset); |
|
646 + if (unwrap->tag == 0) unwrap->tag = 0xff; |
|
647 + (*offset)++; |
|
648 + (*dataSize)--; |
|
649 + } |
|
650 + if (*dataSize == 0) { |
|
651 + return CKYSUCCESS; |
|
652 + } |
|
653 + if (unwrap->length_bytes != 0) { |
|
654 + int len; |
|
655 + if (unwrap->length_bytes == -1) { |
|
656 + len = CKYBuffer_GetChar(buf, *offset); |
|
657 + unwrap->length_bytes = 0; |
|
658 + unwrap->length = len; |
|
659 + (*offset)++; |
|
660 + (*dataSize)--; |
|
661 + if (len & 0x80) { |
|
662 + unwrap->length = 0; |
|
663 + unwrap->length_bytes = len & 0x7f; |
|
664 + } |
|
665 + } |
|
666 + while ((*dataSize != 0) && (unwrap->length_bytes != 0)) { |
|
667 + len = CKYBuffer_GetChar(buf, *offset); |
|
668 + (*offset) ++; |
|
669 + (*dataSize) --; |
|
670 + unwrap->length = ((unwrap->length) << 8 | len); |
|
671 + unwrap->length_bytes--; |
|
672 + } |
|
673 + } |
|
674 + return CKYSUCCESS; |
|
675 +} |
|
676 + |
|
677 +/* |
|
678 + * Remove the BER wrapping first... |
|
679 + */ |
|
680 +static CKYStatus |
|
681 +pivAppletFill_AppendUnwrapBuffer(const CKYBuffer *response, |
|
682 + CKYSize size, void *param) |
|
683 +{ |
|
684 + PIVAppletRespSignDecrypt *prsd = (PIVAppletRespSignDecrypt *)param; |
|
685 + CKYBuffer *buf = prsd->buf; |
|
686 + CKYSize dataSize = CKYBuffer_Size(response); |
|
687 + CKYOffset offset = 0; |
|
688 + |
|
689 + if (dataSize <= 2) { |
|
690 + return CKYSUCCESS; |
|
691 + } |
|
692 + dataSize -= 2; |
|
693 + /* remove the first tag */ |
|
694 + (void) pivUnwrap(response, &offset, &dataSize, &prsd->tag_1); |
|
695 + if (dataSize == 0) { |
|
696 + return CKYSUCCESS; |
|
697 + } |
|
698 + /* remove the second tag */ |
|
699 + (void) pivUnwrap(response, &offset, &dataSize, &prsd->tag_2); |
|
700 + if (dataSize == 0) { |
|
701 + return CKYSUCCESS; |
|
702 + } |
|
703 + /* the rest is real data */ |
|
704 + return CKYBuffer_AppendData(buf, CKYBuffer_Data(response) + offset, |
|
705 + dataSize); |
|
706 +} |
|
707 + |
|
708 +static CKYStatus |
|
709 +piv_wrapEncodeLength(CKYBuffer *buf, int length, int ber_len) |
|
710 +{ |
|
711 + if (ber_len== 1) { |
|
712 + CKYBuffer_AppendChar(buf,length); |
|
713 + } else { |
|
714 + ber_len--; |
|
715 + CKYBuffer_AppendChar(buf,0x80+ber_len); |
|
716 + while(ber_len--) { |
|
717 + CKYBuffer_AppendChar(buf,(length >> (8*ber_len)) & 0xff); |
|
718 + } |
|
719 + } |
|
720 + return CKYSUCCESS; |
|
721 +} |
|
722 +/* |
|
723 + * do a PIV Sign/Decrypt |
|
724 + */ |
|
725 +CKYStatus |
|
726 +PIVApplet_SignDecrypt(CKYCardConnection *conn, CKYByte key, unsigned int keySize, int derive, |
|
727 + const CKYBuffer *data, CKYBuffer *result, CKYISOStatus *apduRC) |
|
728 +{ |
|
729 + CKYStatus ret; |
|
730 + CKYSize dataSize = CKYBuffer_Size(data); |
|
731 + CKYSize outputSize = keySize; |
|
732 + CKYOffset offset = 0; |
|
733 + CKYBuffer tmp; |
|
734 + CKYByte alg; |
|
735 + int ber_len_1; |
|
736 + int ber_len_2; |
|
737 + int length; |
|
738 + PIVAppletArgSignDecrypt pasd; |
|
739 + PIVAppletRespSignDecrypt prsd; |
|
740 + |
|
741 + /* PIV only defines RSA 1024 and 2048, ECC 256 and ECC 384!!! */ |
|
742 + if (keySize == 128) { /* 1024 bit == 128 bytes */ |
|
743 + ber_len_2 = 2; |
|
744 + ber_len_1 = 2; |
|
745 + alg = 0x6; |
|
746 + } else if (keySize == 256) { /* 2048 bits == 256 bytes */ |
|
747 + ber_len_2 = 3; |
|
748 + ber_len_1 = 3; |
|
749 + alg = 0x7; |
|
750 + } else if (keySize == 32) { /* 256 bits = 32 bytes */ |
|
751 + ber_len_2 = 1; |
|
752 + ber_len_1 = 1; |
|
753 + alg = 0x11; |
|
754 + if (!derive) outputSize = keySize*2; |
|
755 + } else if (keySize == 48) { /* 384 bits = 48 bytes */ |
|
756 + ber_len_2 = 1; |
|
757 + ber_len_1 = 1; |
|
758 + alg = 0x14; |
|
759 + if (!derive) outputSize = keySize*2; |
|
760 + } else { |
|
761 + return CKYINVALIDARGS; |
|
762 + } |
|
763 + |
|
764 + CKYBuffer_InitEmpty(&tmp); |
|
765 + ret = CKYBuffer_Reserve(&tmp, CKY_MAX_WRITE_CHUNK_SIZE); |
|
766 + if (ret != CKYSUCCESS) { |
|
767 + goto done; |
|
768 + } |
|
769 + CKYBuffer_AppendChar(&tmp,0x7c); |
|
770 + piv_wrapEncodeLength(&tmp,dataSize + ber_len_2 + 3,ber_len_1); |
|
771 + CKYBuffer_AppendChar(&tmp,0x82); |
|
772 + CKYBuffer_AppendChar(&tmp,0x0); |
|
773 + CKYBuffer_AppendChar(&tmp, derive ? 0x85 : 0x81); |
|
774 + piv_wrapEncodeLength(&tmp,dataSize,ber_len_2); |
|
775 + |
|
776 + /* now length == header length from here to the end*/ |
|
777 + length = CKYBuffer_Size(&tmp); |
|
778 + |
|
779 + if (length + dataSize > CKY_MAX_WRITE_CHUNK_SIZE) { |
|
780 + CKYBuffer_AppendBuffer(&tmp, data, 0, CKY_MAX_WRITE_CHUNK_SIZE-length); |
|
781 + } else { |
|
782 + CKYBuffer_AppendBuffer(&tmp, data, 0, dataSize); |
|
783 + } |
|
784 + |
|
785 + prsd.tag_1.tag = 0; |
|
786 + prsd.tag_1.length_bytes = -1; |
|
787 + prsd.tag_1.length = 0; |
|
788 + prsd.tag_2.tag = 0; |
|
789 + prsd.tag_2.length_bytes = -1; |
|
790 + prsd.tag_2.length = 0; |
|
791 + prsd.buf = result; |
|
792 + pasd.alg = alg; |
|
793 + pasd.key = key; |
|
794 + pasd.buf = &tmp; |
|
795 + |
|
796 + CKYBuffer_Resize(result,0); |
|
797 + for(offset = -length; (dataSize-offset) > CKY_MAX_WRITE_CHUNK_SIZE; ) { |
|
798 + pasd.chain = 1; |
|
799 + pasd.len = 0; |
|
800 + ret = CKYApplet_HandleAPDU(conn, PIVAppletFactory_SignDecrypt, |
|
801 + &pasd, NULL, CKY_SIZE_UNKNOWN, |
|
802 + pivAppletFill_AppendUnwrapBuffer, |
|
803 + &prsd, apduRC); |
|
804 + if (ret != CKYSUCCESS) { |
|
805 + goto done; |
|
806 + } |
|
807 + CKYBuffer_Resize(&tmp,0); |
|
808 + /* increment before we append the next tmp buffer */ |
|
809 + offset += CKY_MAX_WRITE_CHUNK_SIZE; |
|
810 + CKYBuffer_AppendBuffer(&tmp, data, offset, |
|
811 + MIN(dataSize-offset, CKY_MAX_WRITE_CHUNK_SIZE)); |
|
812 + } |
|
813 + |
|
814 + pasd.chain = 0; |
|
815 + pasd.len = outputSize; |
|
816 + |
|
817 + ret = CKYApplet_HandleAPDU(conn, PIVAppletFactory_SignDecrypt, |
|
818 + &pasd, NULL, CKY_SIZE_UNKNOWN, |
|
819 + pivAppletFill_AppendUnwrapBuffer, |
|
820 + &prsd, apduRC); |
|
821 + |
|
822 + if ((ret == CKYSUCCESS) && (CKYBuffer_Size(result) != outputSize)) { |
|
823 + /* RSA returns the same data size as input, didn't happen, so |
|
824 + * something is wrong. */ |
|
825 + } |
|
826 + |
|
827 +done: |
|
828 + CKYBuffer_FreeData(&tmp); |
|
829 + return ret; |
|
830 +} |
|
831 |
|
832 /* |
|
833 * PIN cluster |
|
834 @@ -1033,6 +1630,44 @@ |
|
835 } while ((size > 0) && (ret == CKYSUCCESS)); |
|
836 |
|
837 return ret; |
|
838 +} |
|
839 + |
|
840 +/* |
|
841 + * Write Object |
|
842 + * This makes multiple APDU calls to write the entire object. |
|
843 + * |
|
844 + */ |
|
845 + |
|
846 +CKYStatus |
|
847 +CKYApplet_WriteObjectFull(CKYCardConnection *conn, unsigned long objectID, |
|
848 + CKYOffset offset, CKYSize size, const CKYBuffer *nonce, |
|
849 + const CKYBuffer *data, CKYISOStatus *apduRC) |
|
850 +{ |
|
851 + |
|
852 + CKYBuffer chunk; |
|
853 + CKYOffset srcOffset = 0; |
|
854 + CKYAppletArgWriteObject wod; |
|
855 + CKYStatus ret = CKYSUCCESS; |
|
856 + |
|
857 + wod.objectID = objectID; |
|
858 + wod.offset = offset; |
|
859 + do { |
|
860 + wod.size = (CKYByte) MIN(size, 220); |
|
861 + ret = CKYBuffer_InitFromBuffer(&chunk, data, |
|
862 + srcOffset, wod.size); |
|
863 + if(ret == CKYSUCCESS) { |
|
864 + wod.data = &chunk; |
|
865 + ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_WriteObject, &wod, |
|
866 + nonce, 0, CKYAppletFill_Null, NULL, apduRC); |
|
867 + size -= wod.size; |
|
868 + wod.offset += wod.size; |
|
869 + srcOffset += wod.size; |
|
870 + CKYBuffer_FreeData(&chunk); |
|
871 + } |
|
872 + |
|
873 + } while ((size > 0) && (ret == CKYSUCCESS)); |
|
874 + |
|
875 + return ret; |
|
876 } |
|
877 |
|
878 /* |