1 /*
2 * sm-global-platform.c: Global Platform related procedures
3 *
4 * Copyright (C) 2010 Viktor Tarasov <vtarasov@opentrust.com>
5 * OpenTrust <www.opentrust.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31
32 #include <string.h>
33 #include <assert.h>
34 #include <errno.h>
35 #include <ctype.h>
36 #include <sys/stat.h>
37
38 #include <openssl/des.h>
39 #include <openssl/rand.h>
40
41 #include "libopensc/opensc.h"
42 #include "libopensc/sm.h"
43 #include "libopensc/log.h"
44 #include "libopensc/asn1.h"
45
46 #include "sm-module.h"
47
48 int
sm_gp_decode_card_answer(struct sc_context * ctx,struct sc_remote_data * rdata,unsigned char * out,size_t out_len)49 sm_gp_decode_card_answer(struct sc_context *ctx, struct sc_remote_data *rdata, unsigned char *out, size_t out_len)
50 {
51 LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED);
52 }
53
54
55 int
sm_gp_initialize(struct sc_context * ctx,struct sm_info * sm_info,struct sc_remote_data * rdata)56 sm_gp_initialize(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *rdata)
57 {
58 struct sc_serial_number sn = sm_info->serialnr;
59 struct sm_gp_session *gp_session = &sm_info->session.gp;
60 struct sm_gp_keyset *gp_keyset = &sm_info->session.gp.gp_keyset;
61 struct sc_remote_apdu *new_rapdu = NULL;
62 struct sc_apdu *apdu = NULL;
63 int rv;
64
65 LOG_FUNC_CALLED(ctx);
66 sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP initialize: serial:%s", sc_dump_hex(sn.value, sn.len));
67 sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP initialize: current_df_path %s", sc_print_path(&sm_info->current_path_df));
68 sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP initialize: KMC length %i", gp_keyset->kmc_len);
69
70 if (!rdata || !rdata->alloc)
71 LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
72
73 rv = rdata->alloc(rdata, &new_rapdu);
74 LOG_TEST_RET(ctx, rv, "SM GP decode card answer: cannot allocate remote APDU");
75 apdu = &new_rapdu->apdu;
76
77 rv = RAND_bytes(gp_session->host_challenge, SM_SMALL_CHALLENGE_LEN);
78 if (!rv)
79 LOG_FUNC_RETURN(ctx, SC_ERROR_SM_RAND_FAILED);
80
81 apdu->cse = SC_APDU_CASE_4_SHORT;
82 apdu->cla = 0x80;
83 apdu->ins = 0x50;
84 apdu->p1 = 0x0;
85 apdu->p2 = 0x0;
86 apdu->lc = SM_SMALL_CHALLENGE_LEN;
87 apdu->le = 0x1C;
88 apdu->datalen = SM_SMALL_CHALLENGE_LEN;
89 memcpy(&new_rapdu->sbuf[0], gp_session->host_challenge, SM_SMALL_CHALLENGE_LEN);
90
91 LOG_FUNC_RETURN(ctx, SC_SUCCESS);
92 }
93
94
95 static unsigned char *
sc_gp_get_session_key(struct sc_context * ctx,struct sm_gp_session * gp_session,unsigned char * key)96 sc_gp_get_session_key(struct sc_context *ctx, struct sm_gp_session *gp_session,
97 unsigned char *key)
98 {
99 int out_len;
100 unsigned char *out;
101 unsigned char deriv[16];
102
103 memcpy(deriv, gp_session->card_challenge + 4, 4);
104 memcpy(deriv + 4, gp_session->host_challenge, 4);
105 memcpy(deriv + 8, gp_session->card_challenge, 4);
106 memcpy(deriv + 12, gp_session->host_challenge + 4, 4);
107
108 if (sm_encrypt_des_ecb3(key, deriv, 16, &out, &out_len)) {
109 if (ctx)
110 sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "SM GP get session key: des_ecb3 encryption error");
111 free(out);
112 return NULL;
113 }
114 else if (out==NULL || out_len!=16) {
115 if (ctx)
116 sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "SM GP get session key: des_ecb3 encryption error: out(%p,len:%i)", out, out_len);
117 if (out)
118 free(out);
119 return NULL;
120 }
121
122 return out;
123 }
124
125
126 int
sm_gp_get_cryptogram(unsigned char * session_key,unsigned char * left,unsigned char * right,unsigned char * out,int out_len)127 sm_gp_get_cryptogram(unsigned char *session_key,
128 unsigned char *left, unsigned char *right,
129 unsigned char *out, int out_len)
130 {
131 unsigned char block[24];
132 DES_cblock cksum={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
133
134 if (out_len!=8)
135 return SC_ERROR_INVALID_ARGUMENTS;
136
137 memcpy(block + 0, left, 8);
138 memcpy(block + 8, right, 8);
139 memcpy(block + 16, "\x80\0\0\0\0\0\0\0",8);
140
141 DES_cbc_cksum_3des(block,&cksum, sizeof(block), session_key, &cksum);
142
143 memcpy(out, cksum, 8);
144
145 return 0;
146 }
147
148
149 int
sm_gp_get_mac(unsigned char * key,DES_cblock * icv,unsigned char * in,int in_len,DES_cblock * out)150 sm_gp_get_mac(unsigned char *key, DES_cblock *icv,
151 unsigned char *in, int in_len, DES_cblock *out)
152 {
153 int len;
154 unsigned char *block;
155
156 block = malloc(in_len + 8);
157 if (!block)
158 return SC_ERROR_OUT_OF_MEMORY;
159
160 memcpy(block, in, in_len);
161 memcpy(block + in_len, "\x80\0\0\0\0\0\0\0", 8);
162 len = in_len + 8;
163 len -= (len%8);
164
165 DES_cbc_cksum_3des(block, out, len, key, icv);
166
167 free(block);
168 return 0;
169 }
170
171
172 static int
sm_gp_parse_init_data(struct sc_context * ctx,struct sm_gp_session * gp_session,unsigned char * init_data,size_t init_len)173 sm_gp_parse_init_data(struct sc_context *ctx, struct sm_gp_session *gp_session,
174 unsigned char *init_data, size_t init_len)
175 {
176 struct sm_gp_keyset *gp_keyset = &gp_session->gp_keyset;
177
178 if(init_len != 0x1C)
179 return SC_ERROR_INVALID_DATA;
180
181 gp_keyset->version = *(init_data + 10);
182 gp_keyset->index = *(init_data + 11);
183 memcpy(gp_session->card_challenge, init_data + 12, SM_SMALL_CHALLENGE_LEN);
184
185 return SC_SUCCESS;
186 }
187
188
189 static int
sm_gp_init_session(struct sc_context * ctx,struct sm_gp_session * gp_session,unsigned char * adata,size_t adata_len)190 sm_gp_init_session(struct sc_context *ctx, struct sm_gp_session *gp_session,
191 unsigned char *adata, size_t adata_len)
192 {
193 struct sm_gp_keyset *gp_keyset = &gp_session->gp_keyset;
194 unsigned char cksum[8];
195 int rv;
196
197 LOG_FUNC_CALLED(ctx);
198 if (!adata || adata_len < 8)
199 LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
200
201 sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP init session: auth.data %s", sc_dump_hex(adata, 8));
202
203 gp_session->session_enc = sc_gp_get_session_key(ctx, gp_session, gp_keyset->enc);
204 gp_session->session_mac = sc_gp_get_session_key(ctx, gp_session, gp_keyset->mac);
205 gp_session->session_kek = sc_gp_get_session_key(ctx, gp_session, gp_keyset->kek);
206 if (!gp_session->session_enc || !gp_session->session_mac || !gp_session->session_kek)
207 LOG_TEST_RET(ctx, SC_ERROR_SM_NO_SESSION_KEYS, "SM GP init session: get session keys error");
208 memcpy(gp_session->session_kek, gp_keyset->kek, 16);
209
210 sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP init session: session ENC: %s", sc_dump_hex(gp_session->session_enc, 16));
211 sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP init session: session MAC: %s", sc_dump_hex(gp_session->session_mac, 16));
212 sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP init session: session KEK: %s", sc_dump_hex(gp_session->session_kek, 16));
213
214 memset(cksum, 0, sizeof(cksum));
215 rv = sm_gp_get_cryptogram(gp_session->session_enc, gp_session->host_challenge, gp_session->card_challenge, cksum, sizeof(cksum));
216 LOG_TEST_RET(ctx, rv, "SM GP init session: cannot get cryptogram");
217
218 sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP init session: cryptogram: %s", sc_dump_hex(cksum, 8));
219 if (memcmp(cksum, adata, adata_len))
220 LOG_FUNC_RETURN(ctx, SC_ERROR_SM_AUTHENTICATION_FAILED);
221
222 sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP init session: card authenticated");
223 LOG_FUNC_RETURN(ctx, SC_SUCCESS);
224 }
225
226
227 void
sm_gp_close_session(struct sc_context * ctx,struct sm_gp_session * gp_session)228 sm_gp_close_session(struct sc_context *ctx, struct sm_gp_session *gp_session)
229 {
230 free(gp_session->session_enc);
231 free(gp_session->session_mac);
232 free(gp_session->session_kek);
233 }
234
235
236 int
sm_gp_external_authentication(struct sc_context * ctx,struct sm_info * sm_info,unsigned char * init_data,size_t init_len,struct sc_remote_data * rdata,int (* diversify_keyset)(struct sc_context * ctx,struct sm_info * sm_info,unsigned char * idata,size_t idata_len))237 sm_gp_external_authentication(struct sc_context *ctx, struct sm_info *sm_info,
238 unsigned char *init_data, size_t init_len, struct sc_remote_data *rdata,
239 int (*diversify_keyset)(struct sc_context *ctx,
240 struct sm_info *sm_info,
241 unsigned char *idata, size_t idata_len))
242 {
243 struct sc_remote_apdu *new_rapdu = NULL;
244 struct sc_apdu *apdu = NULL;
245 unsigned char host_cryptogram[8], raw_apdu[SC_MAX_APDU_BUFFER_SIZE];
246 struct sm_gp_session *gp_session = &sm_info->session.gp;
247 DES_cblock mac;
248 int rv, offs = 0;
249
250 LOG_FUNC_CALLED(ctx);
251 if (!sm_info || !init_data || !rdata || !rdata->alloc)
252 LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
253
254 if (init_len != 0x1C)
255 LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "SM GP authentication: invalid auth data length");
256
257 rv = sm_gp_parse_init_data(ctx, gp_session, init_data, init_len);
258 LOG_TEST_RET(ctx, rv, "SM GP authentication: 'INIT DATA' parse error");
259
260 if (diversify_keyset) {
261 rv = (*diversify_keyset)(ctx, sm_info, init_data, init_len);
262 LOG_TEST_RET(ctx, rv, "SM GP authentication: keyset diversification error");
263 }
264
265 rv = sm_gp_init_session(ctx, gp_session, init_data + 20, 8);
266 LOG_TEST_RET(ctx, rv, "SM GP authentication: init session error");
267
268 rv = sm_gp_get_cryptogram(gp_session->session_enc,
269 gp_session->card_challenge, gp_session->host_challenge,
270 host_cryptogram, sizeof(host_cryptogram));
271 LOG_TEST_RET(ctx, rv, "SM GP authentication: get host cryptogram error");
272
273 sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP authentication: host_cryptogram:%s", sc_dump_hex(host_cryptogram, 8));
274
275 rv = rdata->alloc(rdata, &new_rapdu);
276 LOG_TEST_RET(ctx, rv, "SM GP authentication: cannot allocate remote APDU");
277 apdu = &new_rapdu->apdu;
278
279 offs = 0;
280 apdu->cse = SC_APDU_CASE_3_SHORT;
281 apdu->cla = raw_apdu[offs++] = 0x84;
282 apdu->ins = raw_apdu[offs++] = 0x82;
283 apdu->p1 = raw_apdu[offs++] = gp_session->params.level;
284 apdu->p2 = raw_apdu[offs++] = 0;
285 apdu->lc = raw_apdu[offs++] = 0x10;
286 apdu->datalen = 0x10;
287
288 memcpy(raw_apdu + offs, host_cryptogram, 8);
289 offs += 8;
290 rv = sm_gp_get_mac(gp_session->session_mac, &gp_session->mac_icv, raw_apdu, offs, &mac);
291 LOG_TEST_RET(ctx, rv, "SM GP authentication: get MAC error");
292
293 memcpy(new_rapdu->sbuf, host_cryptogram, 8);
294 memcpy(new_rapdu->sbuf + 8, mac, 8);
295 memcpy(gp_session->mac_icv, mac, 8);
296
297 LOG_FUNC_RETURN(ctx, 1);
298 }
299
300
301 static int
sm_gp_encrypt_command_data(struct sc_context * ctx,unsigned char * session_key,const unsigned char * in,size_t in_len,unsigned char ** out,size_t * out_len)302 sm_gp_encrypt_command_data(struct sc_context *ctx, unsigned char *session_key,
303 const unsigned char *in, size_t in_len, unsigned char **out, size_t *out_len)
304 {
305 unsigned char *data = NULL;
306 int rv, len;
307
308 if (!out || !out_len)
309 LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "SM GP encrypt command data error");
310
311 sc_debug(ctx, SC_LOG_DEBUG_SM,
312 "SM GP encrypt command data(len:%"SC_FORMAT_LEN_SIZE_T"u,%p)",
313 in_len, in);
314 if (in==NULL || in_len==0) {
315 *out = NULL;
316 *out_len = 0;
317 LOG_FUNC_RETURN(ctx, SC_SUCCESS);
318 }
319
320 len = in_len + 8;
321 len -= (len%8);
322
323 data = calloc(1, len);
324 if (!data)
325 LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
326
327 *data = in_len;
328 memcpy(data + 1, in, in_len);
329
330 rv = sm_encrypt_des_cbc3(ctx, session_key, data, in_len + 1, out, out_len, 1);
331 free(data);
332 LOG_TEST_RET(ctx, rv, "SM GP encrypt command data: encryption error");
333
334 LOG_FUNC_RETURN(ctx, SC_SUCCESS);
335 }
336
337
338 int
sm_gp_securize_apdu(struct sc_context * ctx,struct sm_info * sm_info,char * init_data,struct sc_apdu * apdu)339 sm_gp_securize_apdu(struct sc_context *ctx, struct sm_info *sm_info,
340 char *init_data, struct sc_apdu *apdu)
341 {
342 unsigned char buff[SC_MAX_APDU_BUFFER_SIZE + 24];
343 unsigned char *apdu_data = NULL;
344 struct sm_gp_session *gp_session = &sm_info->session.gp;
345 unsigned gp_level = sm_info->session.gp.params.level;
346 unsigned gp_index = sm_info->session.gp.params.index;
347 DES_cblock mac;
348 unsigned char *encrypted = NULL;
349 size_t encrypted_len = 0;
350 int rv;
351
352 LOG_FUNC_CALLED(ctx);
353
354 apdu_data = (unsigned char *)apdu->data;
355 sc_debug(ctx, SC_LOG_DEBUG_SM,
356 "SM GP securize APDU(cse:%X,cla:%X,ins:%X,data(len:%"SC_FORMAT_LEN_SIZE_T"u,%p),lc:%"SC_FORMAT_LEN_SIZE_T"u,GP level:%X,GP index:%X",
357 apdu->cse, apdu->cla, apdu->ins, apdu->datalen, apdu->data,
358 apdu->lc, gp_level, gp_index);
359
360 if (gp_level == 0 || (apdu->cla & 0x04))
361 return 0;
362
363 if (gp_level == SM_GP_SECURITY_MAC) {
364 if (apdu->datalen + 8 > SC_MAX_APDU_BUFFER_SIZE)
365 LOG_TEST_RET(ctx, SC_ERROR_WRONG_LENGTH, "SM GP securize APDU: too much data");
366 }
367 else if (gp_level == SM_GP_SECURITY_ENC) {
368 if (!gp_session->session_enc)
369 LOG_TEST_RET(ctx, SC_ERROR_SM_INVALID_SESSION_KEY, "SM GP securize APDU: no ENC session key found");
370
371 if (sm_gp_encrypt_command_data(ctx, gp_session->session_enc, apdu->data, apdu->datalen, &encrypted, &encrypted_len))
372 LOG_TEST_RET(ctx, SC_ERROR_SM_ENCRYPT_FAILED, "SM GP securize APDU: data encryption error");
373
374 if (encrypted_len + 8 > SC_MAX_APDU_BUFFER_SIZE) {
375 rv = SC_ERROR_BUFFER_TOO_SMALL;
376 LOG_TEST_GOTO_ERR(ctx, rv, "SM GP securize APDU: not enough place for encrypted data");
377 }
378
379 sc_debug(ctx, SC_LOG_DEBUG_SM,
380 "SM GP securize APDU: encrypted length %"SC_FORMAT_LEN_SIZE_T"u",
381 encrypted_len);
382 }
383 else {
384 LOG_TEST_RET(ctx, SC_ERROR_SM_INVALID_LEVEL, "SM GP securize APDU: invalid SM level");
385 }
386
387 buff[0] = apdu->cla | 0x04;
388 buff[1] = apdu->ins;
389 buff[2] = apdu->p1;
390 buff[3] = apdu->p2;
391 buff[4] = apdu->lc + 8;
392
393 memcpy(buff + 5, apdu_data, apdu->datalen);
394
395 rv = sm_gp_get_mac(gp_session->session_mac, &gp_session->mac_icv, buff, 5 + apdu->datalen, &mac);
396 LOG_TEST_GOTO_ERR(ctx, rv, "SM GP securize APDU: get MAC error");
397
398 if (gp_level == SM_GP_SECURITY_MAC) {
399 memcpy(apdu_data + apdu->datalen, mac, 8);
400
401 apdu->cla |= 0x04;
402 apdu->datalen += 8;
403 apdu->lc = apdu->datalen;
404
405 if (apdu->cse==SC_APDU_CASE_2_SHORT)
406 apdu->cse = SC_APDU_CASE_4_SHORT;
407 }
408 else if (gp_level == SM_GP_SECURITY_ENC) {
409 memcpy(apdu_data + encrypted_len, mac, 8);
410 if (encrypted_len)
411 memcpy(apdu_data, encrypted, encrypted_len);
412
413 apdu->cla |= 0x04;
414 apdu->datalen = encrypted_len + 8;
415 apdu->lc = encrypted_len + 8;
416
417 if (apdu->cse == SC_APDU_CASE_2_SHORT)
418 apdu->cse = SC_APDU_CASE_4_SHORT;
419
420 if (apdu->cse == SC_APDU_CASE_1)
421 apdu->cse = SC_APDU_CASE_3_SHORT;
422
423 free(encrypted);
424 encrypted = NULL;
425 }
426
427 memcpy(sm_info->session.gp.mac_icv, mac, 8);
428
429 err:
430 free(encrypted);
431 LOG_FUNC_RETURN(ctx, rv);
432 }
433
434
435