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 // An implementation of the Value Added Service protocol
17 //-----------------------------------------------------------------------------
20 #include "cliparser.h"
21 #include "cmdparser.h"
26 #include "iso7816/apduinfo.h"
29 #include "util_posix.h"
30 #include "iso7816/iso7816core.h"
36 #include "crypto/libpcrypto.h"
37 #include "fileutils.h"
38 #include "mbedtls/ecp.h"
39 #include "mbedtls/bignum.h"
40 #include "mbedtls/ecdh.h"
41 #include "mbedtls/ecc_point_compression.h"
42 #include "mbedtls/gcm.h"
44 static const iso14a_polling_frame_t WUPA_FRAME
= {
51 static const iso14a_polling_frame_t ECP_VAS_ONLY_FRAME
= {
52 .frame
= {0x6a, 0x01, 0x00, 0x00, 0x02, 0xe4, 0xd2},
58 uint8_t aid
[] = { 0x4f, 0x53, 0x45, 0x2e, 0x56, 0x41, 0x53, 0x2e, 0x30, 0x31 };
59 uint8_t getVasUrlOnlyP2
= 0x00;
60 uint8_t getVasFullReqP2
= 0x01;
62 static int ParseSelectVASResponse(const uint8_t *response
, size_t resLen
, bool verbose
) {
63 struct tlvdb
*tlvRoot
= tlvdb_parse_multi(response
, resLen
);
65 const struct tlvdb
*versionTlv
= tlvdb_find_full(tlvRoot
, 0x9F21);
66 if (versionTlv
== NULL
) {
68 return PM3_ECARDEXCHANGE
;
70 const struct tlv
*version
= tlvdb_get_tlv(versionTlv
);
71 if (version
->len
!= 2) {
73 return PM3_ECARDEXCHANGE
;
76 PrintAndLogEx(INFO
, "Mobile VAS application version: %d.%d", version
->value
[0], version
->value
[1]);
78 if (version
->value
[0] != 0x01 || version
->value
[1] != 0x00) {
80 return PM3_ECARDEXCHANGE
;
83 const struct tlvdb
*capabilitiesTlv
= tlvdb_find_full(tlvRoot
, 0x9F23);
84 if (capabilitiesTlv
== NULL
) {
86 return PM3_ECARDEXCHANGE
;
88 const struct tlv
*capabilities
= tlvdb_get_tlv(capabilitiesTlv
);
89 if (capabilities
->len
!= 4
90 || capabilities
->value
[0] != 0x00
91 || capabilities
->value
[1] != 0x00
92 || capabilities
->value
[2] != 0x00
93 || (capabilities
->value
[3] & 8) == 0) {
95 return PM3_ECARDEXCHANGE
;
102 static int CreateGetVASDataCommand(const uint8_t *pidHash
, const char *url
, size_t urlLen
, uint8_t *out
, int *outLen
) {
103 if (pidHash
== NULL
&& url
== NULL
) {
104 PrintAndLogEx(FAILED
, "Must provide a Pass Type ID or a URL");
108 if (url
!= NULL
&& urlLen
> 256) {
109 PrintAndLogEx(FAILED
, "URL must be less than 256 characters");
113 uint8_t p2
= pidHash
== NULL
? getVasUrlOnlyP2
: getVasFullReqP2
;
115 size_t reqTlvLen
= 19 + (pidHash
!= NULL
? 35 : 0) + (url
!= NULL
? 3 + urlLen
: 0);
116 uint8_t *reqTlv
= calloc(reqTlvLen
, sizeof(uint8_t));
118 uint8_t version
[] = {0x9F, 0x22, 0x02, 0x01, 0x00};
119 memcpy(reqTlv
, version
, sizeof(version
));
121 uint8_t unknown
[] = {0x9F, 0x28, 0x04, 0x00, 0x00, 0x00, 0x00};
122 memcpy(reqTlv
+ sizeof(version
), unknown
, sizeof(unknown
));
124 uint8_t terminalCapabilities
[] = {0x9F, 0x26, 0x04, 0x00, 0x00, 0x00, 0x02};
125 memcpy(reqTlv
+ sizeof(version
) + sizeof(unknown
), terminalCapabilities
, sizeof(terminalCapabilities
));
127 if (pidHash
!= NULL
) {
128 size_t offset
= sizeof(version
) + sizeof(unknown
) + sizeof(terminalCapabilities
);
129 reqTlv
[offset
] = 0x9F;
130 reqTlv
[offset
+ 1] = 0x25;
131 reqTlv
[offset
+ 2] = 32;
132 memcpy(reqTlv
+ offset
+ 3, pidHash
, 32);
136 size_t offset
= sizeof(version
) + sizeof(unknown
) + sizeof(terminalCapabilities
) + (pidHash
!= NULL
? 35 : 0);
137 reqTlv
[offset
] = 0x9F;
138 reqTlv
[offset
+ 1] = 0x29;
139 reqTlv
[offset
+ 2] = urlLen
;
140 memcpy(reqTlv
+ offset
+ 3, url
, urlLen
);
148 memcpy(out
+ 5, reqTlv
, reqTlvLen
);
149 out
[5 + reqTlvLen
] = 0x00;
151 *outLen
= 6 + reqTlvLen
;
157 static int ParseGetVASDataResponse(const uint8_t *res
, size_t resLen
, uint8_t *cryptogram
, size_t *cryptogramLen
) {
158 struct tlvdb
*tlvRoot
= tlvdb_parse_multi(res
, resLen
);
160 const struct tlvdb
*cryptogramTlvdb
= tlvdb_find_full(tlvRoot
, 0x9F27);
161 if (cryptogramTlvdb
== NULL
) {
163 return PM3_ECARDEXCHANGE
;
165 const struct tlv
*cryptogramTlv
= tlvdb_get_tlv(cryptogramTlvdb
);
167 memcpy(cryptogram
, cryptogramTlv
->value
, cryptogramTlv
->len
);
168 *cryptogramLen
= cryptogramTlv
->len
;
174 static int LoadReaderPrivateKey(const uint8_t *buf
, size_t bufLen
, mbedtls_ecp_keypair
*privKey
) {
175 struct tlvdb
*derRoot
= tlvdb_parse_multi(buf
, bufLen
);
177 const struct tlvdb
*privkeyTlvdb
= tlvdb_find_full(derRoot
, 0x04);
178 if (privkeyTlvdb
== NULL
) {
182 const struct tlv
*privkeyTlv
= tlvdb_get_tlv(privkeyTlvdb
);
184 if (mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1
, privKey
, privkeyTlv
->value
, privkeyTlv
->len
)) {
186 PrintAndLogEx(FAILED
, "Unable to parse private key file. Should be DER encoded ASN1");
190 const struct tlvdb
*pubkeyCoordsTlvdb
= tlvdb_find_full(derRoot
, 0x03);
191 if (pubkeyCoordsTlvdb
== NULL
) {
193 PrintAndLogEx(FAILED
, "Private key file should include public key component");
196 const struct tlv
*pubkeyCoordsTlv
= tlvdb_get_tlv(pubkeyCoordsTlvdb
);
197 if (pubkeyCoordsTlv
->len
!= 66 || pubkeyCoordsTlv
->value
[0] != 0x00 || pubkeyCoordsTlv
->value
[1] != 0x04) {
199 PrintAndLogEx(FAILED
, "Invalid public key data");
203 if (mbedtls_ecp_point_read_binary(&privKey
->grp
, &privKey
->Q
, pubkeyCoordsTlv
->value
+ 1, 65)) {
204 PrintAndLogEx(FAILED
, "Failed to read in public key coordinates");
209 if (mbedtls_ecp_check_pubkey(&privKey
->grp
, &privKey
->Q
)) {
210 PrintAndLogEx(FAILED
, "VAS protocol requires an elliptic key on the P-256 curve");
219 static int GetPrivateKeyHint(mbedtls_ecp_keypair
*privKey
, uint8_t *keyHint
) {
220 uint8_t xcoord
[32] = {0};
221 if (mbedtls_mpi_write_binary(&privKey
->Q
.X
, xcoord
, sizeof(xcoord
))) {
225 uint8_t hash
[32] = {0};
226 sha256hash(xcoord
, 32, hash
);
228 memcpy(keyHint
, hash
, 4);
232 static int LoadMobileEphemeralKey(const uint8_t *xcoordBuf
, mbedtls_ecp_keypair
*pubKey
) {
233 uint8_t compressedEcKey
[33] = {0};
234 compressedEcKey
[0] = 0x02;
235 memcpy(compressedEcKey
+ 1, xcoordBuf
, 32);
237 uint8_t decompressedEcKey
[65] = {0};
238 size_t decompressedEcKeyLen
= 0;
239 if (mbedtls_ecp_decompress(&pubKey
->grp
, compressedEcKey
, sizeof(compressedEcKey
), decompressedEcKey
, &decompressedEcKeyLen
, sizeof(decompressedEcKey
))) {
243 if (mbedtls_ecp_point_read_binary(&pubKey
->grp
, &pubKey
->Q
, decompressedEcKey
, decompressedEcKeyLen
)) {
250 static int internalVasDecrypt(uint8_t *cipherText
, size_t cipherTextLen
, uint8_t *sharedSecret
,
251 uint8_t *ansiSharedInfo
, size_t ansiSharedInfoLen
,
252 const uint8_t *gcmAad
, size_t gcmAadLen
, uint8_t *out
, size_t *outLen
) {
253 uint8_t key
[32] = {0};
254 if (ansi_x963_sha256(sharedSecret
, 32, ansiSharedInfo
, ansiSharedInfoLen
, sizeof(key
), key
)) {
255 PrintAndLogEx(FAILED
, "ANSI X9.63 key derivation failed");
259 uint8_t iv
[16] = {0};
261 mbedtls_gcm_context gcmCtx
;
262 mbedtls_gcm_init(&gcmCtx
);
263 if (mbedtls_gcm_setkey(&gcmCtx
, MBEDTLS_CIPHER_ID_AES
, key
, sizeof(key
) * 8)) {
264 PrintAndLogEx(FAILED
, "Unable to use key in GCM context");
268 if (mbedtls_gcm_auth_decrypt(&gcmCtx
, cipherTextLen
- 16, iv
, sizeof(iv
), gcmAad
, gcmAadLen
, cipherText
+ cipherTextLen
- 16, 16, cipherText
, out
)) {
269 PrintAndLogEx(FAILED
, "Failed to perform GCM decryption");
273 mbedtls_gcm_free(&gcmCtx
);
275 *outLen
= cipherTextLen
- 16;
280 static int DecryptVASCryptogram(uint8_t *pidHash
, uint8_t *cryptogram
, size_t cryptogramLen
, mbedtls_ecp_keypair
*privKey
, uint8_t *out
, size_t *outLen
, uint32_t *timestamp
) {
281 uint8_t keyHint
[4] = {0};
282 if (GetPrivateKeyHint(privKey
, keyHint
) != PM3_SUCCESS
) {
283 PrintAndLogEx(FAILED
, "Unable to generate key hint");
287 if (memcmp(keyHint
, cryptogram
, 4) != 0) {
288 PrintAndLogEx(FAILED
, "Private key does not match cryptogram");
292 mbedtls_ecp_keypair mobilePubKey
;
293 mbedtls_ecp_keypair_init(&mobilePubKey
);
294 mobilePubKey
.grp
= privKey
->grp
;
296 if (LoadMobileEphemeralKey(cryptogram
+ 4, &mobilePubKey
) != PM3_SUCCESS
) {
297 mbedtls_ecp_keypair_free(&mobilePubKey
);
298 PrintAndLogEx(FAILED
, "Unable to parse mobile ephemeral key from cryptogram");
302 mbedtls_mpi sharedSecret
;
303 mbedtls_mpi_init(&sharedSecret
);
305 if (mbedtls_ecdh_compute_shared(&privKey
->grp
, &sharedSecret
, &mobilePubKey
.Q
, &privKey
->d
, NULL
, NULL
)) {
306 mbedtls_mpi_free(&sharedSecret
);
307 mbedtls_ecp_keypair_free(&mobilePubKey
);
308 PrintAndLogEx(FAILED
, "Failed to generate ECDH shared secret");
311 mbedtls_ecp_keypair_free(&mobilePubKey
);
313 uint8_t sharedSecretBytes
[32] = {0};
314 if (mbedtls_mpi_write_binary(&sharedSecret
, sharedSecretBytes
, sizeof(sharedSecretBytes
))) {
315 mbedtls_mpi_free(&sharedSecret
);
316 PrintAndLogEx(FAILED
, "Failed to generate ECDH shared secret");
319 mbedtls_mpi_free(&sharedSecret
);
321 uint8_t string1
[27] = "ApplePay encrypted VAS data";
322 uint8_t string2
[13] = "id-aes256-GCM";
324 uint8_t method1SharedInfo
[73] = {0};
325 method1SharedInfo
[0] = 13;
326 memcpy(method1SharedInfo
+ 1, string2
, sizeof(string2
));
327 memcpy(method1SharedInfo
+ 1 + sizeof(string2
), string1
, sizeof(string1
));
328 memcpy(method1SharedInfo
+ 1 + sizeof(string2
) + sizeof(string1
), pidHash
, 32);
330 uint8_t decryptedData
[68] = {0};
331 size_t decryptedDataLen
= 0;
332 if (internalVasDecrypt(cryptogram
+ 4 + 32, cryptogramLen
- 4 - 32, sharedSecretBytes
, method1SharedInfo
, sizeof(method1SharedInfo
), NULL
, 0, decryptedData
, &decryptedDataLen
)) {
333 if (internalVasDecrypt(cryptogram
+ 4 + 32, cryptogramLen
- 4 - 32, sharedSecretBytes
, string1
, sizeof(string1
), pidHash
, 32, decryptedData
, &decryptedDataLen
)) {
338 memcpy(out
, decryptedData
+ 4, decryptedDataLen
- 4);
339 *outLen
= decryptedDataLen
- 4;
342 for (int i
= 0; i
< 4; ++i
) {
343 *timestamp
= (*timestamp
<< 8) | decryptedData
[i
];
349 static int VASReader(uint8_t *pidHash
, const char *url
, size_t urlLen
, uint8_t *cryptogram
, size_t *cryptogramLen
, bool verbose
) {
350 clearCommandBuffer();
352 iso14a_polling_parameters_t polling_parameters
= {
353 .frames
= { WUPA_FRAME
, ECP_VAS_ONLY_FRAME
},
358 if (SelectCard14443A_4_WithParameters(false, false, NULL
, &polling_parameters
) != PM3_SUCCESS
) {
359 PrintAndLogEx(WARNING
, "No ISO14443-A Card in field");
360 return PM3_ECARDEXCHANGE
;
365 uint8_t selectResponse
[APDU_RES_LEN
] = {0};
366 Iso7816Select(CC_CONTACTLESS
, false, true, aid
, sizeof(aid
), selectResponse
, APDU_RES_LEN
, &resLen
, &status
);
368 if (status
!= 0x9000) {
369 PrintAndLogEx(FAILED
, "Card doesn't support VAS");
370 return PM3_ECARDEXCHANGE
;
373 if (ParseSelectVASResponse(selectResponse
, resLen
, verbose
) != PM3_SUCCESS
) {
374 PrintAndLogEx(FAILED
, "Card doesn't support VAS");
375 return PM3_ECARDEXCHANGE
;
378 uint8_t getVasApdu
[PM3_CMD_DATA_SIZE
];
379 int getVasApduLen
= 0;
381 int s
= CreateGetVASDataCommand(pidHash
, url
, urlLen
, getVasApdu
, &getVasApduLen
);
382 if (s
!= PM3_SUCCESS
) {
386 uint8_t apduRes
[APDU_RES_LEN
] = {0};
389 s
= ExchangeAPDU14a(getVasApdu
, getVasApduLen
, false, false, apduRes
, APDU_RES_LEN
, &apduResLen
);
390 if (s
!= PM3_SUCCESS
) {
391 PrintAndLogEx(FAILED
, "Failed to send APDU");
395 if (apduResLen
== 2 && apduRes
[0] == 0x62 && apduRes
[1] == 0x87) {
396 PrintAndLogEx(WARNING
, "Device returned error on GET VAS DATA. Either doesn't have pass with matching id, or requires user authentication.");
397 return PM3_ECARDEXCHANGE
;
400 if (apduResLen
== 0 || apduRes
[0] != 0x70) {
401 PrintAndLogEx(FAILED
, "Invalid response from peer");
404 return ParseGetVASDataResponse(apduRes
, apduResLen
, cryptogram
, cryptogramLen
);
407 static int CmdVASReader(const char *Cmd
) {
408 CLIParserContext
*ctx
;
409 CLIParserInit(&ctx
, "hf vas reader",
410 "Read and decrypt Value Added Services (VAS) message",
411 "hf vas reader --url https://example.com -> URL Only mode\n"
412 "hf vas reader --pid pass.com.passkit.pksamples.nfcdemo -f vas_privkey.der -@\n"
417 arg_str0(NULL
, "pid", "<str>", "PID, pass type id"),
418 arg_str0("f", "file", "<fn>", "path to terminal private key file"),
419 arg_str0(NULL
, "url", "<str>", "a URL to provide to the mobile device"),
420 arg_lit0("@", NULL
, "continuous mode"),
421 arg_lit0("v", "verbose", "Verbose output"),
424 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
428 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)pid
, 512, &pidlen
);
431 char keyfn
[FILE_PATH_SIZE
] = {0};
432 CLIParamStrToBuf(arg_get_str(ctx
, 2), (uint8_t *)keyfn
, FILE_PATH_SIZE
, &keyfnlen
);
434 if (keyfnlen
== 0 && pidlen
> 0) {
435 PrintAndLogEx(FAILED
, "Must provide path to terminal private key if a pass type id is provided");
442 CLIParamStrToBuf(arg_get_str(ctx
, 3), (uint8_t *)url
, 512, &urllen
);
444 bool continuous
= arg_get_lit(ctx
, 4);
445 bool verbose
= arg_get_lit(ctx
, 5);
449 uint8_t *key_data
= NULL
;
450 size_t key_datalen
= 0;
451 if (loadFile_safe(keyfn
, "", (void **)&key_data
, &key_datalen
) != PM3_SUCCESS
) {
455 mbedtls_ecp_keypair privKey
;
456 mbedtls_ecp_keypair_init(&privKey
);
458 if (LoadReaderPrivateKey(key_data
, key_datalen
, &privKey
) != PM3_SUCCESS
) {
460 mbedtls_ecp_keypair_free(&privKey
);
465 PrintAndLogEx(INFO
, "Requesting pass type id... " _GREEN_("%s"), sprint_ascii((uint8_t *) pid
, pidlen
));
468 PrintAndLogEx(INFO
, "Press " _GREEN_("<Enter>") " to exit");
471 uint8_t pidhash
[32] = {0};
472 sha256hash((uint8_t *) pid
, pidlen
, pidhash
);
476 uint8_t cryptogram
[120] = {0};
477 uint8_t msg
[64] = {0};
478 uint32_t timestamp
= 0;
479 int res
= PM3_SUCCESS
;
482 if (continuous
&& kbd_enter_pressed()) {
486 res
= VASReader((pidlen
> 0) ? pidhash
: NULL
, url
, urllen
, cryptogram
, &clen
, verbose
);
487 if (res
== PM3_SUCCESS
) {
489 res
= DecryptVASCryptogram(pidhash
, cryptogram
, clen
, &privKey
, msg
, &mlen
, ×tamp
);
490 if (res
== PM3_SUCCESS
) {
491 PrintAndLogEx(SUCCESS
, "Timestamp... " _YELLOW_("%d") " (secs since Jan 1, 2001)", timestamp
);
492 PrintAndLogEx(SUCCESS
, "Message..... " _YELLOW_("%s"), sprint_ascii(msg
, mlen
));
493 // extra sleep after successfull read
500 } while (continuous
);
502 mbedtls_ecp_keypair_free(&privKey
);
506 static int CmdVASDecrypt(const char *Cmd
) {
507 CLIParserContext
*ctx
;
508 CLIParserInit(&ctx
, "hf vas decrypt",
509 "Decrypt a previously captured cryptogram",
510 "hf vas decrypt --pid pass.com.passkit.pksamples.nfcdemo -f vas_privkey.der -d c0b77375eae416b79449347f9fe838c05cdb57dc7470b97b93b806cb348771d9bfbe29d58538c7c7d7c3d015fa205b68bfccd726058a62f7f44085ac98dbf877120fd9059f1507b956e0a6d56d0a\n"
515 arg_str0(NULL
, "pid", "<str>", "PID, pass type id"),
516 arg_str0("f", "file", "<fn>", "path to terminal private key file"),
517 arg_str0("d", "data", "<hex>", "cryptogram to decrypt"),
520 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
524 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)pid
, 512, &pidlen
);
527 char keyfn
[FILE_PATH_SIZE
] = {0};
528 CLIParamStrToBuf(arg_get_str(ctx
, 2), (uint8_t *)keyfn
, FILE_PATH_SIZE
, &keyfnlen
);
530 if (keyfnlen
== 0 && pidlen
> 0) {
531 PrintAndLogEx(FAILED
, "Must provide path to terminal private key if a pass type id is provided");
537 uint8_t cryptogram
[120] = {0};
538 CLIGetHexWithReturn(ctx
, 3, cryptogram
, &clen
);
542 uint8_t *key_data
= NULL
;
543 size_t key_datalen
= 0;
544 if (loadFile_safe(keyfn
, "", (void **)&key_data
, &key_datalen
) != PM3_SUCCESS
) {
548 mbedtls_ecp_keypair privKey
;
549 mbedtls_ecp_keypair_init(&privKey
);
551 if (LoadReaderPrivateKey(key_data
, key_datalen
, &privKey
) != PM3_SUCCESS
) {
553 mbedtls_ecp_keypair_free(&privKey
);
558 uint8_t pidhash
[32] = {0};
559 sha256hash((uint8_t *) pid
, pidlen
, pidhash
);
562 uint8_t msg
[64] = {0};
563 uint32_t timestamp
= 0;
565 int res
= DecryptVASCryptogram(pidhash
, cryptogram
, clen
, &privKey
, msg
, &mlen
, ×tamp
);
566 if (res
== PM3_SUCCESS
) {
567 PrintAndLogEx(SUCCESS
, "Timestamp... " _YELLOW_("%d") " (secs since Jan 1, 2001)", timestamp
);
568 PrintAndLogEx(SUCCESS
, "Message..... " _YELLOW_("%s"), sprint_ascii(msg
, mlen
));
571 mbedtls_ecp_keypair_free(&privKey
);
575 static int CmdHelp(const char *Cmd
);
577 static command_t CommandTable
[] = {
578 {"--------", CmdHelp
, AlwaysAvailable
, "----------- " _CYAN_("Value Added Service") " -----------"},
579 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
580 {"--------", CmdHelp
, AlwaysAvailable
, "----------------- " _CYAN_("General") " -----------------"},
581 {"reader", CmdVASReader
, IfPm3Iso14443a
, "Read and decrypt VAS message"},
582 {"decrypt", CmdVASDecrypt
, AlwaysAvailable
, "Decrypt a previously captured VAS cryptogram"},
583 {NULL
, NULL
, NULL
, NULL
}
586 int CmdHFVAS(const char *Cmd
) {
587 clearCommandBuffer();
588 return CmdsParse(CommandTable
, Cmd
);
591 static int CmdHelp(const char *Cmd
) {
592 (void)Cmd
; // Cmd is not used so far
593 CmdsHelp(CommandTable
);