1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
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.
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
27 #include "emv/emvcore.h"
28 #include "emv/emvjson.h"
29 #include "crypto/libpcrypto.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
) {
51 static void bin_ext(uint8_t *dst
, size_t dstlen
, uint8_t *src
, size_t srclen
) {
53 memcpy(dst
, &src
[srclen
- dstlen
], dstlen
);
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
);
61 memcpy(&dst
[dstlen
- srclen
], src
, srclen
);
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
) {
78 dst
= dst
& 0x0000ffffffffffffUL
;
83 static uint64_t computeNLM48(uint64_t x
, uint64_t y
) {
86 for (int i
= 0; i
< 48; i
++) {
87 res
= rotateLeft48(res
);
89 res
= res
^ CIPURSE_POLY
;
97 static void computeNLM(uint8_t *res
, const uint8_t *x
, const uint8_t *y
) {
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;
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
);
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).
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
) {
160 memset(ctx
, 0, sizeof(CipurseContext_t
));
163 void CipurseCSetKey(CipurseContext_t
*ctx
, uint8_t keyId
, uint8_t *key
) {
167 CipurseCClearContext(ctx
);
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
) {
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);
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
) {
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);
234 void CipurseCGenerateMIC(uint8_t *data
, size_t datalen
, uint8_t *mic
) {
236 uint8_t pdata
[datalen
+ CIPURSE_MIC_LENGTH
];
237 memset(pdata
, 0, sizeof(pdata
));
240 memcpy(pdata
, data
, datalen
);
242 if (datalen
% CIPURSE_MIC_LENGTH
)
243 plen
+= CIPURSE_MIC_LENGTH
- datalen
% CIPURSE_MIC_LENGTH
;
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];
257 uint16_t crc2
= CipurseCComputeMICCRC(pdata
, plen
);
260 mic
[1] = crc2
& 0xff;
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 ;
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)
289 memcpy(ctx
->frameKeyNext
, ctx
->frameKey
, CIPURSE_AES_KEY_LENGTH
);
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
);
296 aes_encode(NULL
, hx
, &data
[i
], &dstdata
[i
], CIPURSE_AES_KEY_LENGTH
);
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
];
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
);
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
);
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
];
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
) {
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};
381 memcpy(dstapdu
, srcapdu
, sizeof(sAPDU_t
));
383 if (isCipurseCChannelSecuritySet(ctx
) == false)
386 dstapdu
->CLA
|= 0x04;
387 dstapdu
->data
= dstdatabuf
;
388 dstapdu
->data
[0] = CipurseCGetSMI(ctx
, includeLe
);
390 memcpy(&dstdatabuf
[1], srcapdu
->data
, srcapdu
->Lc
);
392 dstapdu
->data
[dstapdu
->Lc
] = Le
;
395 uint8_t originalLc
= dstapdu
->Lc
;
397 switch (ctx
->RequestSecurity
) {
401 CipurseCAPDUMACEncode(ctx
, dstapdu
, originalLc
, buf
, &buflen
);
402 CipurseCCalcMACPadded(ctx
, buf
, buflen
, NULL
);
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
);
411 dstapdu
->Lc
= srcapdu
->Lc
+ CIPURSE_MIC_LENGTH
;
412 dstapdu
->Lc
+= CIPURSE_AES_BLOCK_LENGTH
- dstapdu
->Lc
% CIPURSE_AES_BLOCK_LENGTH
+ 1; // 1 - SMI
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
);
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};
436 uint8_t micdata
[260] = {0};
437 size_t micdatalen
= 0;
439 if (dstdatalen
!= NULL
)
448 uint16_t xsw
= srcdata
[srcdatalen
] * 0x0100 + srcdata
[srcdatalen
+ 1];
452 if (isCipurseCChannelSecuritySet(ctx
) == false) {
453 memcpy(dstdata
, srcdata
, srcdatalen
);
454 if (dstdatalen
!= NULL
)
455 *dstdatalen
= srcdatalen
;
459 switch (ctx
->ResponseSecurity
) {
463 memcpy(buf
, srcdata
, srcdatalen
);
465 memcpy(&buf
[buflen
], &srcdata
[srcdatalen
], 2);
467 CipurseCCalcMACPadded(ctx
, buf
, buflen
, NULL
);
469 memcpy(dstdata
, srcdata
, srcdatalen
);
470 if (dstdatalen
!= NULL
)
471 *dstdatalen
= srcdatalen
;
474 if (srcdatalen
< CIPURSE_MAC_LENGTH
)
477 buflen
= srcdatalen
- CIPURSE_MAC_LENGTH
;
478 memcpy(buf
, srcdata
, buflen
);
479 memcpy(&buf
[buflen
], &srcdata
[srcdatalen
], 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
;
491 CipurseCChannelDecrypt(ctx
, srcdata
, srcdatalen
, buf
, &buflen
);
492 //PrintAndLogEx(INFO, "data plain[%d]: %s", buflen, sprint_hex(buf, buflen));
495 PrintAndLogEx(ERR
, "APDU can't decode crypto stream");
499 micdatalen
= buflen
- 2 - CIPURSE_MIC_LENGTH
;
500 memcpy(micdata
, buf
, buflen
);
501 memcpy(&micdata
[micdatalen
], &buf
[buflen
- 2], 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;
512 *sw
= micdata
[micdatalen
- 2] * 0x0100 + micdata
[micdatalen
- 1];