1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2012 Frederik Möllers
4 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
5 // at your option, any later version. See the LICENSE.txt file for the text of
7 //-----------------------------------------------------------------------------
8 // Commands related to the German electronic Identification Card
9 //-----------------------------------------------------------------------------
17 #include <ctype.h> // tolower
18 #include "cliparser.h"
19 #include "cmdparser.h" // command_t
20 #include "commonutil.h" // ARRAYLEN
21 #include "comms.h" // clearCommandBuffer
23 #include "util_posix.h"
25 static int CmdHelp(const char *Cmd
);
27 // Perform (part of) the PACE protocol
28 static int CmdHFEPACollectPACENonces(const char *Cmd
) {
29 CLIParserContext
*ctx
;
30 CLIParserInit(&ctx
, "hf epa cnonces",
31 "Tries to collect nonces when doing part of PACE protocol.",
32 "hf epa cnonces --size 4 --num 4 --delay 1");
36 arg_int1(NULL
, "size", "<dec>", "nonce size"),
37 arg_int1(NULL
, "num", "<dec>", "number of nonces to collect"),
38 arg_int1("d", "delay", "<dec>", "delay between attempts"),
41 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
44 int m
= arg_get_int_def(ctx
, 1, 0);
45 int n
= arg_get_int_def(ctx
, 2, 0);
46 int d
= arg_get_int_def(ctx
, 3, 0);
50 // values are expected to be > 0
54 PrintAndLogEx(SUCCESS
, "Collecting %u %u byte nonces", n
, m
);
55 PrintAndLogEx(SUCCESS
, "Start: %" PRIu64
, msclock() / 1000);
62 for (uint32_t i
= 0; i
< n
; i
++) {
65 PacketResponseNG resp
;
67 SendCommandNG(CMD_HF_EPA_COLLECT_NONCE
, (uint8_t *)&payload
, sizeof(payload
));
69 WaitForResponse(CMD_HF_EPA_COLLECT_NONCE
, &resp
);
71 // check if command failed
72 if (resp
.oldarg
[0] != 0) {
73 PrintAndLogEx(FAILED
, "Error in step %" PRId64
", Return code: %" PRId64
, resp
.oldarg
[0], resp
.oldarg
[1]);
75 size_t nonce_length
= resp
.oldarg
[1];
76 char *nonce
= (char *) calloc(2 * nonce_length
+ 1, sizeof(uint8_t));
77 for (int j
= 0; j
< nonce_length
; j
++) {
78 sprintf(nonce
+ (2 * j
), "%02X", resp
.data
.asBytes
[j
]);
81 PrintAndLogEx(SUCCESS
, "Length: %zu, Nonce: %s", nonce_length
, nonce
);
89 PrintAndLogEx(SUCCESS
, "End: %" PRIu64
, msclock() / 1000);
93 // perform the PACE protocol by replaying APDUs
94 static int CmdHFEPAPACEReplay(const char *Cmd
) {
95 CLIParserContext
*ctx
;
96 CLIParserInit(&ctx
, "hf epa preplay",
97 "Perform PACE protocol by replaying given APDUs",
98 "hf epa preplay --mse 0022C1A4 --get 1068000000 --map 1086000002 --pka 1234ABCDEF --ma 1A2B3C4D");
102 arg_str1(NULL
, "mse", "<hex>", "msesa APDU"),
103 arg_str1(NULL
, "get", "<hex>", "gn APDU"),
104 arg_str1(NULL
, "map", "<hex>", "map APDU"),
105 arg_str1(NULL
, "pka", "<hex>", "pka APDU"),
106 arg_str1(NULL
, "ma", "<hex>", "ma APDU"),
109 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
112 uint8_t msesa_apdu
[41] = {0};
113 CLIGetHexWithReturn(ctx
, 1, msesa_apdu
, &msesa_len
);
116 uint8_t gn_apdu
[8] = {0};
117 CLIGetHexWithReturn(ctx
, 2, gn_apdu
, &gn_len
);
120 uint8_t map_apdu
[75] = {0};
121 CLIGetHexWithReturn(ctx
, 3, map_apdu
, &map_len
);
124 uint8_t pka_apdu
[75] = {0};
125 CLIGetHexWithReturn(ctx
, 4, pka_apdu
, &pka_len
);
128 uint8_t ma_apdu
[18] = {0};
129 CLIGetHexWithReturn(ctx
, 5, ma_apdu
, &ma_len
);
133 uint8_t apdu_lengths
[5] = {msesa_len
, gn_len
, map_len
, pka_len
, ma_len
};
134 // pointers to the arrays to be able to iterate
135 uint8_t *apdus
[] = {msesa_apdu
, gn_apdu
, map_apdu
, pka_apdu
, ma_apdu
};
138 PacketResponseNG resp
;
140 // transfer the APDUs to the Proxmark
141 uint8_t data
[PM3_CMD_DATA_SIZE
];
143 conn
.block_after_ACK
= true;
144 for (int i
= 0; i
< ARRAYLEN(apdu_lengths
); i
++) {
145 // transfer the APDU in several parts if necessary
146 for (int j
= 0; j
* sizeof(data
) < apdu_lengths
[i
]; j
++) {
147 // amount of data in this packet
148 int packet_length
= apdu_lengths
[i
] - (j
* sizeof(data
));
149 if (packet_length
> sizeof(data
)) {
150 packet_length
= sizeof(data
);
152 if ((i
== ARRAYLEN(apdu_lengths
) - 1) && (j
* sizeof(data
) >= apdu_lengths
[i
] - 1)) {
153 // Disable fast mode on last packet
154 conn
.block_after_ACK
= false;
156 memcpy(data
, // + (j * sizeof(data)),
157 apdus
[i
] + (j
* sizeof(data
)),
160 clearCommandBuffer();
162 // arg1: offset into the APDU
163 SendCommandOLD(CMD_HF_EPA_REPLAY
, i
+ 1, j
* sizeof(data
), packet_length
, data
, packet_length
);
164 WaitForResponse(CMD_ACK
, &resp
);
165 if (resp
.oldarg
[0] != 0) {
166 PrintAndLogEx(WARNING
, "Transfer of APDU #%d Part %d failed!", i
, j
);
172 // now perform the replay
173 clearCommandBuffer();
174 SendCommandMIX(CMD_HF_EPA_REPLAY
, 0, 0, 0, NULL
, 0);
175 WaitForResponse(CMD_ACK
, &resp
);
177 if (resp
.oldarg
[0] != 0) {
178 PrintAndLogEx(SUCCESS
, "\nPACE replay failed in step %u!", (uint32_t)resp
.oldarg
[0]);
179 PrintAndLogEx(SUCCESS
, "Measured times:");
180 PrintAndLogEx(SUCCESS
, "MSE Set AT: %u us", resp
.data
.asDwords
[0]);
181 PrintAndLogEx(SUCCESS
, "GA Get Nonce: %u us", resp
.data
.asDwords
[1]);
182 PrintAndLogEx(SUCCESS
, "GA Map Nonce: %u us", resp
.data
.asDwords
[2]);
183 PrintAndLogEx(SUCCESS
, "GA Perform Key Agreement: %u us", resp
.data
.asDwords
[3]);
184 PrintAndLogEx(SUCCESS
, "GA Mutual Authenticate: %u us", resp
.data
.asDwords
[4]);
186 PrintAndLogEx(SUCCESS
, "PACE replay successful!");
187 PrintAndLogEx(SUCCESS
, "MSE Set AT: %u us", resp
.data
.asDwords
[0]);
188 PrintAndLogEx(SUCCESS
, "GA Get Nonce: %u us", resp
.data
.asDwords
[1]);
189 PrintAndLogEx(SUCCESS
, "GA Map Nonce: %u us", resp
.data
.asDwords
[2]);
190 PrintAndLogEx(SUCCESS
, "GA Perform Key Agreement: %u us", resp
.data
.asDwords
[3]);
191 PrintAndLogEx(SUCCESS
, "GA Mutual Authenticate: %u us", resp
.data
.asDwords
[4]);
196 static command_t CommandTable
[] = {
197 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
198 {"cnonces", CmdHFEPACollectPACENonces
, IfPm3Iso14443
, "Acquire encrypted PACE nonces of specific size"},
199 {"preplay", CmdHFEPAPACEReplay
, IfPm3Iso14443
, "Perform PACE protocol by replaying given APDUs"},
200 {NULL
, NULL
, NULL
, NULL
}
203 static int CmdHelp(const char *Cmd
) {
204 (void)Cmd
; // Cmd is not used so far
205 CmdsHelp(CommandTable
);
209 int CmdHFEPA(const char *Cmd
) {
210 clearCommandBuffer();
211 return CmdsParse(CommandTable
, Cmd
);