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