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