1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2018 Merlok
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 // 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 //-----------------------------------------------------------------------------
14 #include "emv/emvcore.h"
15 #include "emv/emvjson.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"
28 char *ShortDescription
;
32 fido2Error_t fido2Errors
[] = {
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."},
84 fido2Commands Command
;
85 fido2PacketType PckType
;
90 fido2Desc_t fido2CmdGetInfoRespDesc
[] = {
91 {fido2CmdMakeCredential
, ptResponse
, 0x01, "fmt"},
92 {fido2CmdMakeCredential
, ptResponse
, 0x02, "authData"},
93 {fido2CmdMakeCredential
, ptResponse
, 0x03, "attStmt"},
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"},
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"},
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"},
119 {fido2CmdGetNextAssertion
, ptResponse
, 0x01, "credential"},
120 {fido2CmdGetNextAssertion
, ptResponse
, 0x02, "authData"},
121 {fido2CmdGetNextAssertion
, ptResponse
, 0x03, "signature"},
122 {fido2CmdGetNextAssertion
, ptResponse
, 0x04, "publicKeyCredentialUserEntity"},
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"},
131 {fido2CmdClientPIN
, ptResponse
, 0x01, "keyAgreement"},
132 {fido2CmdClientPIN
, ptResponse
, 0x02, "pinToken"},
133 {fido2CmdClientPIN
, ptResponse
, 0x03, "retries"},
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"},
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"},
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
;
157 return fido2Errors
[0].Description
;
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
;
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};
173 return EMVSelect(ECC_CONTACTLESS
, ActivateField
, LeaveFieldON
, data
, sizeof(data
), Result
, MaxResultLen
, ResultLen
, sw
, NULL
);
176 int FIDOExchange(sAPDU apdu
, uint8_t *Result
, size_t MaxResultLen
, size_t *ResultLen
, uint16_t *sw
) {
177 int res
= EMVExchange(ECC_CONTACTLESS
, true, apdu
, Result
, MaxResultLen
, ResultLen
, sw
, NULL
);
178 if (res
== 5) // apdu result (sw) not a 0x9000
181 while (!res
&& (*sw
>> 8) == 0x61) {
182 size_t oldlen
= *ResultLen
;
183 res
= EMVExchange(ECC_CONTACTLESS
, 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
187 *ResultLen
+= oldlen
;
188 if (*ResultLen
> MaxResultLen
)
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
);
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
);
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
);
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
);
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
);
221 int FIDOCheckDERAndGetKey(uint8_t *der
, size_t derLen
, bool verbose
, uint8_t *publicKey
, size_t publicKeyMaxLen
) {
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
);
229 PrintAndLog("ERROR: CA parse certificate returned -0x%x - %s", -res
, ecdsa_get_error(res
));
232 PrintAndLog("CA load OK. %d skipped", res
);
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
);
239 PrintAndLog("ERROR: DER parse returned 0x%x - %s", (res
<0)?-res
:res
, ecdsa_get_error(res
));
242 // get certificate info
243 char linfo
[300] = {0};
245 mbedtls_x509_crt_info(linfo
, sizeof(linfo
), " ", &cert
);
246 PrintAndLog("DER certificate info:\n%s", linfo
);
249 // verify certificate
250 uint32_t verifyflags
= 0;
251 res
= mbedtls_x509_crt_verify(&cert
, &cacert
, NULL
, NULL
, &verifyflags
, NULL
, NULL
);
253 PrintAndLog("ERROR: DER verify returned 0x%x - %s", (res
<0)?-res
:res
, ecdsa_get_error(res
));
255 PrintAndLog("Certificate OK.");
259 memset(linfo
, 0x00, sizeof(linfo
));
260 mbedtls_x509_crt_verify_info(linfo
, sizeof(linfo
), " ", verifyflags
);
261 PrintAndLog("Verification info:\n%s", linfo
);
265 res
= ecdsa_public_key_from_pk(&cert
.pk
, publicKey
, publicKeyMaxLen
);
267 PrintAndLog("ERROR: getting public key from certificate 0x%x - %s", (res
<0)?-res
:res
, ecdsa_get_error(res
));
270 PrintAndLog("Got a public key from certificate:\n%s", sprint_hex_inrow(publicKey
, 65));
274 PrintAndLog("------------------DER-------------------");
276 mbedtls_x509_crt_free(&cert
);
277 mbedtls_x509_crt_free(&cacert
);
282 #define fido_check_if(r) if ((r) != CborNoError) {return r;} else
283 #define fido_check(r) if ((r) != CborNoError) return r;
285 int FIDO2CreateMakeCredentionalReq(json_t
*root
, uint8_t *data
, size_t maxdatalen
, size_t *datalen
) {
288 if (!root
|| !data
|| !maxdatalen
)
295 cbor_encoder_init(&encoder
, data
, maxdatalen
, 0);
298 res
= cbor_encoder_create_map(&encoder
, &map
, 5);
301 res
= cbor_encode_uint(&map
, 1);
303 res
= CBOREncodeClientDataHash(root
, &map
);
308 res
= cbor_encode_uint(&map
, 2);
310 res
= CBOREncodeElm(root
, "RelyingPartyEntity", &map
);
315 res
= cbor_encode_uint(&map
, 3);
317 res
= CBOREncodeElm(root
, "UserEntity", &map
);
322 res
= cbor_encode_uint(&map
, 4);
324 res
= CBOREncodeElm(root
, "pubKeyCredParams", &map
);
329 res
= cbor_encode_uint(&map
, 7);
331 res
= CBOREncodeElm(root
, "MakeCredentialOptions", &map
);
335 res
= cbor_encoder_close_container(&encoder
, &map
);
338 size_t len
= cbor_encoder_get_buffer_size(&encoder
, data
);
345 bool CheckrpIdHash(json_t
*json
, uint8_t *hash
) {
346 char hashval
[300] = {0};
347 uint8_t hash2
[32] = {0};
349 JsonLoadStr(json
, "$.RelyingPartyEntity.id", hashval
);
350 sha256hash((uint8_t *)hashval
, strlen(hashval
), hash2
);
352 return !memcmp(hash
, hash2
, 32);
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
) {
358 uint8_t rval
[300] = {0};
359 uint8_t sval
[300] = {0};
360 res
= ecdsa_asn1_get_signature(sign
, signLen
, rval
, sval
);
363 PrintAndLog(" r: %s", sprint_hex(rval
, 32));
364 PrintAndLog(" s: %s", sprint_hex(sval
, 32));
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!");
375 uint8_t xbuf
[4096] = {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
381 //PrintAndLog("--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
382 res
= ecdsa_signature_verify(publickey
, xbuf
, xbuflen
, sign
, signLen
);
384 if (res
== -0x4e00) {
385 PrintAndLog("Signature is NOT VALID.");
387 PrintAndLog("Other signature check error: %x %s", (res
<0)?-res
:res
, ecdsa_get_error(res
));
391 PrintAndLog("Signature is OK.");
394 PrintAndLog("Invalid signature. res=%d.", res
);
401 int FIDO2MakeCredentionalParseRes(json_t
*root
, uint8_t *data
, size_t dataLen
, bool verbose
, bool verbose2
, bool showCBOR
, bool showDERTLV
) {
403 CborValue map
, mapsmt
;
410 res
= CborMapGetKeyById(&parser
, &map
, data
, dataLen
, 1);
414 res
= cbor_value_dup_text_string(&map
, &buf
, &n
, &map
);
416 PrintAndLog("format: %s", buf
);
420 uint8_t authData
[400] = {0};
421 size_t authDataLen
= 0;
422 res
= CborMapGetKeyById(&parser
, &map
, data
, dataLen
, 2);
425 res
= cbor_value_dup_byte_string(&map
, &ubuf
, &n
, &map
);
429 memcpy(authData
, ubuf
, authDataLen
);
432 PrintAndLog("authData[%d]: %s", n
, sprint_hex_inrow(authData
, authDataLen
));
434 PrintAndLog("authData[%d]: %s...", n
, sprint_hex(authData
, MIN(authDataLen
, 16)));
437 PrintAndLog("RP ID Hash: %s", sprint_hex(ubuf
, 32));
440 if (CheckrpIdHash(root
, ubuf
)) {
441 PrintAndLog("rpIdHash OK.");
443 PrintAndLog("rpIdHash ERROR!");
446 PrintAndLog("Flags 0x%02x:", ubuf
[32]);
450 PrintAndLog("up - user presence result");
452 PrintAndLog("uv - user verification (fingerprint scan or a PIN or ...) result");
454 PrintAndLog("at - attested credential data included");
456 PrintAndLog("ed - extension data included");
458 uint32_t cntr
= (uint32_t)bytes_to_num(&ubuf
[33], 4);
459 PrintAndLog("Counter: %d", cntr
);
460 JsonSaveInt(root
, "$.AppData.Counter", cntr
);
463 PrintAndLog("AAGUID: %s", sprint_hex(&ubuf
[37], 16));
464 JsonSaveBufAsHexCompact(root
, "$.AppData.AAGUID", &ubuf
[37], 16);
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
);
472 //Credentional public key (COSE_KEY)
473 uint8_t coseKey
[65] = {0};
474 uint16_t cplen
= n
- 55 - cridlen
;
476 PrintAndLog("Credentional public key (COSE_KEY)[%d]: %s", cplen
, sprint_hex_inrow(&ubuf
[55 + cridlen
], cplen
));
478 PrintAndLog("Credentional public key (COSE_KEY)[%d]: %s...", cplen
, sprint_hex(&ubuf
[55 + cridlen
], MIN(cplen
, 16)));
480 JsonSaveBufAsHexCompact(root
, "$.AppData.COSE_KEY", &ubuf
[55 + cridlen
], cplen
);
483 PrintAndLog("COSE structure:");
484 PrintAndLog("---------------- CBOR ------------------");
485 TinyCborPrintFIDOPackage(fido2COSEKey
, true, &ubuf
[55 + cridlen
], cplen
);
486 PrintAndLog("---------------- CBOR ------------------");
489 res
= COSEGetECDSAKey(&ubuf
[55 + cridlen
], cplen
, verbose
, coseKey
);
491 PrintAndLog("ERROR: Can't get COSE_KEY.");
493 PrintAndLog("COSE public key: %s", sprint_hex_inrow(coseKey
, sizeof(coseKey
)));
494 JsonSaveBufAsHexCompact(root
, "$.AppData.COSEPublicKey", coseKey
, sizeof(coseKey
));
499 // attStmt - we are check only as DER certificate
501 uint8_t sign
[128] = {0};
503 uint8_t der
[4097] = {0};
506 res
= CborMapGetKeyById(&parser
, &map
, data
, dataLen
, 3);
510 res
= cbor_value_enter_container(&map
, &mapsmt
);
513 while (!cbor_value_at_end(&mapsmt
)) {
515 res
= CborGetStringValue(&mapsmt
, key
, sizeof(key
), &n
);
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
);
524 if (!strcmp(key
, "sig")) {
525 res
= CborGetBinStringValue(&mapsmt
, sign
, sizeof(sign
), &signLen
);
528 PrintAndLog("signature [%d]: %s", signLen
, sprint_hex_inrow(sign
, signLen
));
530 PrintAndLog("signature [%d]: %s...", signLen
, sprint_hex(sign
, MIN(signLen
, 16)));
534 if (!strcmp(key
, "x5c")) {
535 res
= CborGetArrayBinStringValue(&mapsmt
, der
, sizeof(der
), &derLen
);
538 PrintAndLog("DER certificate[%d]:\n------------------DER-------------------", derLen
);
539 dump_buffer_simple((const unsigned char *)der
, derLen
, NULL
);
540 PrintAndLog("\n----------------DER---------------------");
542 PrintAndLog("DER [%d]: %s...", derLen
, sprint_hex(der
, MIN(derLen
, 16)));
544 JsonSaveBufAsHexCompact(root
, "$.AppData.DER", der
, derLen
);
547 res
= cbor_value_leave_container(&map
, &mapsmt
);
550 uint8_t public_key
[65] = {0};
552 // print DER certificate in TLV view
554 PrintAndLog("----------------DER TLV-----------------");
555 asn1_print(der
, derLen
, " ");
556 PrintAndLog("----------------DER TLV-----------------");
558 FIDOCheckDERAndGetKey(der
, derLen
, verbose
, public_key
, sizeof(public_key
));
559 JsonSaveBufAsHexCompact(root
, "$.AppData.DERPublicKey", public_key
, sizeof(public_key
));
561 // check ANSI X9.62 format ECDSA signature (on P-256)
562 FIDO2CheckSignature(root
, public_key
, sign
, signLen
, authData
, authDataLen
, verbose
);
567 int FIDO2CreateGetAssertionReq(json_t
*root
, uint8_t *data
, size_t maxdatalen
, size_t *datalen
, bool createAllowList
) {
570 if (!root
|| !data
|| !maxdatalen
)
575 CborEncoder map
, array
, mapint
;
577 cbor_encoder_init(&encoder
, data
, maxdatalen
, 0);
580 res
= cbor_encoder_create_map(&encoder
, &map
, createAllowList
? 4 : 3);
583 res
= cbor_encode_uint(&map
, 1);
585 res
= CBOREncodeElm(root
, "$.RelyingPartyEntity.id", &map
);
590 res
= cbor_encode_uint(&map
, 2);
592 res
= CBOREncodeClientDataHash(root
, &map
);
597 if (createAllowList
) {
598 res
= cbor_encode_uint(&map
, 3);
600 res
= cbor_encoder_create_array(&map
, &array
, 1);
602 res
= cbor_encoder_create_map(&array
, &mapint
, 2);
604 res
= cbor_encode_text_stringz(&mapint
, "type");
607 res
= cbor_encode_text_stringz(&mapint
, "public-key");
610 res
= cbor_encode_text_stringz(&mapint
, "id");
613 res
= CBOREncodeElm(root
, "$.AppData.CredentialId", &mapint
);
616 res
= cbor_encoder_close_container(&array
, &mapint
);
619 res
= cbor_encoder_close_container(&map
, &array
);
625 res
= cbor_encode_uint(&map
, 5);
627 res
= CBOREncodeElm(root
, "GetAssertionOptions", &map
);
631 res
= cbor_encoder_close_container(&encoder
, &map
);
634 size_t len
= cbor_encoder_get_buffer_size(&encoder
, data
);
641 int FIDO2GetAssertionParseRes(json_t
*root
, uint8_t *data
, size_t dataLen
, bool verbose
, bool verbose2
, bool showCBOR
) {
643 CborValue map
, mapint
;
649 res
= CborMapGetKeyById(&parser
, &map
, data
, dataLen
, 1);
653 res
= cbor_value_enter_container(&map
, &mapint
);
656 while (!cbor_value_at_end(&mapint
)) {
658 res
= CborGetStringValue(&mapint
, key
, sizeof(key
), &n
);
661 if (!strcmp(key
, "type")) {
662 char ctype
[200] = {0};
663 res
= CborGetStringValue(&mapint
, ctype
, sizeof(ctype
), &n
);
665 PrintAndLog("credential type: %s", ctype
);
668 if (!strcmp(key
, "id")) {
669 uint8_t cid
[200] = {0};
670 res
= CborGetBinStringValue(&mapint
, cid
, sizeof(cid
), &n
);
672 PrintAndLog("credential id [%d]: %s", n
, sprint_hex(cid
, n
));
675 res
= cbor_value_leave_container(&map
, &mapint
);
679 uint8_t authData
[400] = {0};
680 size_t authDataLen
= 0;
681 res
= CborMapGetKeyById(&parser
, &map
, data
, dataLen
, 2);
684 res
= cbor_value_dup_byte_string(&map
, &ubuf
, &n
, &map
);
688 memcpy(authData
, ubuf
, authDataLen
);
691 PrintAndLog("authData[%d]: %s", n
, sprint_hex_inrow(authData
, authDataLen
));
693 PrintAndLog("authData[%d]: %s...", n
, sprint_hex(authData
, MIN(authDataLen
, 16)));
696 PrintAndLog("RP ID Hash: %s", sprint_hex(ubuf
, 32));
699 if (CheckrpIdHash(root
, ubuf
)) {
700 PrintAndLog("rpIdHash OK.");
702 PrintAndLog("rpIdHash ERROR!");
705 PrintAndLog("Flags 0x%02x:", ubuf
[32]);
709 PrintAndLog("up - user presence result");
711 PrintAndLog("uv - user verification (fingerprint scan or a PIN or ...) result");
713 PrintAndLog("at - attested credential data included");
715 PrintAndLog("ed - extension data included");
717 uint32_t cntr
= (uint32_t)bytes_to_num(&ubuf
[33], 4);
718 PrintAndLog("Counter: %d", cntr
);
719 JsonSaveInt(root
, "$.AppData.Counter", cntr
);
723 // publicKeyCredentialUserEntity
724 res
= CborMapGetKeyById(&parser
, &map
, data
, dataLen
, 4);
726 PrintAndLog("UserEntity n/a");
728 res
= cbor_value_enter_container(&map
, &mapint
);
731 while (!cbor_value_at_end(&mapint
)) {
733 res
= CborGetStringValue(&mapint
, key
, sizeof(key
), &n
);
736 if (!strcmp(key
, "name") || !strcmp(key
, "displayName")) {
737 char cname
[200] = {0};
738 res
= CborGetStringValue(&mapint
, cname
, sizeof(cname
), &n
);
740 PrintAndLog("UserEntity %s: %s", key
, cname
);
743 if (!strcmp(key
, "id")) {
744 uint8_t cid
[200] = {0};
745 res
= CborGetBinStringValue(&mapint
, cid
, sizeof(cid
), &n
);
747 PrintAndLog("UserEntity id [%d]: %s", n
, sprint_hex(cid
, n
));
750 uint8_t idbuf
[100] = {0};
753 JsonLoadBufAsHex(root
, "$.UserEntity.id", idbuf
, sizeof(idbuf
), &idbuflen
);
755 if (idbuflen
== n
&& !memcmp(idbuf
, cid
, idbuflen
)) {
756 PrintAndLog("UserEntity id OK.");
758 PrintAndLog("ERROR: Wrong UserEntity id (from json: %s)", sprint_hex(idbuf
, idbuflen
));
762 res
= cbor_value_leave_container(&map
, &mapint
);
768 res
= CborMapGetKeyById(&parser
, &map
, data
, dataLen
, 3);
771 res
= cbor_value_dup_byte_string(&map
, &ubuf
, &n
, &map
);
774 uint8_t *sign
= ubuf
;
779 PrintAndLog("signature [%d]: %s", signLen
, sprint_hex_inrow(sign
, signLen
));
781 PrintAndLog("signature [%d]: %s...", signLen
, sprint_hex(sign
, MIN(signLen
, 16)));
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
);
789 // check ANSI X9.62 format ECDSA signature (on P-256)
790 FIDO2CheckSignature(root
, PublicKey
, sign
, signLen
, authData
, authDataLen
, verbose
);
794 // numberOfCredentials
795 res
= CborMapGetKeyById(&parser
, &map
, data
, dataLen
, 5);
797 PrintAndLog("numberOfCredentials: 1 by default");
799 int64_t numberOfCredentials
= 0;
800 cbor_value_get_int64(&map
, &numberOfCredentials
);
801 PrintAndLog("numberOfCredentials: %lld", (long long)numberOfCredentials
);