From ee92b8a0977fc9be80fb273b383032aaa1e708af Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Tue, 27 Nov 2018 12:24:55 +0200 Subject: [PATCH] refactoring make credentials and add some sketch to get assertion --- client/cmdhffido.c | 220 +--------------------------------- client/fido/fido2.json | 6 +- client/fido/fidocore.c | 260 +++++++++++++++++++++++++++++++++++++++++ client/fido/fidocore.h | 3 + 4 files changed, 272 insertions(+), 217 deletions(-) diff --git a/client/cmdhffido.c b/client/cmdhffido.c index e2d53560d..db427236c 100644 --- a/client/cmdhffido.c +++ b/client/cmdhffido.c @@ -648,218 +648,6 @@ int GetExistsFileNameJson(char *prefixDir, char *reqestedFileName, char *fileNam return 0; } -bool CheckrpIdHash(json_t *json, uint8_t *hash) { - char hashval[300] = {0}; - uint8_t hash2[32] = {0}; - - JsonLoadStr(json, "$.RelyingPartyEntity.id", hashval); - sha256hash((uint8_t *)hashval, strlen(hashval), hash2); - - return !memcmp(hash, hash2, 32); -} - -int MakeCredentionalParseRes(json_t *root, uint8_t *data, size_t dataLen, bool verbose, bool verbose2, bool showCBOR, bool showDERTLV) { - CborParser parser; - CborValue map, mapsmt; - int res; - char *buf; - uint8_t *ubuf; - size_t n; - - // fmt - res = CborMapGetKeyById(&parser, &map, data, dataLen, 1); - if (res) - return res; - - res = cbor_value_dup_text_string(&map, &buf, &n, &map); - cbor_check(res); - PrintAndLog("format: %s", buf); - free(buf); - - // authData - uint8_t authData[400] = {0}; - size_t authDataLen = 0; - res = CborMapGetKeyById(&parser, &map, data, dataLen, 2); - if (res) - return res; - res = cbor_value_dup_byte_string(&map, &ubuf, &n, &map); - cbor_check(res); - - authDataLen = n; - memcpy(authData, ubuf, authDataLen); - - if (verbose2) { - PrintAndLog("authData[%d]: %s", n, sprint_hex_inrow(authData, authDataLen)); - } else { - PrintAndLog("authData[%d]: %s...", n, sprint_hex(authData, MIN(authDataLen, 16))); - } - - PrintAndLog("RP ID Hash: %s", sprint_hex(ubuf, 32)); - - // check RP ID Hash - if (CheckrpIdHash(root, ubuf)) { - PrintAndLog("rpIdHash OK."); - } else { - PrintAndLog("rpIdHash ERROR!"); - } - - PrintAndLog("Flags 0x%02x:", ubuf[32]); - if (!ubuf[32]) - PrintAndLog("none"); - if (ubuf[32] & 0x01) - PrintAndLog("up - user presence result"); - if (ubuf[32] & 0x04) - PrintAndLog("uv - user verification (fingerprint scan or a PIN or ...) result"); - if (ubuf[32] & 0x40) - PrintAndLog("at - attested credential data included"); - if (ubuf[32] & 0x80) - PrintAndLog("ed - extension data included"); - - uint32_t cntr = (uint32_t)bytes_to_num(&ubuf[33], 4); - PrintAndLog("Counter: %d", cntr); - JsonSaveInt(root, "$.AppData.Counter", cntr); - - // attestation data - PrintAndLog("AAGUID: %s", sprint_hex(&ubuf[37], 16)); - JsonSaveBufAsHexCompact(root, "$.AppData.AAGUID", &ubuf[37], 16); - - // Credential ID - uint8_t cridlen = (uint16_t)bytes_to_num(&ubuf[53], 2); - PrintAndLog("Credential id[%d]: %s", cridlen, sprint_hex_inrow(&ubuf[55], cridlen)); - JsonSaveInt(root, "$.AppData.CredentialIdLen", cridlen); - JsonSaveBufAsHexCompact(root, "$.AppData.CredentialId", &ubuf[55], cridlen); - - //Credentional public key (COSE_KEY) - uint8_t coseKey[65] = {0}; - uint16_t cplen = n - 55 - cridlen; - if (verbose2) { - PrintAndLog("Credentional public key (COSE_KEY)[%d]: %s", cplen, sprint_hex_inrow(&ubuf[55 + cridlen], cplen)); - } else { - PrintAndLog("Credentional public key (COSE_KEY)[%d]: %s...", cplen, sprint_hex(&ubuf[55 + cridlen], MIN(cplen, 16))); - } - JsonSaveBufAsHexCompact(root, "$.AppData.COSE_KEY", &ubuf[55 + cridlen], cplen); - - if (showCBOR) { - PrintAndLog("COSE structure:"); - PrintAndLog("---------------- CBOR ------------------"); - TinyCborPrintFIDOPackage(fido2COSEKey, true, &ubuf[55 + cridlen], cplen); - PrintAndLog("---------------- CBOR ------------------"); - } - - res = COSEGetECDSAKey(&ubuf[55 + cridlen], cplen, verbose, coseKey); - if (res) { - PrintAndLog("ERROR: Can't get COSE_KEY."); - } else { - PrintAndLog("COSE public key: %s", sprint_hex_inrow(coseKey, sizeof(coseKey))); - JsonSaveBufAsHexCompact(root, "$.AppData.COSEPublicKey", coseKey, sizeof(coseKey)); - } - - free(ubuf); - - // attStmt - we are check only as DER certificate - int64_t alg = 0; - uint8_t sign[128] = {0}; - size_t signLen = 0; - uint8_t der[4097] = {0}; - size_t derLen = 0; - - res = CborMapGetKeyById(&parser, &map, data, dataLen, 3); - if (res) - return res; - - res = cbor_value_enter_container(&map, &mapsmt); - cbor_check(res); - - while (!cbor_value_at_end(&mapsmt)) { - char key[100] = {0}; - res = CborGetStringValue(&mapsmt, key, sizeof(key), &n); - cbor_check(res); - if (!strcmp(key, "alg")) { - cbor_value_get_int64(&mapsmt, &alg); - PrintAndLog("Alg [%lld] %s", (long long)alg, GetCOSEAlgDescription(alg)); - res = cbor_value_advance_fixed(&mapsmt); - cbor_check(res); - } - - if (!strcmp(key, "sig")) { - res = CborGetBinStringValue(&mapsmt, sign, sizeof(sign), &signLen); - cbor_check(res); - if (verbose2) { - PrintAndLog("signature [%d]: %s", signLen, sprint_hex_inrow(sign, signLen)); - } else { - PrintAndLog("signature [%d]: %s...", signLen, sprint_hex(sign, MIN(signLen, 16))); - } - } - - if (!strcmp(key, "x5c")) { - res = CborGetArrayBinStringValue(&mapsmt, der, sizeof(der), &derLen); - cbor_check(res); - if (verbose2) { - PrintAndLog("DER certificate[%d]:\n------------------DER-------------------", derLen); - dump_buffer_simple((const unsigned char *)der, derLen, NULL); - PrintAndLog("\n----------------DER---------------------"); - } else { - PrintAndLog("DER [%d]: %s...", derLen, sprint_hex(der, MIN(derLen, 16))); - } - JsonSaveBufAsHexCompact(root, "$.AppData.DER", der, derLen); - } - } - res = cbor_value_leave_container(&map, &mapsmt); - cbor_check(res); - - uint8_t public_key[65] = {0}; - - // print DER certificate in TLV view - if (showDERTLV) { - PrintAndLog("----------------DER TLV-----------------"); - asn1_print(der, derLen, " "); - PrintAndLog("----------------DER TLV-----------------"); - } - FIDOCheckDERAndGetKey(der, derLen, verbose, public_key, sizeof(public_key)); - JsonSaveBufAsHexCompact(root, "$.AppData.DERPublicKey", public_key, sizeof(public_key)); - - // check ANSI X9.62 format ECDSA signature (on P-256) - uint8_t rval[300] = {0}; - uint8_t sval[300] = {0}; - res = ecdsa_asn1_get_signature(sign, signLen, rval, sval); - if (!res) { - if (verbose) { - PrintAndLog(" r: %s", sprint_hex(rval, 32)); - PrintAndLog(" s: %s", sprint_hex(sval, 32)); - } - - uint8_t clientDataHash[32] = {0}; - size_t clientDataHashLen = 0; - res = JsonLoadBufAsHex(root, "$.ClientDataHash", clientDataHash, sizeof(clientDataHash), &clientDataHashLen); - if (res || clientDataHashLen != 32) { - PrintAndLog("ERROR: Can't get clientDataHash from json!"); - return 2; - } - - uint8_t xbuf[4096] = {0}; - size_t xbuflen = 0; - res = FillBuffer(xbuf, sizeof(xbuf), &xbuflen, - authData, authDataLen, // rpIdHash[32] + flags[1] + signCount[4] + ... - clientDataHash, 32, // Hash of the serialized client data. "$.ClientDataHash" from json - NULL, 0); - //PrintAndLog("--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen)); - res = ecdsa_signature_verify(public_key, xbuf, xbuflen, sign, signLen); - if (res) { - if (res == -0x4e00) { - PrintAndLog("Signature is NOT VALID."); - } else { - PrintAndLog("Other signature check error: %x %s", (res<0)?-res:res, ecdsa_get_error(res)); - } - } else { - PrintAndLog("Signature is OK."); - } - } else { - PrintAndLog("Invalid signature. res=%d.", res); - } - - return 0; -} - int CmdHFFido2MakeCredential(const char *cmd) { json_error_t error; json_t *root = NULL; @@ -1058,18 +846,18 @@ int CmdHFFido2GetAssertion(const char *cmd) { DropField(); return 2; } -/* - res = FIDO2CreateMakeCredentionalReq(root, data, sizeof(data), &datalen); + + res = FIDO2CreateGetAssertionReq(root, data, sizeof(data), &datalen); if (res) return res; if (showCBOR) { PrintAndLog("CBOR get assertion request:"); PrintAndLog("---------------- CBOR ------------------"); - TinyCborPrintFIDOPackage(fido2CmdMakeCredential, false, data, datalen); + TinyCborPrintFIDOPackage(fido2CmdGetAssertion, false, data, datalen); PrintAndLog("---------------- CBOR ------------------"); } -*/ + res = FIDO2GetAssertion(data, datalen, buf, sizeof(buf), &len, &sw); DropField(); if (res) { diff --git a/client/fido/fido2.json b/client/fido/fido2.json index c158ef117..66449144f 100644 --- a/client/fido/fido2.json +++ b/client/fido/fido2.json @@ -25,5 +25,9 @@ "options": { "uv": false, "rk": false + }, + "GetAssertionOptions": { + "up": true, + "uv": false } -} \ No newline at end of file +} diff --git a/client/fido/fidocore.c b/client/fido/fidocore.c index c65959eff..22aee7c9f 100644 --- a/client/fido/fidocore.c +++ b/client/fido/fidocore.c @@ -21,6 +21,7 @@ #include "crypto/asn1utils.h" #include "crypto/libpcrypto.h" #include "fido/additional_ca.h" +#include "fido/cose.h" typedef struct { uint8_t ErrorCode; @@ -367,4 +368,263 @@ int FIDO2CreateMakeCredentionalReq(json_t *root, uint8_t *data, size_t maxdatale return 0; } +bool CheckrpIdHash(json_t *json, uint8_t *hash) { + char hashval[300] = {0}; + uint8_t hash2[32] = {0}; + + JsonLoadStr(json, "$.RelyingPartyEntity.id", hashval); + sha256hash((uint8_t *)hashval, strlen(hashval), hash2); + return !memcmp(hash, hash2, 32); +} + +int MakeCredentionalParseRes(json_t *root, uint8_t *data, size_t dataLen, bool verbose, bool verbose2, bool showCBOR, bool showDERTLV) { + CborParser parser; + CborValue map, mapsmt; + int res; + char *buf; + uint8_t *ubuf; + size_t n; + + // fmt + res = CborMapGetKeyById(&parser, &map, data, dataLen, 1); + if (res) + return res; + + res = cbor_value_dup_text_string(&map, &buf, &n, &map); + cbor_check(res); + PrintAndLog("format: %s", buf); + free(buf); + + // authData + uint8_t authData[400] = {0}; + size_t authDataLen = 0; + res = CborMapGetKeyById(&parser, &map, data, dataLen, 2); + if (res) + return res; + res = cbor_value_dup_byte_string(&map, &ubuf, &n, &map); + cbor_check(res); + + authDataLen = n; + memcpy(authData, ubuf, authDataLen); + + if (verbose2) { + PrintAndLog("authData[%d]: %s", n, sprint_hex_inrow(authData, authDataLen)); + } else { + PrintAndLog("authData[%d]: %s...", n, sprint_hex(authData, MIN(authDataLen, 16))); + } + + PrintAndLog("RP ID Hash: %s", sprint_hex(ubuf, 32)); + + // check RP ID Hash + if (CheckrpIdHash(root, ubuf)) { + PrintAndLog("rpIdHash OK."); + } else { + PrintAndLog("rpIdHash ERROR!"); + } + + PrintAndLog("Flags 0x%02x:", ubuf[32]); + if (!ubuf[32]) + PrintAndLog("none"); + if (ubuf[32] & 0x01) + PrintAndLog("up - user presence result"); + if (ubuf[32] & 0x04) + PrintAndLog("uv - user verification (fingerprint scan or a PIN or ...) result"); + if (ubuf[32] & 0x40) + PrintAndLog("at - attested credential data included"); + if (ubuf[32] & 0x80) + PrintAndLog("ed - extension data included"); + + uint32_t cntr = (uint32_t)bytes_to_num(&ubuf[33], 4); + PrintAndLog("Counter: %d", cntr); + JsonSaveInt(root, "$.AppData.Counter", cntr); + + // attestation data + PrintAndLog("AAGUID: %s", sprint_hex(&ubuf[37], 16)); + JsonSaveBufAsHexCompact(root, "$.AppData.AAGUID", &ubuf[37], 16); + + // Credential ID + uint8_t cridlen = (uint16_t)bytes_to_num(&ubuf[53], 2); + PrintAndLog("Credential id[%d]: %s", cridlen, sprint_hex_inrow(&ubuf[55], cridlen)); + JsonSaveInt(root, "$.AppData.CredentialIdLen", cridlen); + JsonSaveBufAsHexCompact(root, "$.AppData.CredentialId", &ubuf[55], cridlen); + + //Credentional public key (COSE_KEY) + uint8_t coseKey[65] = {0}; + uint16_t cplen = n - 55 - cridlen; + if (verbose2) { + PrintAndLog("Credentional public key (COSE_KEY)[%d]: %s", cplen, sprint_hex_inrow(&ubuf[55 + cridlen], cplen)); + } else { + PrintAndLog("Credentional public key (COSE_KEY)[%d]: %s...", cplen, sprint_hex(&ubuf[55 + cridlen], MIN(cplen, 16))); + } + JsonSaveBufAsHexCompact(root, "$.AppData.COSE_KEY", &ubuf[55 + cridlen], cplen); + + if (showCBOR) { + PrintAndLog("COSE structure:"); + PrintAndLog("---------------- CBOR ------------------"); + TinyCborPrintFIDOPackage(fido2COSEKey, true, &ubuf[55 + cridlen], cplen); + PrintAndLog("---------------- CBOR ------------------"); + } + + res = COSEGetECDSAKey(&ubuf[55 + cridlen], cplen, verbose, coseKey); + if (res) { + PrintAndLog("ERROR: Can't get COSE_KEY."); + } else { + PrintAndLog("COSE public key: %s", sprint_hex_inrow(coseKey, sizeof(coseKey))); + JsonSaveBufAsHexCompact(root, "$.AppData.COSEPublicKey", coseKey, sizeof(coseKey)); + } + + free(ubuf); + + // attStmt - we are check only as DER certificate + int64_t alg = 0; + uint8_t sign[128] = {0}; + size_t signLen = 0; + uint8_t der[4097] = {0}; + size_t derLen = 0; + + res = CborMapGetKeyById(&parser, &map, data, dataLen, 3); + if (res) + return res; + + res = cbor_value_enter_container(&map, &mapsmt); + cbor_check(res); + + while (!cbor_value_at_end(&mapsmt)) { + char key[100] = {0}; + res = CborGetStringValue(&mapsmt, key, sizeof(key), &n); + cbor_check(res); + if (!strcmp(key, "alg")) { + cbor_value_get_int64(&mapsmt, &alg); + PrintAndLog("Alg [%lld] %s", (long long)alg, GetCOSEAlgDescription(alg)); + res = cbor_value_advance_fixed(&mapsmt); + cbor_check(res); + } + + if (!strcmp(key, "sig")) { + res = CborGetBinStringValue(&mapsmt, sign, sizeof(sign), &signLen); + cbor_check(res); + if (verbose2) { + PrintAndLog("signature [%d]: %s", signLen, sprint_hex_inrow(sign, signLen)); + } else { + PrintAndLog("signature [%d]: %s...", signLen, sprint_hex(sign, MIN(signLen, 16))); + } + } + + if (!strcmp(key, "x5c")) { + res = CborGetArrayBinStringValue(&mapsmt, der, sizeof(der), &derLen); + cbor_check(res); + if (verbose2) { + PrintAndLog("DER certificate[%d]:\n------------------DER-------------------", derLen); + dump_buffer_simple((const unsigned char *)der, derLen, NULL); + PrintAndLog("\n----------------DER---------------------"); + } else { + PrintAndLog("DER [%d]: %s...", derLen, sprint_hex(der, MIN(derLen, 16))); + } + JsonSaveBufAsHexCompact(root, "$.AppData.DER", der, derLen); + } + } + res = cbor_value_leave_container(&map, &mapsmt); + cbor_check(res); + + uint8_t public_key[65] = {0}; + + // print DER certificate in TLV view + if (showDERTLV) { + PrintAndLog("----------------DER TLV-----------------"); + asn1_print(der, derLen, " "); + PrintAndLog("----------------DER TLV-----------------"); + } + FIDOCheckDERAndGetKey(der, derLen, verbose, public_key, sizeof(public_key)); + JsonSaveBufAsHexCompact(root, "$.AppData.DERPublicKey", public_key, sizeof(public_key)); + + // check ANSI X9.62 format ECDSA signature (on P-256) + uint8_t rval[300] = {0}; + uint8_t sval[300] = {0}; + res = ecdsa_asn1_get_signature(sign, signLen, rval, sval); + if (!res) { + if (verbose) { + PrintAndLog(" r: %s", sprint_hex(rval, 32)); + PrintAndLog(" s: %s", sprint_hex(sval, 32)); + } + + uint8_t clientDataHash[32] = {0}; + size_t clientDataHashLen = 0; + res = JsonLoadBufAsHex(root, "$.ClientDataHash", clientDataHash, sizeof(clientDataHash), &clientDataHashLen); + if (res || clientDataHashLen != 32) { + PrintAndLog("ERROR: Can't get clientDataHash from json!"); + return 2; + } + + uint8_t xbuf[4096] = {0}; + size_t xbuflen = 0; + res = FillBuffer(xbuf, sizeof(xbuf), &xbuflen, + authData, authDataLen, // rpIdHash[32] + flags[1] + signCount[4] + ... + clientDataHash, 32, // Hash of the serialized client data. "$.ClientDataHash" from json + NULL, 0); + //PrintAndLog("--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen)); + res = ecdsa_signature_verify(public_key, xbuf, xbuflen, sign, signLen); + if (res) { + if (res == -0x4e00) { + PrintAndLog("Signature is NOT VALID."); + } else { + PrintAndLog("Other signature check error: %x %s", (res<0)?-res:res, ecdsa_get_error(res)); + } + } else { + PrintAndLog("Signature is OK."); + } + } else { + PrintAndLog("Invalid signature. res=%d.", res); + } + + return 0; +} + +int FIDO2CreateGetAssertionReq(json_t *root, uint8_t *data, size_t maxdatalen, size_t *datalen) { + if (datalen) + *datalen = 0; + if (!root || !data || !maxdatalen) + return 1; + + int res; + CborEncoder encoder; + CborEncoder map; + + cbor_encoder_init(&encoder, data, maxdatalen, 0); + + // create main map + res = cbor_encoder_create_map(&encoder, &map, 5); + fido_check_if(res) { + // rpId + res = cbor_encode_uint(&map, 1); + fido_check_if(res) { +// char hashval[300] = {0}; +// JsonLoadStr(json, "$.RelyingPartyEntity.id", hashval); + + res = CBOREncodeElm(root, "RelyingPartyEntity", &map); + fido_check(res); + } + + // clientDataHash + res = cbor_encode_uint(&map, 2); + fido_check_if(res) { + res = CBOREncodeClientDataHash(root, &map); + fido_check(res); + } + + // options + res = cbor_encode_uint(&map, 5); + fido_check_if(res) { + res = CBOREncodeElm(root, "GetAssertionOptions", &map); + fido_check(res); + } + } + res = cbor_encoder_close_container(&encoder, &map); + fido_check(res); + + size_t len = cbor_encoder_get_buffer_size(&encoder, data); + if (datalen) + *datalen = len; + + return 0; +} diff --git a/client/fido/fidocore.h b/client/fido/fidocore.h index 5d6dc4be1..4baf029bb 100644 --- a/client/fido/fidocore.h +++ b/client/fido/fidocore.h @@ -48,6 +48,9 @@ extern int FIDOCheckDERAndGetKey(uint8_t *der, size_t derLen, bool verbose, uint extern char *fido2GetCmdMemberDescription(uint8_t cmdCode, bool isResponse, int memberNum); extern char *fido2GetCmdErrorDescription(uint8_t errorCode); +extern bool CheckrpIdHash(json_t *json, uint8_t *hash); extern int FIDO2CreateMakeCredentionalReq(json_t *root, uint8_t *data, size_t maxdatalen, size_t *datalen); +extern int MakeCredentionalParseRes(json_t *root, uint8_t *data, size_t dataLen, bool verbose, bool verbose2, bool showCBOR, bool showDERTLV); +extern int FIDO2CreateGetAssertionReq(json_t *root, uint8_t *data, size_t maxdatalen, size_t *datalen); #endif /* __FIDOCORE_H__ */