]> git.zerfleddert.de Git - proxmark3-svn/blob - client/fido/fidocore.c
fido fix (#775)
[proxmark3-svn] / client / fido / fidocore.c
1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2018 Merlok
3 //
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
6 // the license.
7 //-----------------------------------------------------------------------------
8 // FIDO2 authenticators core data and commands
9 // https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html
10 //-----------------------------------------------------------------------------
11 //
12
13 #include "fidocore.h"
14 #include "emv/emvcore.h"
15 #include "emv/emvjson.h"
16 #include <cbor.h>
17 #include "cbortools.h"
18 #include <mbedtls/x509_crt.h>
19 #include <mbedtls/x509.h>
20 #include <mbedtls/pk.h>
21 #include "crypto/asn1utils.h"
22 #include "crypto/libpcrypto.h"
23 #include "fido/additional_ca.h"
24 #include "fido/cose.h"
25 #include "protocols.h"
26
27
28 typedef struct {
29 uint8_t ErrorCode;
30 char *ShortDescription;
31 char *Description;
32 } fido2Error_t;
33
34 fido2Error_t fido2Errors[] = {
35 {0xFF, "n/a", "n/a"},
36 {0x00, "CTAP1_ERR_SUCCESS", "Indicates successful response."},
37 {0x01, "CTAP1_ERR_INVALID_COMMAND", "The command is not a valid CTAP command."},
38 {0x02, "CTAP1_ERR_INVALID_PARAMETER", "The command included an invalid parameter."},
39 {0x03, "CTAP1_ERR_INVALID_LENGTH", "Invalid message or item length."},
40 {0x04, "CTAP1_ERR_INVALID_SEQ", "Invalid message sequencing."},
41 {0x05, "CTAP1_ERR_TIMEOUT", "Message timed out."},
42 {0x06, "CTAP1_ERR_CHANNEL_BUSY", "Channel busy."},
43 {0x0A, "CTAP1_ERR_LOCK_REQUIRED", "Command requires channel lock."},
44 {0x0B, "CTAP1_ERR_INVALID_CHANNEL", "Command not allowed on this cid."},
45 {0x10, "CTAP2_ERR_CBOR_PARSING", "Error while parsing CBOR."},
46 {0x11, "CTAP2_ERR_CBOR_UNEXPECTED_TYPE", "Invalid/unexpected CBOR error."},
47 {0x12, "CTAP2_ERR_INVALID_CBOR", "Error when parsing CBOR."},
48 {0x13, "CTAP2_ERR_INVALID_CBOR_TYPE", "Invalid or unexpected CBOR type."},
49 {0x14, "CTAP2_ERR_MISSING_PARAMETER", "Missing non-optional parameter."},
50 {0x15, "CTAP2_ERR_LIMIT_EXCEEDED", "Limit for number of items exceeded."},
51 {0x16, "CTAP2_ERR_UNSUPPORTED_EXTENSION", "Unsupported extension."},
52 {0x17, "CTAP2_ERR_TOO_MANY_ELEMENTS", "Limit for number of items exceeded."},
53 {0x18, "CTAP2_ERR_EXTENSION_NOT_SUPPORTED", "Unsupported extension."},
54 {0x19, "CTAP2_ERR_CREDENTIAL_EXCLUDED", "Valid credential found in the exludeList."},
55 {0x20, "CTAP2_ERR_CREDENTIAL_NOT_VALID", "Credential not valid for authenticator."},
56 {0x21, "CTAP2_ERR_PROCESSING", "Processing (Lengthy operation is in progress)."},
57 {0x22, "CTAP2_ERR_INVALID_CREDENTIAL", "Credential not valid for the authenticator."},
58 {0x23, "CTAP2_ERR_USER_ACTION_PENDING", "Authentication is waiting for user interaction."},
59 {0x24, "CTAP2_ERR_OPERATION_PENDING", "Processing, lengthy operation is in progress."},
60 {0x25, "CTAP2_ERR_NO_OPERATIONS", "No request is pending."},
61 {0x26, "CTAP2_ERR_UNSUPPORTED_ALGORITHM", "Authenticator does not support requested algorithm."},
62 {0x27, "CTAP2_ERR_OPERATION_DENIED", "Not authorized for requested operation."},
63 {0x28, "CTAP2_ERR_KEY_STORE_FULL", "Internal key storage is full."},
64 {0x29, "CTAP2_ERR_NOT_BUSY", "Authenticator cannot cancel as it is not busy."},
65 {0x2A, "CTAP2_ERR_NO_OPERATION_PENDING", "No outstanding operations."},
66 {0x2B, "CTAP2_ERR_UNSUPPORTED_OPTION", "Unsupported option."},
67 {0x2C, "CTAP2_ERR_INVALID_OPTION", "Unsupported option."},
68 {0x2D, "CTAP2_ERR_KEEPALIVE_CANCEL", "Pending keep alive was cancelled."},
69 {0x2E, "CTAP2_ERR_NO_CREDENTIALS", "No valid credentials provided."},
70 {0x2F, "CTAP2_ERR_USER_ACTION_TIMEOUT", "Timeout waiting for user interaction."},
71 {0x30, "CTAP2_ERR_NOT_ALLOWED", "Continuation command, such as, authenticatorGetNextAssertion not allowed."},
72 {0x31, "CTAP2_ERR_PIN_INVALID", "PIN Blocked."},
73 {0x32, "CTAP2_ERR_PIN_BLOCKED", "PIN Blocked."},
74 {0x33, "CTAP2_ERR_PIN_AUTH_INVALID", "PIN authentication,pinAuth, verification failed."},
75 {0x34, "CTAP2_ERR_PIN_AUTH_BLOCKED", "PIN authentication,pinAuth, blocked. Requires power recycle to reset."},
76 {0x35, "CTAP2_ERR_PIN_NOT_SET", "No PIN has been set."},
77 {0x36, "CTAP2_ERR_PIN_REQUIRED", "PIN is required for the selected operation."},
78 {0x37, "CTAP2_ERR_PIN_POLICY_VIOLATION", "PIN policy violation. Currently only enforces minimum length."},
79 {0x38, "CTAP2_ERR_PIN_TOKEN_EXPIRED", "pinToken expired on authenticator."},
80 {0x39, "CTAP2_ERR_REQUEST_TOO_LARGE", "Authenticator cannot handle this request due to memory constraints."},
81 {0x7F, "CTAP1_ERR_OTHER", "Other unspecified error."},
82 {0xDF, "CTAP2_ERR_SPEC_LAST", "CTAP 2 spec last error."},
83 };
84
85 typedef struct {
86 fido2Commands Command;
87 fido2PacketType PckType;
88 int MemberNumber;
89 char *Description;
90 } fido2Desc_t;
91
92 fido2Desc_t fido2CmdGetInfoRespDesc[] = {
93 {fido2CmdMakeCredential, ptResponse, 0x01, "fmt"},
94 {fido2CmdMakeCredential, ptResponse, 0x02, "authData"},
95 {fido2CmdMakeCredential, ptResponse, 0x03, "attStmt"},
96
97 {fido2CmdMakeCredential, ptQuery, 0x01, "clientDataHash"},
98 {fido2CmdMakeCredential, ptQuery, 0x02, "rp"},
99 {fido2CmdMakeCredential, ptQuery, 0x03, "user"},
100 {fido2CmdMakeCredential, ptQuery, 0x04, "pubKeyCredParams"},
101 {fido2CmdMakeCredential, ptQuery, 0x05, "excludeList"},
102 {fido2CmdMakeCredential, ptQuery, 0x06, "extensions"},
103 {fido2CmdMakeCredential, ptQuery, 0x07, "options"},
104 {fido2CmdMakeCredential, ptQuery, 0x08, "pinAuth"},
105 {fido2CmdMakeCredential, ptQuery, 0x09, "pinProtocol"},
106
107 {fido2CmdGetAssertion, ptResponse, 0x01, "credential"},
108 {fido2CmdGetAssertion, ptResponse, 0x02, "authData"},
109 {fido2CmdGetAssertion, ptResponse, 0x03, "signature"},
110 {fido2CmdGetAssertion, ptResponse, 0x04, "publicKeyCredentialUserEntity"},
111 {fido2CmdGetAssertion, ptResponse, 0x05, "numberOfCredentials"},
112
113 {fido2CmdGetAssertion, ptQuery, 0x01, "rpId"},
114 {fido2CmdGetAssertion, ptQuery, 0x02, "clientDataHash"},
115 {fido2CmdGetAssertion, ptQuery, 0x03, "allowList"},
116 {fido2CmdGetAssertion, ptQuery, 0x04, "extensions"},
117 {fido2CmdGetAssertion, ptQuery, 0x05, "options"},
118 {fido2CmdGetAssertion, ptQuery, 0x06, "pinAuth"},
119 {fido2CmdGetAssertion, ptQuery, 0x07, "pinProtocol"},
120
121 {fido2CmdGetNextAssertion, ptResponse, 0x01, "credential"},
122 {fido2CmdGetNextAssertion, ptResponse, 0x02, "authData"},
123 {fido2CmdGetNextAssertion, ptResponse, 0x03, "signature"},
124 {fido2CmdGetNextAssertion, ptResponse, 0x04, "publicKeyCredentialUserEntity"},
125
126 {fido2CmdGetInfo, ptResponse, 0x01, "versions"},
127 {fido2CmdGetInfo, ptResponse, 0x02, "extensions"},
128 {fido2CmdGetInfo, ptResponse, 0x03, "aaguid"},
129 {fido2CmdGetInfo, ptResponse, 0x04, "options"},
130 {fido2CmdGetInfo, ptResponse, 0x05, "maxMsgSize"},
131 {fido2CmdGetInfo, ptResponse, 0x06, "pinProtocols"},
132
133 {fido2CmdClientPIN, ptResponse, 0x01, "keyAgreement"},
134 {fido2CmdClientPIN, ptResponse, 0x02, "pinToken"},
135 {fido2CmdClientPIN, ptResponse, 0x03, "retries"},
136
137 {fido2CmdClientPIN, ptQuery, 0x01, "pinProtocol"},
138 {fido2CmdClientPIN, ptQuery, 0x02, "subCommand"},
139 {fido2CmdClientPIN, ptQuery, 0x03, "keyAgreement"},
140 {fido2CmdClientPIN, ptQuery, 0x04, "pinAuth"},
141 {fido2CmdClientPIN, ptQuery, 0x05, "newPinEnc"},
142 {fido2CmdClientPIN, ptQuery, 0x06, "pinHashEnc"},
143 {fido2CmdClientPIN, ptQuery, 0x07, "getKeyAgreement"},
144 {fido2CmdClientPIN, ptQuery, 0x08, "getRetries"},
145
146 {fido2COSEKey, ptResponse, 0x01, "kty"},
147 {fido2COSEKey, ptResponse, 0x03, "alg"},
148 {fido2COSEKey, ptResponse, -1, "crv"},
149 {fido2COSEKey, ptResponse, -2, "x - coordinate"},
150 {fido2COSEKey, ptResponse, -3, "y - coordinate"},
151 {fido2COSEKey, ptResponse, -4, "d - private key"},
152 };
153
154 char *fido2GetCmdErrorDescription(uint8_t errorCode) {
155 for (int i = 0; i < sizeof(fido2Errors) / sizeof(fido2Error_t); i++)
156 if (fido2Errors[i].ErrorCode == errorCode)
157 return fido2Errors[i].Description;
158
159 return fido2Errors[0].Description;
160 }
161
162 char *fido2GetCmdMemberDescription(uint8_t cmdCode, bool isResponse, int memberNum) {
163 for (int i = 0; i < sizeof(fido2CmdGetInfoRespDesc) / sizeof(fido2Desc_t); i++)
164 if (fido2CmdGetInfoRespDesc[i].Command == cmdCode &&
165 fido2CmdGetInfoRespDesc[i].PckType == (isResponse ? ptResponse : ptQuery) &&
166 fido2CmdGetInfoRespDesc[i].MemberNumber == memberNum )
167 return fido2CmdGetInfoRespDesc[i].Description;
168
169 return NULL;
170 }
171
172 int FIDOSelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
173 uint8_t data[] = {0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01};
174
175 return EMVSelect(ECC_CONTACTLESS, ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL);
176 }
177
178 int FIDOExchange(uint8_t* apdu, int apdulen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
179 int res = EMVExchange(ECC_CONTACTLESS, true, apdu, apdulen, Result, MaxResultLen, ResultLen, sw, NULL);
180 if (res == 5) // apdu result (sw) not a 0x9000
181 res = 0;
182 // software chaining
183 while (!res && (*sw >> 8) == 0x61) {
184 uint8_t La = *sw & 0xff;
185 uint8_t get_response_APDU[5] = {apdu[0], ISO7816_GET_RESPONSE, 0x00, 0x00, La};
186 size_t oldlen = *ResultLen;
187 res = EMVExchange(ECC_CONTACTLESS, true, get_response_APDU, sizeof(get_response_APDU), &Result[oldlen], MaxResultLen - oldlen, ResultLen, sw, NULL);
188 if (res == 5) // apdu result (sw) not a 0x9000
189 res = 0;
190
191 *ResultLen += oldlen;
192 if (*ResultLen > MaxResultLen)
193 return 100;
194 }
195 return res;
196 }
197
198 int FIDORegister(uint8_t *params, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw)
199 {
200 uint8_t APDU[5 + 64] = {0x00, 0x01, 0x03, 0x00, 64, 0x00};
201 memcpy(APDU + 5, params, 64);
202 return FIDOExchange(APDU, 5 + 64, Result, MaxResultLen, ResultLen, sw);
203 }
204
205 int FIDOAuthentication(uint8_t *params, uint8_t paramslen, uint8_t controlb, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw)
206 {
207 uint8_t APDU[APDU_COMMAND_LEN] = {0x00, 0x02, controlb, 0x00, paramslen, 0x00};
208 memcpy(APDU + 5, params, paramslen);
209 int apdu_len = 5 + paramslen;
210 return FIDOExchange(APDU, apdu_len, Result, MaxResultLen, ResultLen, sw);
211 }
212
213 int FIDO2GetInfo(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw)
214 {
215 uint8_t APDU[6] = {0x80, 0x10, 0x00, 0x00, 0x01, fido2CmdGetInfo};
216 return FIDOExchange(APDU, sizeof(APDU), Result, MaxResultLen, ResultLen, sw);
217 }
218
219 int FIDO2MakeCredential(uint8_t *params, uint8_t paramslen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw)
220 {
221 uint8_t APDU[APDU_COMMAND_LEN] = {0x80, 0x10, 0x00, 0x00, paramslen + 1, fido2CmdMakeCredential, 0x00};
222 memcpy(APDU+6, params, paramslen);
223 int apdu_len = 5 + paramslen + 1;
224 return FIDOExchange(APDU, apdu_len, Result, MaxResultLen, ResultLen, sw);
225 }
226
227 int FIDO2GetAssertion(uint8_t *params, uint8_t paramslen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw)
228 {
229 uint8_t APDU[APDU_COMMAND_LEN] = {0x80, 0x10, 0x00, 0x00, paramslen + 1, fido2CmdGetAssertion, 0x00};
230 memcpy(APDU+6, params, paramslen);
231 int apdu_len = 5 + paramslen + 1;
232 return FIDOExchange(APDU, apdu_len, Result, MaxResultLen, ResultLen, sw);
233 }
234
235 int FIDOCheckDERAndGetKey(uint8_t *der, size_t derLen, bool verbose, uint8_t *publicKey, size_t publicKeyMaxLen) {
236 int res;
237
238 // load CA's
239 mbedtls_x509_crt cacert;
240 mbedtls_x509_crt_init(&cacert);
241 res = mbedtls_x509_crt_parse(&cacert, (const unsigned char *) additional_ca_pem, additional_ca_pem_len);
242 if (res < 0) {
243 PrintAndLog("ERROR: CA parse certificate returned -0x%x - %s", -res, ecdsa_get_error(res));
244 }
245 if (verbose)
246 PrintAndLog("CA load OK. %d skipped", res);
247
248 // load DER certificate from authenticator's data
249 mbedtls_x509_crt cert;
250 mbedtls_x509_crt_init(&cert);
251 res = mbedtls_x509_crt_parse_der(&cert, der, derLen);
252 if (res) {
253 PrintAndLog("ERROR: DER parse returned 0x%x - %s", (res<0)?-res:res, ecdsa_get_error(res));
254 }
255
256 // get certificate info
257 char linfo[300] = {0};
258 if (verbose) {
259 mbedtls_x509_crt_info(linfo, sizeof(linfo), " ", &cert);
260 PrintAndLog("DER certificate info:\n%s", linfo);
261 }
262
263 // verify certificate
264 uint32_t verifyflags = 0;
265 res = mbedtls_x509_crt_verify(&cert, &cacert, NULL, NULL, &verifyflags, NULL, NULL);
266 if (res) {
267 PrintAndLog("ERROR: DER verify returned 0x%x - %s", (res<0)?-res:res, ecdsa_get_error(res));
268 } else {
269 PrintAndLog("Certificate OK.");
270 }
271
272 if (verbose) {
273 memset(linfo, 0x00, sizeof(linfo));
274 mbedtls_x509_crt_verify_info(linfo, sizeof(linfo), " ", verifyflags);
275 PrintAndLog("Verification info:\n%s", linfo);
276 }
277
278 // get public key
279 res = ecdsa_public_key_from_pk(&cert.pk, publicKey, publicKeyMaxLen);
280 if (res) {
281 PrintAndLog("ERROR: getting public key from certificate 0x%x - %s", (res<0)?-res:res, ecdsa_get_error(res));
282 } else {
283 if (verbose)
284 PrintAndLog("Got a public key from certificate:\n%s", sprint_hex_inrow(publicKey, 65));
285 }
286
287 if (verbose)
288 PrintAndLog("------------------DER-------------------");
289
290 mbedtls_x509_crt_free(&cert);
291 mbedtls_x509_crt_free(&cacert);
292
293 return 0;
294 }
295
296 #define fido_check_if(r) if ((r) != CborNoError) {return r;} else
297 #define fido_check(r) if ((r) != CborNoError) return r;
298
299 int FIDO2CreateMakeCredentionalReq(json_t *root, uint8_t *data, size_t maxdatalen, size_t *datalen) {
300 if (datalen)
301 *datalen = 0;
302 if (!root || !data || !maxdatalen)
303 return 1;
304
305 int res;
306 CborEncoder encoder;
307 CborEncoder map;
308
309 cbor_encoder_init(&encoder, data, maxdatalen, 0);
310
311 // create main map
312 res = cbor_encoder_create_map(&encoder, &map, 5);
313 fido_check_if(res) {
314 // clientDataHash
315 res = cbor_encode_uint(&map, 1);
316 fido_check_if(res) {
317 res = CBOREncodeClientDataHash(root, &map);
318 fido_check(res);
319 }
320
321 // rp
322 res = cbor_encode_uint(&map, 2);
323 fido_check_if(res) {
324 res = CBOREncodeElm(root, "RelyingPartyEntity", &map);
325 fido_check(res);
326 }
327
328 // user
329 res = cbor_encode_uint(&map, 3);
330 fido_check_if(res) {
331 res = CBOREncodeElm(root, "UserEntity", &map);
332 fido_check(res);
333 }
334
335 // pubKeyCredParams
336 res = cbor_encode_uint(&map, 4);
337 fido_check_if(res) {
338 res = CBOREncodeElm(root, "pubKeyCredParams", &map);
339 fido_check(res);
340 }
341
342 // options
343 res = cbor_encode_uint(&map, 7);
344 fido_check_if(res) {
345 res = CBOREncodeElm(root, "MakeCredentialOptions", &map);
346 fido_check(res);
347 }
348 }
349 res = cbor_encoder_close_container(&encoder, &map);
350 fido_check(res);
351
352 size_t len = cbor_encoder_get_buffer_size(&encoder, data);
353 if (datalen)
354 *datalen = len;
355
356 return 0;
357 }
358
359 bool CheckrpIdHash(json_t *json, uint8_t *hash) {
360 char hashval[300] = {0};
361 uint8_t hash2[32] = {0};
362
363 JsonLoadStr(json, "$.RelyingPartyEntity.id", hashval);
364 sha256hash((uint8_t *)hashval, strlen(hashval), hash2);
365
366 return !memcmp(hash, hash2, 32);
367 }
368
369 // check ANSI X9.62 format ECDSA signature (on P-256)
370 int FIDO2CheckSignature(json_t *root, uint8_t *publickey, uint8_t *sign, size_t signLen, uint8_t *authData, size_t authDataLen, bool verbose) {
371 int res;
372 uint8_t rval[300] = {0};
373 uint8_t sval[300] = {0};
374 res = ecdsa_asn1_get_signature(sign, signLen, rval, sval);
375 if (!res) {
376 if (verbose) {
377 PrintAndLog(" r: %s", sprint_hex(rval, 32));
378 PrintAndLog(" s: %s", sprint_hex(sval, 32));
379 }
380
381 uint8_t clientDataHash[32] = {0};
382 size_t clientDataHashLen = 0;
383 res = JsonLoadBufAsHex(root, "$.ClientDataHash", clientDataHash, sizeof(clientDataHash), &clientDataHashLen);
384 if (res || clientDataHashLen != 32) {
385 PrintAndLog("ERROR: Can't get clientDataHash from json!");
386 return 2;
387 }
388
389 uint8_t xbuf[4096] = {0};
390 size_t xbuflen = 0;
391 res = FillBuffer(xbuf, sizeof(xbuf), &xbuflen,
392 authData, authDataLen, // rpIdHash[32] + flags[1] + signCount[4]
393 clientDataHash, 32, // Hash of the serialized client data. "$.ClientDataHash" from json
394 NULL, 0);
395 //PrintAndLog("--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
396 res = ecdsa_signature_verify(publickey, xbuf, xbuflen, sign, signLen);
397 if (res) {
398 if (res == -0x4e00) {
399 PrintAndLog("Signature is NOT VALID.");
400 } else {
401 PrintAndLog("Other signature check error: %x %s", (res<0)?-res:res, ecdsa_get_error(res));
402 }
403 return res;
404 } else {
405 PrintAndLog("Signature is OK.");
406 }
407 } else {
408 PrintAndLog("Invalid signature. res=%d.", res);
409 return res;
410 }
411
412 return 0;
413 }
414
415 int FIDO2MakeCredentionalParseRes(json_t *root, uint8_t *data, size_t dataLen, bool verbose, bool verbose2, bool showCBOR, bool showDERTLV) {
416 CborParser parser;
417 CborValue map, mapsmt;
418 int res;
419 char *buf;
420 uint8_t *ubuf;
421 size_t n;
422
423 // fmt
424 res = CborMapGetKeyById(&parser, &map, data, dataLen, 1);
425 if (res)
426 return res;
427
428 res = cbor_value_dup_text_string(&map, &buf, &n, &map);
429 cbor_check(res);
430 PrintAndLog("format: %s", buf);
431 free(buf);
432
433 // authData
434 uint8_t authData[400] = {0};
435 size_t authDataLen = 0;
436 res = CborMapGetKeyById(&parser, &map, data, dataLen, 2);
437 if (res)
438 return res;
439 res = cbor_value_dup_byte_string(&map, &ubuf, &n, &map);
440 cbor_check(res);
441
442 authDataLen = n;
443 memcpy(authData, ubuf, authDataLen);
444
445 if (verbose2) {
446 PrintAndLog("authData[%d]: %s", n, sprint_hex_inrow(authData, authDataLen));
447 } else {
448 PrintAndLog("authData[%d]: %s...", n, sprint_hex(authData, MIN(authDataLen, 16)));
449 }
450
451 PrintAndLog("RP ID Hash: %s", sprint_hex(ubuf, 32));
452
453 // check RP ID Hash
454 if (CheckrpIdHash(root, ubuf)) {
455 PrintAndLog("rpIdHash OK.");
456 } else {
457 PrintAndLog("rpIdHash ERROR!");
458 }
459
460 PrintAndLog("Flags 0x%02x:", ubuf[32]);
461 if (!ubuf[32])
462 PrintAndLog("none");
463 if (ubuf[32] & 0x01)
464 PrintAndLog("up - user presence result");
465 if (ubuf[32] & 0x04)
466 PrintAndLog("uv - user verification (fingerprint scan or a PIN or ...) result");
467 if (ubuf[32] & 0x40)
468 PrintAndLog("at - attested credential data included");
469 if (ubuf[32] & 0x80)
470 PrintAndLog("ed - extension data included");
471
472 uint32_t cntr = (uint32_t)bytes_to_num(&ubuf[33], 4);
473 PrintAndLog("Counter: %d", cntr);
474 JsonSaveInt(root, "$.AppData.Counter", cntr);
475
476 // attestation data
477 PrintAndLog("AAGUID: %s", sprint_hex(&ubuf[37], 16));
478 JsonSaveBufAsHexCompact(root, "$.AppData.AAGUID", &ubuf[37], 16);
479
480 // Credential ID
481 uint8_t cridlen = (uint16_t)bytes_to_num(&ubuf[53], 2);
482 PrintAndLog("Credential id[%d]: %s", cridlen, sprint_hex_inrow(&ubuf[55], cridlen));
483 JsonSaveInt(root, "$.AppData.CredentialIdLen", cridlen);
484 JsonSaveBufAsHexCompact(root, "$.AppData.CredentialId", &ubuf[55], cridlen);
485
486 //Credentional public key (COSE_KEY)
487 uint8_t coseKey[65] = {0};
488 uint16_t cplen = n - 55 - cridlen;
489 if (verbose2) {
490 PrintAndLog("Credentional public key (COSE_KEY)[%d]: %s", cplen, sprint_hex_inrow(&ubuf[55 + cridlen], cplen));
491 } else {
492 PrintAndLog("Credentional public key (COSE_KEY)[%d]: %s...", cplen, sprint_hex(&ubuf[55 + cridlen], MIN(cplen, 16)));
493 }
494 JsonSaveBufAsHexCompact(root, "$.AppData.COSE_KEY", &ubuf[55 + cridlen], cplen);
495
496 if (showCBOR) {
497 PrintAndLog("COSE structure:");
498 PrintAndLog("---------------- CBOR ------------------");
499 TinyCborPrintFIDOPackage(fido2COSEKey, true, &ubuf[55 + cridlen], cplen);
500 PrintAndLog("---------------- CBOR ------------------");
501 }
502
503 res = COSEGetECDSAKey(&ubuf[55 + cridlen], cplen, verbose, coseKey);
504 if (res) {
505 PrintAndLog("ERROR: Can't get COSE_KEY.");
506 } else {
507 PrintAndLog("COSE public key: %s", sprint_hex_inrow(coseKey, sizeof(coseKey)));
508 JsonSaveBufAsHexCompact(root, "$.AppData.COSEPublicKey", coseKey, sizeof(coseKey));
509 }
510
511 free(ubuf);
512
513 // attStmt - we are check only as DER certificate
514 int64_t alg = 0;
515 uint8_t sign[128] = {0};
516 size_t signLen = 0;
517 uint8_t der[4097] = {0};
518 size_t derLen = 0;
519
520 res = CborMapGetKeyById(&parser, &map, data, dataLen, 3);
521 if (res)
522 return res;
523
524 res = cbor_value_enter_container(&map, &mapsmt);
525 cbor_check(res);
526
527 while (!cbor_value_at_end(&mapsmt)) {
528 char key[100] = {0};
529 res = CborGetStringValue(&mapsmt, key, sizeof(key), &n);
530 cbor_check(res);
531 if (!strcmp(key, "alg")) {
532 cbor_value_get_int64(&mapsmt, &alg);
533 PrintAndLog("Alg [%lld] %s", (long long)alg, GetCOSEAlgDescription(alg));
534 res = cbor_value_advance_fixed(&mapsmt);
535 cbor_check(res);
536 }
537
538 if (!strcmp(key, "sig")) {
539 res = CborGetBinStringValue(&mapsmt, sign, sizeof(sign), &signLen);
540 cbor_check(res);
541 if (verbose2) {
542 PrintAndLog("signature [%d]: %s", signLen, sprint_hex_inrow(sign, signLen));
543 } else {
544 PrintAndLog("signature [%d]: %s...", signLen, sprint_hex(sign, MIN(signLen, 16)));
545 }
546 }
547
548 if (!strcmp(key, "x5c")) {
549 res = CborGetArrayBinStringValue(&mapsmt, der, sizeof(der), &derLen);
550 cbor_check(res);
551 if (verbose2) {
552 PrintAndLog("DER certificate[%d]:\n------------------DER-------------------", derLen);
553 dump_buffer_simple((const unsigned char *)der, derLen, NULL);
554 PrintAndLog("\n----------------DER---------------------");
555 } else {
556 PrintAndLog("DER [%d]: %s...", derLen, sprint_hex(der, MIN(derLen, 16)));
557 }
558 JsonSaveBufAsHexCompact(root, "$.AppData.DER", der, derLen);
559 }
560 }
561 res = cbor_value_leave_container(&map, &mapsmt);
562 cbor_check(res);
563
564 uint8_t public_key[65] = {0};
565
566 // print DER certificate in TLV view
567 if (showDERTLV) {
568 PrintAndLog("----------------DER TLV-----------------");
569 asn1_print(der, derLen, " ");
570 PrintAndLog("----------------DER TLV-----------------");
571 }
572 FIDOCheckDERAndGetKey(der, derLen, verbose, public_key, sizeof(public_key));
573 JsonSaveBufAsHexCompact(root, "$.AppData.DERPublicKey", public_key, sizeof(public_key));
574
575 // check ANSI X9.62 format ECDSA signature (on P-256)
576 FIDO2CheckSignature(root, public_key, sign, signLen, authData, authDataLen, verbose);
577
578 return 0;
579 }
580
581 int FIDO2CreateGetAssertionReq(json_t *root, uint8_t *data, size_t maxdatalen, size_t *datalen, bool createAllowList) {
582 if (datalen)
583 *datalen = 0;
584 if (!root || !data || !maxdatalen)
585 return 1;
586
587 int res;
588 CborEncoder encoder;
589 CborEncoder map, array, mapint;
590
591 cbor_encoder_init(&encoder, data, maxdatalen, 0);
592
593 // create main map
594 res = cbor_encoder_create_map(&encoder, &map, createAllowList ? 4 : 3);
595 fido_check_if(res) {
596 // rpId
597 res = cbor_encode_uint(&map, 1);
598 fido_check_if(res) {
599 res = CBOREncodeElm(root, "$.RelyingPartyEntity.id", &map);
600 fido_check(res);
601 }
602
603 // clientDataHash
604 res = cbor_encode_uint(&map, 2);
605 fido_check_if(res) {
606 res = CBOREncodeClientDataHash(root, &map);
607 fido_check(res);
608 }
609
610 // allowList
611 if (createAllowList) {
612 res = cbor_encode_uint(&map, 3);
613 fido_check_if(res) {
614 res = cbor_encoder_create_array(&map, &array, 1);
615 fido_check_if(res) {
616 res = cbor_encoder_create_map(&array, &mapint, 2);
617 fido_check_if(res) {
618 res = cbor_encode_text_stringz(&mapint, "type");
619 fido_check(res);
620
621 res = cbor_encode_text_stringz(&mapint, "public-key");
622 fido_check(res);
623
624 res = cbor_encode_text_stringz(&mapint, "id");
625 fido_check(res);
626
627 res = CBOREncodeElm(root, "$.AppData.CredentialId", &mapint);
628 fido_check(res);
629 }
630 res = cbor_encoder_close_container(&array, &mapint);
631 fido_check(res);
632 }
633 res = cbor_encoder_close_container(&map, &array);
634 fido_check(res);
635 }
636 }
637
638 // options
639 res = cbor_encode_uint(&map, 5);
640 fido_check_if(res) {
641 res = CBOREncodeElm(root, "GetAssertionOptions", &map);
642 fido_check(res);
643 }
644 }
645 res = cbor_encoder_close_container(&encoder, &map);
646 fido_check(res);
647
648 size_t len = cbor_encoder_get_buffer_size(&encoder, data);
649 if (datalen)
650 *datalen = len;
651
652 return 0;
653 }
654
655 int FIDO2GetAssertionParseRes(json_t *root, uint8_t *data, size_t dataLen, bool verbose, bool verbose2, bool showCBOR) {
656 CborParser parser;
657 CborValue map, mapint;
658 int res;
659 uint8_t *ubuf;
660 size_t n;
661
662 // credential
663 res = CborMapGetKeyById(&parser, &map, data, dataLen, 1);
664 if (res)
665 return res;
666
667 res = cbor_value_enter_container(&map, &mapint);
668 cbor_check(res);
669
670 while (!cbor_value_at_end(&mapint)) {
671 char key[100] = {0};
672 res = CborGetStringValue(&mapint, key, sizeof(key), &n);
673 cbor_check(res);
674
675 if (!strcmp(key, "type")) {
676 char ctype[200] = {0};
677 res = CborGetStringValue(&mapint, ctype, sizeof(ctype), &n);
678 cbor_check(res);
679 PrintAndLog("credential type: %s", ctype);
680 }
681
682 if (!strcmp(key, "id")) {
683 uint8_t cid[200] = {0};
684 res = CborGetBinStringValue(&mapint, cid, sizeof(cid), &n);
685 cbor_check(res);
686 PrintAndLog("credential id [%d]: %s", n, sprint_hex(cid, n));
687 }
688 }
689 res = cbor_value_leave_container(&map, &mapint);
690 cbor_check(res);
691
692 // authData
693 uint8_t authData[400] = {0};
694 size_t authDataLen = 0;
695 res = CborMapGetKeyById(&parser, &map, data, dataLen, 2);
696 if (res)
697 return res;
698 res = cbor_value_dup_byte_string(&map, &ubuf, &n, &map);
699 cbor_check(res);
700
701 authDataLen = n;
702 memcpy(authData, ubuf, authDataLen);
703
704 if (verbose2) {
705 PrintAndLog("authData[%d]: %s", n, sprint_hex_inrow(authData, authDataLen));
706 } else {
707 PrintAndLog("authData[%d]: %s...", n, sprint_hex(authData, MIN(authDataLen, 16)));
708 }
709
710 PrintAndLog("RP ID Hash: %s", sprint_hex(ubuf, 32));
711
712 // check RP ID Hash
713 if (CheckrpIdHash(root, ubuf)) {
714 PrintAndLog("rpIdHash OK.");
715 } else {
716 PrintAndLog("rpIdHash ERROR!");
717 }
718
719 PrintAndLog("Flags 0x%02x:", ubuf[32]);
720 if (!ubuf[32])
721 PrintAndLog("none");
722 if (ubuf[32] & 0x01)
723 PrintAndLog("up - user presence result");
724 if (ubuf[32] & 0x04)
725 PrintAndLog("uv - user verification (fingerprint scan or a PIN or ...) result");
726 if (ubuf[32] & 0x40)
727 PrintAndLog("at - attested credential data included");
728 if (ubuf[32] & 0x80)
729 PrintAndLog("ed - extension data included");
730
731 uint32_t cntr = (uint32_t)bytes_to_num(&ubuf[33], 4);
732 PrintAndLog("Counter: %d", cntr);
733 JsonSaveInt(root, "$.AppData.Counter", cntr);
734
735 free(ubuf);
736
737 // publicKeyCredentialUserEntity
738 res = CborMapGetKeyById(&parser, &map, data, dataLen, 4);
739 if (res) {
740 PrintAndLog("UserEntity n/a");
741 } else {
742 res = cbor_value_enter_container(&map, &mapint);
743 cbor_check(res);
744
745 while (!cbor_value_at_end(&mapint)) {
746 char key[100] = {0};
747 res = CborGetStringValue(&mapint, key, sizeof(key), &n);
748 cbor_check(res);
749
750 if (!strcmp(key, "name") || !strcmp(key, "displayName")) {
751 char cname[200] = {0};
752 res = CborGetStringValue(&mapint, cname, sizeof(cname), &n);
753 cbor_check(res);
754 PrintAndLog("UserEntity %s: %s", key, cname);
755 }
756
757 if (!strcmp(key, "id")) {
758 uint8_t cid[200] = {0};
759 res = CborGetBinStringValue(&mapint, cid, sizeof(cid), &n);
760 cbor_check(res);
761 PrintAndLog("UserEntity id [%d]: %s", n, sprint_hex(cid, n));
762
763 // check
764 uint8_t idbuf[100] = {0};
765 size_t idbuflen;
766
767 JsonLoadBufAsHex(root, "$.UserEntity.id", idbuf, sizeof(idbuf), &idbuflen);
768
769 if (idbuflen == n && !memcmp(idbuf, cid, idbuflen)) {
770 PrintAndLog("UserEntity id OK.");
771 } else {
772 PrintAndLog("ERROR: Wrong UserEntity id (from json: %s)", sprint_hex(idbuf, idbuflen));
773 }
774 }
775 }
776 res = cbor_value_leave_container(&map, &mapint);
777 cbor_check(res);
778 }
779
780
781 // signature
782 res = CborMapGetKeyById(&parser, &map, data, dataLen, 3);
783 if (res)
784 return res;
785 res = cbor_value_dup_byte_string(&map, &ubuf, &n, &map);
786 cbor_check(res);
787
788 uint8_t *sign = ubuf;
789 size_t signLen = n;
790
791 cbor_check(res);
792 if (verbose2) {
793 PrintAndLog("signature [%d]: %s", signLen, sprint_hex_inrow(sign, signLen));
794 } else {
795 PrintAndLog("signature [%d]: %s...", signLen, sprint_hex(sign, MIN(signLen, 16)));
796 }
797
798 // get public key from json
799 uint8_t PublicKey[65] = {0};
800 size_t PublicKeyLen = 0;
801 JsonLoadBufAsHex(root, "$.AppData.COSEPublicKey", PublicKey, 65, &PublicKeyLen);
802
803 // check ANSI X9.62 format ECDSA signature (on P-256)
804 FIDO2CheckSignature(root, PublicKey, sign, signLen, authData, authDataLen, verbose);
805
806 free(ubuf);
807
808 // numberOfCredentials
809 res = CborMapGetKeyById(&parser, &map, data, dataLen, 5);
810 if (res) {
811 PrintAndLog("numberOfCredentials: 1 by default");
812 } else {
813 int64_t numberOfCredentials = 0;
814 cbor_value_get_int64(&map, &numberOfCredentials);
815 PrintAndLog("numberOfCredentials: %lld", (long long)numberOfCredentials);
816 }
817
818 return 0;
819 }
Impressum, Datenschutz