fix one too small
[RRG-proxmark3.git] / client / src / cipurse / cipursecrypto.c
blobed823d9bbb6cb4a42969ae5a405af9033a11cd4b
1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // CIPURSE crypto primitives
17 //-----------------------------------------------------------------------------
19 #include "cipursecrypto.h"
21 #include "commonutil.h" // ARRAYLEN
22 #include "comms.h" // DropField
23 #include "util_posix.h" // msleep
24 #include <string.h> // memcpy memset
26 #include "cmdhf14a.h"
27 #include "emv/emvcore.h"
28 #include "emv/emvjson.h"
29 #include "crypto/libpcrypto.h"
30 #include "ui.h"
31 #include "util.h"
33 uint8_t AESData0[CIPURSE_AES_KEY_LENGTH] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
34 uint8_t QConstant[CIPURSE_AES_KEY_LENGTH] = {0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74};
36 uint8_t CipurseCSecurityLevelEnc(CipurseChannelSecurityLevel lvl) {
37 switch (lvl) {
38 case CPSNone:
39 return 0x00;
40 case CPSPlain:
41 return 0x00;
42 case CPSMACed:
43 return 0x01;
44 case CPSEncrypted:
45 return 0x02;
46 default:
47 return 0x00;
51 static void bin_ext(uint8_t *dst, size_t dstlen, uint8_t *src, size_t srclen) {
52 if (srclen > dstlen)
53 memcpy(dst, &src[srclen - dstlen], dstlen);
54 else
55 memcpy(dst, src, dstlen);
58 static void bin_pad(uint8_t *dst, size_t dstlen, uint8_t *src, size_t srclen) {
59 memset(dst, 0, dstlen);
60 if (srclen <= dstlen)
61 memcpy(&dst[dstlen - srclen], src, srclen);
62 else
63 memcpy(dst, src, dstlen);
66 static void bin_pad2(uint8_t *dst, size_t dstlen, uint8_t *src, size_t srclen) {
67 memset(dst, 0, dstlen);
68 uint8_t dbl[srclen * 2];
69 memcpy(dbl, src, srclen);
70 memcpy(&dbl[srclen], src, srclen);
71 bin_pad(dst, dstlen, dbl, srclen * 2);
74 static uint64_t rotateLeft48(uint64_t src) {
75 uint64_t dst = src << 1;
76 if (dst & 0x0001000000000000UL) {
77 dst = dst | 1;
78 dst = dst & 0x0000ffffffffffffUL;
80 return dst;
83 static uint64_t computeNLM48(uint64_t x, uint64_t y) {
84 uint64_t res = 0;
86 for (int i = 0; i < 48; i++) {
87 res = rotateLeft48(res);
88 if (res & 1)
89 res = res ^ CIPURSE_POLY;
90 y = rotateLeft48(y);
91 if (y & 1)
92 res = res ^ x;
94 return res;
97 static void computeNLM(uint8_t *res, const uint8_t *x, const uint8_t *y) {
98 uint64_t x64 = 0;
99 uint64_t y64 = 0;
101 for (int i = 0; i < 6; i++) {
102 x64 = (x64 << 8) | x[i];
103 y64 = (y64 << 8) | y[i];
106 uint64_t res64 = computeNLM48(x64, y64);
108 for (int i = 0; i < 6; i++) {
109 res[5 - i] = res64 & 0xff;
110 res64 = res64 >> 8;
114 static void CipurseCGenerateK0AndCp(CipurseContext_t *ctx) {
115 uint8_t temp1[CIPURSE_AES_KEY_LENGTH] = {0};
116 uint8_t temp2[CIPURSE_AES_KEY_LENGTH] = {0};
117 uint8_t kp[CIPURSE_SECURITY_PARAM_N] = {0};
119 // session key derivation function
120 // kP := NLM(EXT(kID), rP)
121 // k0 := AES(key=PAD2(kP) XOR PAD(rT),kID) XOR kID
122 bin_ext(temp1, CIPURSE_SECURITY_PARAM_N, ctx->key, CIPURSE_AES_KEY_LENGTH);
123 computeNLM(kp, ctx->rP, temp1); // param sizes == 6 bytes
124 bin_pad2(temp1, CIPURSE_AES_KEY_LENGTH, kp, CIPURSE_SECURITY_PARAM_N);
125 bin_pad(temp2, CIPURSE_AES_KEY_LENGTH, ctx->rT, CIPURSE_SECURITY_PARAM_N);
126 bin_xor(temp1, temp2, CIPURSE_AES_KEY_LENGTH);
128 // session key K0
129 aes_encode(NULL, temp1, ctx->key, ctx->k0, CIPURSE_AES_KEY_LENGTH);
130 bin_xor(ctx->k0, ctx->key, CIPURSE_AES_KEY_LENGTH);
132 // first frame key k1, function to calculate k1,
133 // k1 := AES(key = RP; k0 XOR RT) XOR (k0 XOR RT)
134 memcpy(temp1, ctx->k0, CIPURSE_AES_KEY_LENGTH);
135 bin_xor(temp1, ctx->RT, CIPURSE_AES_KEY_LENGTH);
136 aes_encode(NULL, ctx->RP, temp1, temp2, CIPURSE_AES_KEY_LENGTH);
137 bin_xor(temp1, temp2, CIPURSE_AES_KEY_LENGTH);
138 memcpy(ctx->frameKey, temp1, CIPURSE_AES_KEY_LENGTH);
140 // function to caluclate cP := AES(key=k0, RP).
141 // terminal response
142 aes_encode(NULL, ctx->k0, ctx->RP, ctx->cP, CIPURSE_AES_KEY_LENGTH);
145 static void CipurseCGenerateCT(uint8_t *k0, uint8_t *RT, uint8_t *CT) {
146 aes_encode(NULL, k0, RT, CT, CIPURSE_AES_KEY_LENGTH);
149 // from: https://github.com/duychuongvn/cipurse-card-core/blob/master/src/main/java/com/github/duychuongvn/cirpusecard/core/security/securemessaging/CipurseSecureMessage.java#L68
150 void CipurseCGetKVV(uint8_t *key, uint8_t *kvv) {
151 uint8_t res[16] = {0};
152 aes_encode(NULL, key, AESData0, res, CIPURSE_AES_KEY_LENGTH);
153 memcpy(kvv, res, CIPURSE_KVV_LENGTH);
156 void CipurseCClearContext(CipurseContext_t *ctx) {
157 if (ctx == NULL)
158 return;
160 memset(ctx, 0, sizeof(CipurseContext_t));
163 void CipurseCSetKey(CipurseContext_t *ctx, uint8_t keyId, uint8_t *key) {
164 if (ctx == NULL)
165 return;
167 CipurseCClearContext(ctx);
169 ctx->keyId = keyId;
170 memcpy(ctx->key, key, member_size(CipurseContext_t, key));
173 void CipurseCChannelSetSecurityLevels(CipurseContext_t *ctx, CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp) {
174 ctx->RequestSecurity = req;
175 ctx->ResponseSecurity = resp;
178 bool isCipurseCChannelSecuritySet(CipurseContext_t *ctx) {
179 return ((ctx->RequestSecurity != CPSNone) && (ctx->ResponseSecurity != CPSNone));
182 void CipurseCSetRandomFromPICC(CipurseContext_t *ctx, uint8_t *random) {
183 if (ctx == NULL)
184 return;
186 memcpy(ctx->RP, random, member_size(CipurseContext_t, RP));
187 memcpy(ctx->rP, random + member_size(CipurseContext_t, RP), member_size(CipurseContext_t, rP));
190 void CipurseCSetRandomHost(CipurseContext_t *ctx) {
191 memset(ctx->RT, 0x10, member_size(CipurseContext_t, RT));
192 memset(ctx->rT, 0x20, member_size(CipurseContext_t, rT));
195 uint8_t CipurseCGetSMI(CipurseContext_t *ctx, bool LePresent) {
196 uint8_t res = LePresent ? 1 : 0;
197 res = res | (CipurseCSecurityLevelEnc(ctx->ResponseSecurity) << 2);
198 res = res | (CipurseCSecurityLevelEnc(ctx->RequestSecurity) << 6);
199 return res;
202 static void CipurseCFillAuthData(CipurseContext_t *ctx, uint8_t *authdata) {
203 memcpy(authdata, ctx->cP, member_size(CipurseContext_t, cP));
204 memcpy(&authdata[member_size(CipurseContext_t, cP)], ctx->RT, member_size(CipurseContext_t, RT));
205 memcpy(&authdata[member_size(CipurseContext_t, cP) + member_size(CipurseContext_t, RT)], ctx->rT, member_size(CipurseContext_t, rT));
208 void CipurseCAuthenticateHost(CipurseContext_t *ctx, uint8_t *authdata) {
209 if (ctx == NULL)
210 return;
212 CipurseCSetRandomHost(ctx);
213 CipurseCGenerateK0AndCp(ctx);
214 CipurseCGenerateCT(ctx->k0, ctx->RT, ctx->CT);
216 if (authdata != NULL)
217 CipurseCFillAuthData(ctx, authdata);
220 bool CipurseCCheckCT(CipurseContext_t *ctx, uint8_t *CT) {
221 return (memcmp(CT, ctx->CT, CIPURSE_AES_KEY_LENGTH) == 0);
224 static uint16_t CipurseCComputeMICCRC(const uint8_t *data, size_t len) {
225 uint16_t initCRC = 0x6363;
226 for (size_t i = 0; i < len; i++) {
227 uint8_t ch = data[i] ^ initCRC;
228 ch = ch ^ ((ch << 4) & 0xff);
229 initCRC = (initCRC >> 8) ^ (ch << 8) ^ (ch << 3) ^ (ch >> 4);
231 return initCRC;
234 void CipurseCGenerateMIC(uint8_t *data, size_t datalen, uint8_t *mic) {
235 size_t plen = 0;
236 uint8_t pdata[datalen + CIPURSE_MIC_LENGTH];
237 memset(pdata, 0, sizeof(pdata));
239 // 0x00 padding
240 memcpy(pdata, data, datalen);
241 plen = datalen;
242 if (datalen % CIPURSE_MIC_LENGTH)
243 plen += CIPURSE_MIC_LENGTH - datalen % CIPURSE_MIC_LENGTH;
245 // crc
246 uint16_t crc1 = CipurseCComputeMICCRC(pdata, plen);
248 for (size_t i = 0; i < datalen; i += 4) {
249 uint8_t tmp1 = pdata[i + 0];
250 uint8_t tmp2 = pdata[i + 1];
251 pdata[i + 0] = pdata[i + 2];
252 pdata[i + 1] = pdata[i + 3];
253 pdata[i + 2] = tmp1;
254 pdata[i + 3] = tmp2;
257 uint16_t crc2 = CipurseCComputeMICCRC(pdata, plen);
258 if (mic != NULL) {
259 mic[0] = crc2 >> 8;
260 mic[1] = crc2 & 0xff;
261 mic[2] = crc1 >> 8;
262 mic[3] = crc1 & 0xff;
266 bool CipurseCCheckMIC(uint8_t *data, size_t datalen, uint8_t *mic) {
267 uint8_t xmic[CIPURSE_MIC_LENGTH] = {0};
269 CipurseCGenerateMIC(data, datalen, xmic);
270 return (memcmp(xmic, mic, CIPURSE_MIC_LENGTH) == 0);
273 /* from: https://github.com/duychuongvn/cipurse-card-core/blob/master/src/main/java/com/github/duychuongvn/cirpusecard/core/security/crypto/CipurseCrypto.java#L521
275 * Encrypt/Decrypt the given data using ciphering mechanism explained the OPST.
276 * Data should be already padded.
278 * hx-1 := ki , hx := AES( key = hx-1 ; q) XOR q, Cx := AES( key = hx ;
279 * Dx ), hx+1 := AES( key = hx ; q ) XOR q, Cx+1 := AES( key = hx+1 ;
280 * Dx+1 ), ... hy := AES( key = hy-1 ; q ) XOR q, Cy := AES( key = hy ;
281 * Dy ), ki+1 := hy
283 void CipurseCEncryptDecrypt(CipurseContext_t *ctx, uint8_t *data, size_t datalen, uint8_t *dstdata, bool isEncrypt) {
284 uint8_t hx[CIPURSE_AES_KEY_LENGTH] = {0};
286 if (datalen == 0 || datalen % CIPURSE_AES_KEY_LENGTH != 0)
287 return;
289 memcpy(ctx->frameKeyNext, ctx->frameKey, CIPURSE_AES_KEY_LENGTH);
290 int i = 0;
291 while (datalen > i) {
292 aes_encode(NULL, QConstant, ctx->frameKeyNext, hx, CIPURSE_AES_KEY_LENGTH);
293 bin_xor(hx, ctx->frameKeyNext, CIPURSE_AES_KEY_LENGTH);
295 if (isEncrypt)
296 aes_encode(NULL, hx, &data[i], &dstdata[i], CIPURSE_AES_KEY_LENGTH);
297 else
298 aes_decode(NULL, hx, &data[i], &dstdata[i], CIPURSE_AES_KEY_LENGTH);
300 memcpy(ctx->frameKeyNext, hx, CIPURSE_AES_KEY_LENGTH);
301 i += CIPURSE_AES_KEY_LENGTH;
303 memcpy(ctx->frameKey, ctx->frameKeyNext, CIPURSE_AES_KEY_LENGTH);
306 void CipurseCChannelEncrypt(CipurseContext_t *ctx, uint8_t *data, size_t datalen, uint8_t *encdata, size_t *encdatalen) {
307 uint8_t pdata[datalen + CIPURSE_AES_KEY_LENGTH];
308 size_t pdatalen = 0;
309 AddISO9797M2Padding(pdata, &pdatalen, data, datalen, CIPURSE_AES_KEY_LENGTH);
311 CipurseCEncryptDecrypt(ctx, pdata, pdatalen, encdata, true);
312 *encdatalen = pdatalen;
315 void CipurseCChannelDecrypt(CipurseContext_t *ctx, uint8_t *data, size_t datalen, uint8_t *plaindata, size_t *plaindatalen) {
316 CipurseCEncryptDecrypt(ctx, data, datalen, plaindata, false);
317 *plaindatalen = FindISO9797M2PaddingDataLen(plaindata, datalen);
320 /* from: https://github.com/duychuongvn/cipurse-card-core/blob/master/src/main/java/com/github/duychuongvn/cirpusecard/core/security/crypto/CipurseCrypto.java#L473
322 * Generate OSPT MAC on the given input data.
323 * Data should be already padded.
325 * Calculation of Mi and ki+1: hx := ki , hx+1 := AES( key = hx ; Dx )
326 * XOR Dx , hx+2 := AES( key = hx+1 ; Dx+1 ) XOR Dx+1, hx+3 := AES( key =
327 * hx+2 ; Dx+2 ) XOR Dx+2, ... hy+1 := AES( key = hy ; Dy ) XOR Dy, ki+1 :=
328 * hy+1 M'i := AES( key = ki ; ki+1 ) XOR ki+1, Mi := m LS bits of M'i = (
329 * (M'i )0, (M'i )1, ..., (M'i )m-1)
331 void CipurseCGenerateMAC(CipurseContext_t *ctx, uint8_t *data, size_t datalen, uint8_t *mac) {
332 uint8_t temp[CIPURSE_AES_KEY_LENGTH] = {0};
334 memcpy(ctx->frameKeyNext, ctx->frameKey, CIPURSE_AES_KEY_LENGTH);
335 int i = 0;
336 while (datalen > i) {
337 aes_encode(NULL, ctx->frameKeyNext, &data[i], temp, CIPURSE_AES_KEY_LENGTH);
338 bin_xor(temp, &data[i], CIPURSE_AES_KEY_LENGTH);
339 memcpy(ctx->frameKeyNext, temp, CIPURSE_AES_KEY_LENGTH);
340 i += CIPURSE_AES_KEY_LENGTH;
343 aes_encode(NULL, ctx->frameKey, ctx->frameKeyNext, temp, CIPURSE_AES_KEY_LENGTH);
344 bin_xor(temp, ctx->frameKeyNext, CIPURSE_AES_KEY_LENGTH);
345 memcpy(ctx->frameKey, ctx->frameKeyNext, CIPURSE_AES_KEY_LENGTH);
346 if (mac != NULL)
347 memcpy(mac, temp, CIPURSE_MAC_LENGTH);
350 void CipurseCCalcMACPadded(CipurseContext_t *ctx, uint8_t *data, size_t datalen, uint8_t *mac) {
351 uint8_t pdata[datalen + CIPURSE_AES_KEY_LENGTH];
352 size_t pdatalen = 0;
353 AddISO9797M2Padding(pdata, &pdatalen, data, datalen, CIPURSE_AES_KEY_LENGTH);
354 CipurseCGenerateMAC(ctx, pdata, pdatalen, mac);
357 bool CipurseCCheckMACPadded(CipurseContext_t *ctx, uint8_t *data, size_t datalen, uint8_t *mac) {
358 uint8_t xmac[CIPURSE_MAC_LENGTH] = {0};
359 CipurseCCalcMACPadded(ctx, data, datalen, xmac);
360 return (memcmp(mac, xmac, CIPURSE_MAC_LENGTH) == 0);
363 static void CipurseCAPDUMACEncode(CipurseContext_t *ctx, sAPDU_t *apdu, uint8_t originalLc, uint8_t *data, size_t *datalen) {
364 data[0] = apdu->CLA;
365 data[1] = apdu->INS;
366 data[2] = apdu->P1;
367 data[3] = apdu->P2;
368 data[4] = apdu->Lc;
369 *datalen = 5 + apdu->Lc;
371 if (ctx->RequestSecurity == CPSMACed || ctx->RequestSecurity == CPSEncrypted)
372 *datalen = 5 + originalLc;
373 memcpy(&data[5], apdu->data, *datalen);
376 void CipurseCAPDUReqEncode(CipurseContext_t *ctx, sAPDU_t *srcapdu, sAPDU_t *dstapdu, uint8_t *dstdatabuf, bool includeLe, uint8_t Le) {
377 uint8_t mac[CIPURSE_MAC_LENGTH] = {0};
378 uint8_t buf[260] = {0};
379 size_t buflen = 0;
381 memcpy(dstapdu, srcapdu, sizeof(sAPDU_t));
383 if (isCipurseCChannelSecuritySet(ctx) == false)
384 return;
386 dstapdu->CLA |= 0x04;
387 dstapdu->data = dstdatabuf;
388 dstapdu->data[0] = CipurseCGetSMI(ctx, includeLe);
389 dstapdu->Lc++;
390 memcpy(&dstdatabuf[1], srcapdu->data, srcapdu->Lc);
391 if (includeLe) {
392 dstapdu->data[dstapdu->Lc] = Le;
393 dstapdu->Lc++;
395 uint8_t originalLc = dstapdu->Lc;
397 switch (ctx->RequestSecurity) {
398 case CPSNone:
399 break;
400 case CPSPlain:
401 CipurseCAPDUMACEncode(ctx, dstapdu, originalLc, buf, &buflen);
402 CipurseCCalcMACPadded(ctx, buf, buflen, NULL);
403 break;
404 case CPSMACed:
405 dstapdu->Lc += CIPURSE_MAC_LENGTH;
406 CipurseCAPDUMACEncode(ctx, dstapdu, originalLc, buf, &buflen);
407 CipurseCCalcMACPadded(ctx, buf, buflen, mac);
408 memcpy(&dstdatabuf[dstapdu->Lc - CIPURSE_MAC_LENGTH], mac, CIPURSE_MAC_LENGTH);
409 break;
410 case CPSEncrypted:
411 dstapdu->Lc = srcapdu->Lc + CIPURSE_MIC_LENGTH;
412 dstapdu->Lc += CIPURSE_AES_BLOCK_LENGTH - dstapdu->Lc % CIPURSE_AES_BLOCK_LENGTH + 1; // 1 - SMI
413 if (includeLe)
414 dstapdu->Lc++;
416 CipurseCAPDUMACEncode(ctx, dstapdu, originalLc, buf, &buflen);
417 CipurseCGenerateMIC(buf, buflen, mac);
418 buf[0] = dstapdu->CLA;
419 buf[1] = dstapdu->INS;
420 buf[2] = dstapdu->P1;
421 buf[3] = dstapdu->P2;
422 memcpy(&buf[4], srcapdu->data, srcapdu->Lc);
423 memcpy(&buf[4 + srcapdu->Lc], mac, CIPURSE_MIC_LENGTH);
424 //PrintAndLogEx(INFO, "data plain[%d]: %s", 4 + srcapdu->Lc + CIPURSE_MIC_LENGTH, sprint_hex(buf, 4 + srcapdu->Lc + CIPURSE_MIC_LENGTH));
425 CipurseCChannelEncrypt(ctx, buf, 4 + srcapdu->Lc + CIPURSE_MIC_LENGTH, &dstdatabuf[1], &buflen);
426 break;
427 default:
428 break;
433 void CipurseCAPDURespDecode(CipurseContext_t *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen, uint16_t *sw) {
434 uint8_t buf[260] = {0};
435 size_t buflen = 0;
436 uint8_t micdata[260] = {0};
437 size_t micdatalen = 0;
439 if (dstdatalen != NULL)
440 *dstdatalen = 0;
441 if (sw != NULL)
442 *sw = 0;
444 if (srcdatalen < 2)
445 return;
447 srcdatalen -= 2;
448 uint16_t xsw = srcdata[srcdatalen] * 0x0100 + srcdata[srcdatalen + 1];
449 if (sw)
450 *sw = xsw;
452 if (isCipurseCChannelSecuritySet(ctx) == false) {
453 memcpy(dstdata, srcdata, srcdatalen);
454 if (dstdatalen != NULL)
455 *dstdatalen = srcdatalen;
456 return;
459 switch (ctx->ResponseSecurity) {
460 case CPSNone:
461 break;
462 case CPSPlain:
463 memcpy(buf, srcdata, srcdatalen);
464 buflen = srcdatalen;
465 memcpy(&buf[buflen], &srcdata[srcdatalen], 2);
466 buflen += 2;
467 CipurseCCalcMACPadded(ctx, buf, buflen, NULL);
469 memcpy(dstdata, srcdata, srcdatalen);
470 if (dstdatalen != NULL)
471 *dstdatalen = srcdatalen;
472 break;
473 case CPSMACed:
474 if (srcdatalen < CIPURSE_MAC_LENGTH)
475 return;
477 buflen = srcdatalen - CIPURSE_MAC_LENGTH;
478 memcpy(buf, srcdata, buflen);
479 memcpy(&buf[buflen], &srcdata[srcdatalen], 2);
480 buflen += 2;
482 srcdatalen -= CIPURSE_MAC_LENGTH;
483 if (CipurseCCheckMACPadded(ctx, buf, buflen, &srcdata[srcdatalen]) == false) {
484 PrintAndLogEx(WARNING, "APDU MAC is not valid!");
486 memcpy(dstdata, srcdata, srcdatalen);
487 if (dstdatalen != NULL)
488 *dstdatalen = srcdatalen;
489 break;
490 case CPSEncrypted:
491 CipurseCChannelDecrypt(ctx, srcdata, srcdatalen, buf, &buflen);
492 //PrintAndLogEx(INFO, "data plain[%d]: %s", buflen, sprint_hex(buf, buflen));
494 if (buflen == 0) {
495 PrintAndLogEx(ERR, "APDU can't decode crypto stream");
496 break;
499 micdatalen = buflen - 2 - CIPURSE_MIC_LENGTH;
500 memcpy(micdata, buf, buflen);
501 memcpy(&micdata[micdatalen], &buf[buflen - 2], 2);
502 micdatalen += 2;
504 if (CipurseCCheckMIC(micdata, micdatalen, &buf[micdatalen - 2]) == false) {
505 PrintAndLogEx(ERR, "APDU response MIC is not valid!");
508 memcpy(dstdata, buf, micdatalen - 2);
509 if (dstdatalen != NULL)
510 *dstdatalen = micdatalen - 2;
511 if (sw)
512 *sw = micdata[micdatalen - 2] * 0x0100 + micdata[micdatalen - 1];
513 break;
514 default:
515 break;