Skip to content

Instantly share code, notes, and snippets.

@x1ddos
Created April 29, 2020 18:34
Show Gist options
  • Save x1ddos/8f93bc4d91455460225c0300fea57603 to your computer and use it in GitHub Desktop.
Save x1ddos/8f93bc4d91455460225c0300fea57603 to your computer and use it in GitHub Desktop.
diff --git src/fido2/ctap.c src/fido2/ctap.c
index 5eb8e72..358d294 100644
--- src/fido2/ctap.c
+++ src/fido2/ctap.c
@@ -4,155 +4,66 @@
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
-#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "ctap.h"
-
#include "cbor.h"
-#include "cose_key.h"
+
+#include "ctap.h"
+#include "u2f.h"
#include "ctaphid.h"
#include "ctap_parse.h"
+#include "ctap_errors.h"
+#include "cose_key.h"
+#include "crypto.h"
+#include "util.h"
+#include "log.h"
#include "device.h"
+#include APP_CONFIG
+#include "wallet.h"
#include "extensions.h"
-#include "fido2_keys.h"
-#include "storage.h"
-
-#include <memory/memory.h>
-#include <crypto/sha2/sha256.h>
-#include <screen.h>
-#include <securechip/securechip.h>
-#include <ui/workflow_stack.h>
-#include <usb/usb_packet.h>
-#include <util.h>
-#include <workflow/confirm.h>
-#include <workflow/select_ctap_credential.h>
-#include <workflow/status.h>
-#include <workflow/unlock.h>
-/**
- * CTAP request codes.
- */
-#define CTAP_REQ_MAKE_CREDENTIAL (0x01)
-#define CTAP_REQ_GET_ASSERTION (0x02)
-#define CTAP_REQ_CANCEL (0x03)
-#define CTAP_REQ_GET_INFO (0x04)
-#define CTAP_REQ_CLIENT_PIN (0x06)
-#define CTAP_REQ_RESET (0x07)
-#define CTAP_REQ_GET_NEXT_ASSERTION (0x08)
-#define CTAP_REQ_VENDOR_FIRST (0x40)
-#define CTAP_REQ_VENDOR_LAST (0xBF)
+#include "device.h"
+#include "data_migration.h"
-/**
- * Auth data flags, defined in [WebAuthn] sec. 6.1. Authenticator Data.
- */
-/**
- * User is present/not present.
- */
-#define CTAP_AUTH_DATA_FLAG_USER_PRESENT (1 << 0)
-/**
- * User is verified/not verified.
- */
-#define CTAP_AUTH_DATA_FLAG_USER_VERIFIED (1 << 2)
-/**
- * Indicates whether the authenticator added attested credential data.
- */
-#define CTAP_AUTH_DATA_FLAG_ATTESTED_CRED_DATA_INCLUDED (1 << 6)
-/**
- * Indicates if the authenticator data has extensions.
- */
-#define CTAP_AUTH_DATA_FLAG_EXTENSION_DATA_INCLUDED (1 << 7)
+uint8_t PIN_TOKEN[PIN_TOKEN_SIZE];
+uint8_t KEY_AGREEMENT_PUB[64];
+static uint8_t KEY_AGREEMENT_PRIV[32];
+static int8_t PIN_BOOT_ATTEMPTS_LEFT = PIN_BOOT_ATTEMPTS;
-/**
- * Response tags.
- */
-#define CTAP_RESP_VERSIONS (0x1)
-#define CTAP_RESP_EXTENSIONS (0x2)
-#define CTAP_RESP_AAGUID (0x3)
-#define CTAP_RESP_OPTIONS (0x4)
-#define CTAP_RESP_MAX_MSG_SIZE (0x5)
-#define CTAP_RESP_PIN_PROTOCOLS (0x6)
-
-#define CTAP_RESP_FMT (0x01)
-#define CTAP_RESP_AUTH_DATA (0x02)
-#define CTAP_RESP_ATT_STMT (0x03)
-
-#define CTAP_RESP_CREDENTIAL (0x01)
-#define CTAP_RESP_SIGNATURE (0x03)
-#define CTAP_RESP_PUBKEY_CREDENTIAL_USER_ENTITY (0x04)
-#define CTAP_RESP_NUM_CREDENTIALS (0x05)
-
-#define CTAP_RESP_KEY_AGREEMENT (0x01)
-#define CTAP_RESP_PIN_TOKEN (0x02)
-#define CTAP_RESP_RETRIES (0x03)
-
-
-typedef struct {
- enum {
- CTAP_MAKE_CREDENTIAL_STARTED,
- CTAP_MAKE_CREDENTIAL_UNLOCKED,
- CTAP_MAKE_CREDENTIAL_WAIT_CONFIRM,
- CTAP_MAKE_CREDENTIAL_FINISHED,
- CTAP_MAKE_CREDENTIAL_FAILED,
- } state;
- ctap_make_credential_req_t req;
-} ctap_make_credential_state_t;
-
-typedef struct {
- enum {
- CTAP_GET_ASSERTION_STARTED,
- CTAP_GET_ASSERTION_UNLOCKED,
- CTAP_GET_ASSERTION_WAIT_CONFIRM,
- CTAP_GET_ASSERTION_CONFIRMED,
- CTAP_GET_ASSERTION_SELECT_CREDENTIAL,
- CTAP_GET_ASSERTION_SELECTED_CREDENTIAL,
- /** User aborted the request */
- CTAP_GET_ASSERTION_DENIED,
- /** No valid credentials were found. */
- CTAP_GET_ASSERTION_NO_CREDENTIALS,
- CTAP_GET_ASSERTION_FINISHED,
- } state;
- /** Key handle that was selected for authentication. */
- u2f_keyhandle_t auth_credential;
- /** Private key corresponding to auth_credential. */
- uint8_t auth_privkey[HMAC_SHA256_LEN];
- /** User ID corresponding to auth_credential.
- *
- * When no allow list is present, it's mandatory that
- * we add a user ID to the credential we return.
- */
- uint8_t user_id[CTAP_USER_ID_MAX_SIZE];
- /** Actual size of the user ID. */
- size_t user_id_size;
- /**
- * List of valid credentials for this GA request.
- */
- ctap_credential_display_list_t cred_list;
- /**
- * For each credential in cred_list, save the index
- * that that credential has in the RK memory.
- */
- int cred_idx[CTAP_CREDENTIAL_LIST_MAX_SIZE];
- ctap_get_assertion_req_t req;
-} ctap_get_assertion_state_t;
-
-static struct {
- enum {
- CTAP_BLOCKING_OP_NONE,
- CTAP_BLOCKING_OP_MAKE_CRED,
- CTAP_BLOCKING_OP_GET_ASSERTION
- } blocking_op;
- union {
- ctap_make_credential_state_t make_cred;
- ctap_get_assertion_state_t get_assertion;
- } data;
-} _state = {0};
-
-static uint8_t ctap_get_info(CborEncoder * encoder)
+AuthenticatorState STATE;
+
+static void ctap_reset_key_agreement();
+
+struct _getAssertionState getAssertionState;
+
+uint8_t verify_pin_auth(uint8_t * pinAuth, uint8_t * clientDataHash)
+{
+ uint8_t hmac[32];
+
+ crypto_sha256_hmac_init(PIN_TOKEN, PIN_TOKEN_SIZE, hmac);
+ crypto_sha256_update(clientDataHash, CLIENT_DATA_HASH_SIZE);
+ crypto_sha256_hmac_final(PIN_TOKEN, PIN_TOKEN_SIZE, hmac);
+
+ if (memcmp(pinAuth, hmac, 16) == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ printf2(TAG_ERR,"Pin auth failed\n");
+ dump_hex1(TAG_ERR,pinAuth,16);
+ dump_hex1(TAG_ERR,hmac,16);
+ return CTAP2_ERR_PIN_AUTH_INVALID;
+ }
+
+}
+
+
+
+uint8_t ctap_get_info(CborEncoder * encoder)
{
- (void)_state;
int ret;
CborEncoder array;
CborEncoder map;
@@ -164,7 +75,8 @@ static uint8_t ctap_get_info(CborEncoder * encoder)
ret = cbor_encoder_create_map(encoder, &map, 6);
check_ret(ret);
{
- ret = cbor_encode_uint(&map, CTAP_RESP_VERSIONS); // versions key
+
+ ret = cbor_encode_uint(&map, RESP_versions); // versions key
check_ret(ret);
{
ret = cbor_encoder_create_array(&map, &array, 2);
@@ -179,7 +91,7 @@ static uint8_t ctap_get_info(CborEncoder * encoder)
check_ret(ret);
}
- ret = cbor_encode_uint(&map, CTAP_RESP_EXTENSIONS);
+ ret = cbor_encode_uint(&map, RESP_extensions);
check_ret(ret);
{
ret = cbor_encoder_create_array(&map, &array, 1);
@@ -192,17 +104,17 @@ static uint8_t ctap_get_info(CborEncoder * encoder)
check_ret(ret);
}
- ret = cbor_encode_uint(&map, CTAP_RESP_AAGUID);
+ ret = cbor_encode_uint(&map, RESP_aaguid);
check_ret(ret);
{
ret = cbor_encode_byte_string(&map, aaguid, 16);
check_ret(ret);
}
- ret = cbor_encode_uint(&map, CTAP_RESP_OPTIONS);
+ ret = cbor_encode_uint(&map, RESP_options);
check_ret(ret);
{
- ret = cbor_encoder_create_map(&map, &options, 4);
+ ret = cbor_encoder_create_map(&map, &options,4);
check_ret(ret);
{
ret = cbor_encode_text_string(&options, "rk", 2);
@@ -219,40 +131,45 @@ static uint8_t ctap_get_info(CborEncoder * encoder)
check_ret(ret);
}
- ret = cbor_encode_text_string(&options, "uv", 2); // Capable of verifying user
+ // NOT [yet] capable of verifying user
+ // Do not add option if UV isn't supported.
+ //
+ // ret = cbor_encode_text_string(&options, "uv", 2);
+ // check_ret(ret);
+ // {
+ // ret = cbor_encode_boolean(&options, 0);
+ // check_ret(ret);
+ // }
+
+ ret = cbor_encode_text_string(&options, "plat", 4);
check_ret(ret);
{
- /*
- * The option should be true/false based on whether the UV function has already
- * been initialized.
- */
- ret = cbor_encode_boolean(&options, true);
+ ret = cbor_encode_boolean(&options, 0); // Not attached to platform
check_ret(ret);
}
- ret = cbor_encode_text_string(&options, "plat", 4);
+
+ ret = cbor_encode_text_string(&options, "clientPin", 9);
check_ret(ret);
{
- ret = cbor_encode_boolean(&options, 0); // Not attached to platform
+ ret = cbor_encode_boolean(&options, ctap_is_pin_set());
check_ret(ret);
}
- /*
- * We're not capable of PIN authentication, so the clientPin option
- * should be unset.
- */
+
+
}
ret = cbor_encoder_close_container(&map, &options);
check_ret(ret);
}
- ret = cbor_encode_uint(&map, CTAP_RESP_MAX_MSG_SIZE);
+ ret = cbor_encode_uint(&map, RESP_maxMsgSize);
check_ret(ret);
{
ret = cbor_encode_int(&map, CTAP_MAX_MESSAGE_SIZE);
check_ret(ret);
}
- ret = cbor_encode_uint(&map, CTAP_RESP_PIN_PROTOCOLS);
+ ret = cbor_encode_uint(&map, RESP_pinProtocols);
check_ret(ret);
{
ret = cbor_encoder_create_array(&map, &pins, 1);
@@ -264,6 +181,12 @@ static uint8_t ctap_get_info(CborEncoder * encoder)
ret = cbor_encoder_close_container(&map, &pins);
check_ret(ret);
}
+
+
+
+
+
+
}
ret = cbor_encoder_close_container(encoder, &map);
check_ret(ret);
@@ -273,7 +196,7 @@ static uint8_t ctap_get_info(CborEncoder * encoder)
-static int ctap_add_cose_key(CborEncoder* cose_key, uint8_t* x, uint8_t* y, int32_t algtype)
+static int ctap_add_cose_key(CborEncoder * cose_key, uint8_t * x, uint8_t * y, uint8_t credtype, int32_t algtype)
{
int ret;
CborEncoder map;
@@ -323,214 +246,417 @@ static int ctap_add_cose_key(CborEncoder* cose_key, uint8_t* x, uint8_t* y, int3
return 0;
}
+static int ctap_generate_cose_key(CborEncoder * cose_key, uint8_t * hmac_input, int len, uint8_t credtype, int32_t algtype)
+{
+ uint8_t x[32], y[32];
-/**
- * Encode the 32bit U2F counter value as a big-endian
- * sequence of bytes.
- * @param counter Counter to encode.
- * @param buf_out Buffer in which to encode the counter. Must be 4 bytes wide.
- */
-static void _encode_u2f_counter(uint32_t counter, uint8_t* buf_out)
+ if (credtype != PUB_KEY_CRED_PUB_KEY)
+ {
+ printf2(TAG_ERR,"Error, pubkey credential type not supported\n");
+ return -1;
+ }
+ switch(algtype)
+ {
+ case COSE_ALG_ES256:
+ if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_FAST);
+ crypto_ecc256_derive_public_key(hmac_input, len, x, y);
+ if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
+ break;
+ default:
+ printf2(TAG_ERR,"Error, COSE alg %d not supported\n", algtype);
+ return -1;
+ }
+ int ret = ctap_add_cose_key(cose_key, x, y, credtype, algtype);
+ check_ret(ret);
+ return 0;
+}
+
+void make_auth_tag(uint8_t * rpIdHash, uint8_t * nonce, uint32_t count, uint8_t * tag)
{
- *buf_out++ = (counter >> 24) & 0xff;
- *buf_out++ = (counter >> 16) & 0xff;
- *buf_out++ = (counter >> 8) & 0xff;
- *buf_out++ = (counter >> 0) & 0xff;
+ uint8_t hashbuf[32];
+ memset(hashbuf,0,sizeof(hashbuf));
+ crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY, 0, hashbuf);
+ crypto_sha256_update(rpIdHash, 32);
+ crypto_sha256_update(nonce, CREDENTIAL_NONCE_SIZE);
+ crypto_sha256_update((uint8_t*)&count, 4);
+ crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY,0,hashbuf);
+
+ memmove(tag, hashbuf, CREDENTIAL_TAG_SIZE);
}
-/**
- * Copy the source string into the destination buffer.
- * If the string is too long to fit the destination buffer,
- * truncate the string with "...", so that the resulting
- * string is always a valid NULL-terminated string.
- */
-static void _copy_or_truncate(char* dst, size_t dst_size, const char* src)
+void ctap_flush_state()
+{
+ authenticator_write_state(&STATE);
+}
+
+static uint32_t auth_data_update_count(CTAP_authDataHeader * authData)
{
- size_t src_size = strlen(src);
- bool truncate = false;
-
- const char* padding = "...";
- size_t padding_size = strlen(padding);
-
- if (dst_size < src_size + 1) {
- /*
- * String is too long.
- * Truncate the source string to the biggest possible size.
- */
- truncate = true;
- src_size = dst_size - 1 - padding_size;
- }
- strncpy(dst, src, src_size);
- if (!truncate) {
- dst[src_size] = '\0';
- } else {
- strcpy(dst + src_size, padding);
- dst[src_size + padding_size] = '\0';
+ uint32_t count = ctap_atomic_count( 0 );
+ if (count == 0) // count 0 will indicate invalid token
+ {
+ count = ctap_atomic_count( 0 );
+
}
+ uint8_t * byte = (uint8_t*) &authData->signCount;
+
+ *byte++ = (count >> 24) & 0xff;
+ *byte++ = (count >> 16) & 0xff;
+ *byte++ = (count >> 8) & 0xff;
+ *byte++ = (count >> 0) & 0xff;
+
+ return count;
+}
+
+static void ctap_increment_rk_store()
+{
+ STATE.rk_stored++;
+ ctap_flush_state();
+}
+
+static int is_matching_rk(CTAP_residentKey * rk, CTAP_residentKey * rk2)
+{
+ return (memcmp(rk->id.rpIdHash, rk2->id.rpIdHash, 32) == 0) &&
+ (memcmp(rk->user.id, rk2->user.id, rk->user.id_size) == 0) &&
+ (rk->user.id_size == rk2->user.id_size);
}
-static int _is_matching_rk(ctap_resident_key_t* rk, ctap_resident_key_t* rk2)
+static int ctap_make_extensions(CTAP_extensions * ext, uint8_t * ext_encoder_buf, unsigned int * ext_encoder_buf_size)
{
- return (memcmp(rk->rp_id_hash, rk2->rp_id_hash, 32) == 0) &&
- (memcmp(rk->rp_id, rk2->rp_id, CTAP_STORAGE_RP_ID_MAX_SIZE) == 0) &&
- (memcmp(rk->user_name, rk2->user_name, CTAP_STORAGE_USER_NAME_LIMIT) == 0);
+ CborEncoder extensions;
+ int ret;
+ uint8_t output[64];
+ uint8_t shared_secret[32];
+ uint8_t hmac[32];
+ uint8_t credRandom[32];
+
+ if (ext->hmac_secret_present == EXT_HMAC_SECRET_PARSED)
+ {
+ printf1(TAG_CTAP, "Processing hmac-secret..\r\n");
+
+ crypto_ecc256_shared_secret((uint8_t*) &ext->hmac_secret.keyAgreement.pubkey,
+ KEY_AGREEMENT_PRIV,
+ shared_secret);
+ crypto_sha256_init();
+ crypto_sha256_update(shared_secret, 32);
+ crypto_sha256_final(shared_secret);
+
+ crypto_sha256_hmac_init(shared_secret, 32, hmac);
+ crypto_sha256_update(ext->hmac_secret.saltEnc, ext->hmac_secret.saltLen);
+ crypto_sha256_hmac_final(shared_secret, 32, hmac);
+
+ if (memcmp(ext->hmac_secret.saltAuth, hmac, 16) == 0)
+ {
+ printf1(TAG_CTAP, "saltAuth is valid\r\n");
+ }
+ else
+ {
+ printf1(TAG_CTAP, "saltAuth is invalid\r\n");
+ return CTAP2_ERR_EXTENSION_FIRST;
+ }
+
+ // Generate credRandom
+ crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY2, 0, credRandom);
+ crypto_sha256_update((uint8_t*)&ext->hmac_secret.credential->id, sizeof(CredentialId));
+ crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY2, 0, credRandom);
+
+ // Decrypt saltEnc
+ crypto_aes256_init(shared_secret, NULL);
+ crypto_aes256_decrypt(ext->hmac_secret.saltEnc, ext->hmac_secret.saltLen);
+
+ // Generate outputs
+ crypto_sha256_hmac_init(credRandom, 32, output);
+ crypto_sha256_update(ext->hmac_secret.saltEnc, 32);
+ crypto_sha256_hmac_final(credRandom, 32, output);
+
+ if (ext->hmac_secret.saltLen == 64)
+ {
+ crypto_sha256_hmac_init(credRandom, 32, output + 32);
+ crypto_sha256_update(ext->hmac_secret.saltEnc + 32, 32);
+ crypto_sha256_hmac_final(credRandom, 32, output + 32);
+ }
+
+ // Encrypt for final output
+ crypto_aes256_init(shared_secret, NULL);
+ crypto_aes256_encrypt(output, ext->hmac_secret.saltLen);
+
+ // output
+ cbor_encoder_init(&extensions, ext_encoder_buf, *ext_encoder_buf_size, 0);
+ {
+ CborEncoder hmac_secret_map;
+ ret = cbor_encoder_create_map(&extensions, &hmac_secret_map, 1);
+ check_ret(ret);
+ {
+ ret = cbor_encode_text_stringz(&hmac_secret_map, "hmac-secret");
+ check_ret(ret);
+
+ ret = cbor_encode_byte_string(&hmac_secret_map, output, ext->hmac_secret.saltLen);
+ check_ret(ret);
+ }
+ ret = cbor_encoder_close_container(&extensions, &hmac_secret_map);
+ check_ret(ret);
+ }
+ *ext_encoder_buf_size = cbor_encoder_get_buffer_size(&extensions, ext_encoder_buf);
+ }
+ else if (ext->hmac_secret_present == EXT_HMAC_SECRET_REQUESTED)
+ {
+ cbor_encoder_init(&extensions, ext_encoder_buf, *ext_encoder_buf_size, 0);
+ {
+ CborEncoder hmac_secret_map;
+ ret = cbor_encoder_create_map(&extensions, &hmac_secret_map, 1);
+ check_ret(ret);
+ {
+ ret = cbor_encode_text_stringz(&hmac_secret_map, "hmac-secret");
+ check_ret(ret);
+
+ ret = cbor_encode_boolean(&hmac_secret_map, 1);
+ check_ret(ret);
+ }
+ ret = cbor_encoder_close_container(&extensions, &hmac_secret_map);
+ check_ret(ret);
+ }
+ *ext_encoder_buf_size = cbor_encoder_get_buffer_size(&extensions, ext_encoder_buf);
+ }
+ else
+ {
+ *ext_encoder_buf_size = 0;
+ }
+ return 0;
}
-static void _get_assertion_unlock_cb(bool result, void* param) {
- (void)param;
- if (!result) {
- /*
- * User didn't authenticate.
- * Let's count this as a "user denied" error.
- */
- _state.data.get_assertion.state = CTAP_GET_ASSERTION_DENIED;
- return;
- }
- _state.data.get_assertion.state = CTAP_GET_ASSERTION_UNLOCKED;
+static unsigned int get_credential_id_size(CTAP_credentialDescriptor * cred)
+{
+ if (cred->type == PUB_KEY_CRED_CTAP1)
+ return U2F_KEY_HANDLE_SIZE;
+ if (cred->type == PUB_KEY_CRED_CUSTOM)
+ return getAssertionState.customCredIdSize;
+ return sizeof(CredentialId);
}
-static void _get_assertion_allow_cb(bool result, void* param)
+static int ctap2_user_presence_test()
{
- (void)param;
- ctap_get_assertion_state_t* state = &_state.data.get_assertion;
- if (result) {
- state->state = CTAP_GET_ASSERTION_CONFIRMED;
- } else {
- state->state = CTAP_GET_ASSERTION_DENIED;
+ device_set_status(CTAPHID_STATUS_UPNEEDED);
+ int ret = ctap_user_presence_test(CTAP2_UP_DELAY_MS);
+ if ( ret > 1 )
+ {
+ return CTAP2_ERR_PROCESSING;
+ }
+ else if ( ret > 0 )
+ {
+ return CTAP1_ERR_SUCCESS;
+ }
+ else if (ret < 0)
+ {
+ return CTAP2_ERR_KEEPALIVE_CANCEL;
+ }
+ else
+ {
+ return CTAP2_ERR_ACTION_TIMEOUT;
}
}
-static workflow_t* _get_assertion_confirm(ctap_rp_id_t* rp)
+static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, uint32_t * len, CTAP_credInfo * credInfo)
{
- char prompt_buf[100];
- size_t prompt_size;
- if (rp->name && rp->name[0] != '\0') {
- /* There is a human-readable name attached to this domain. */
- prompt_size = snprintf(prompt_buf, 100, "Authenticate on\n%s\n(%.*s)\nsize %u",
- rp->name, (int)rp->size, rp->id, sizeof(_state));
- } else {
- prompt_size = snprintf(prompt_buf, 100, "Authenticate on\n%.*s\nsize %u",
- (int)rp->size, rp->id, sizeof(_state));
- }
- if (prompt_size >= 100) {
- prompt_buf[99] = '\0';
- }
-
- const confirm_params_t params = {
- .title = "FIDO2",
- .body = prompt_buf,
- .scrollable = false,
- };
- return workflow_confirm(&params, _get_assertion_allow_cb, NULL);
+ CborEncoder cose_key;
+
+ unsigned int auth_data_sz = sizeof(CTAP_authDataHeader);
+ uint32_t count;
+ CTAP_residentKey rk, rk2;
+ CTAP_authData * authData = (CTAP_authData *)auth_data_buf;
+
+ uint8_t * cose_key_buf = auth_data_buf + sizeof(CTAP_authData);
+
+ // memset(&cose_key, 0, sizeof(CTAP_residentKey));
+ memset(&rk, 0, sizeof(CTAP_residentKey));
+ memset(&rk2, 0, sizeof(CTAP_residentKey));
+
+ if((sizeof(CTAP_authDataHeader)) > *len)
+ {
+ printf1(TAG_ERR,"assertion fail, auth_data_buf must be at least %d bytes\n", sizeof(CTAP_authData) - sizeof(CTAP_attestHeader));
+ exit(1);
+ }
+
+ crypto_sha256_init();
+ crypto_sha256_update(rp->id, rp->size);
+ crypto_sha256_final(authData->head.rpIdHash);
+
+ count = auth_data_update_count(&authData->head);
+
+ int but;
+
+ but = ctap2_user_presence_test(CTAP2_UP_DELAY_MS);
+ if (CTAP2_ERR_PROCESSING == but)
+ {
+ authData->head.flags = (0 << 0); // User presence disabled
+ }
+ else
+ {
+ check_retr(but);
+ authData->head.flags = (1 << 0); // User presence
+ }
+
+
+ device_set_status(CTAPHID_STATUS_PROCESSING);
+
+ authData->head.flags |= (ctap_is_pin_set() << 2);
+
+
+ if (credInfo != NULL)
+ {
+ // add attestedCredentialData
+ authData->head.flags |= (1 << 6);//include attestation data
+
+ cbor_encoder_init(&cose_key, cose_key_buf, *len - sizeof(CTAP_authData), 0);
+
+ device_read_aaguid(authData->attest.aaguid);
+ authData->attest.credLenL = sizeof(CredentialId) & 0x00FF;
+ authData->attest.credLenH = (sizeof(CredentialId) & 0xFF00) >> 8;
+
+ memset((uint8_t*)&authData->attest.id, 0, sizeof(CredentialId));
+
+ ctap_generate_rng(authData->attest.id.nonce, CREDENTIAL_NONCE_SIZE);
+
+ authData->attest.id.count = count;
+
+ memmove(authData->attest.id.rpIdHash, authData->head.rpIdHash, 32);
+
+ // Make a tag we can later check to make sure this is a token we made
+ make_auth_tag(authData->head.rpIdHash, authData->attest.id.nonce, count, authData->attest.id.tag);
+
+ // resident key
+ if (credInfo->rk)
+ {
+ memmove(&rk.id, &authData->attest.id, sizeof(CredentialId));
+ memmove(&rk.user, &credInfo->user, sizeof(CTAP_userEntity));
+
+ unsigned int index = STATE.rk_stored;
+ unsigned int i;
+ for (i = 0; i < index; i++)
+ {
+ ctap_load_rk(i, &rk2);
+ if (is_matching_rk(&rk, &rk2))
+ {
+ ctap_overwrite_rk(i, &rk);
+ goto done_rk;
+ }
+ }
+ if (index >= ctap_rk_size())
+ {
+ printf2(TAG_ERR, "Out of memory for resident keys\r\n");
+ return CTAP2_ERR_KEY_STORE_FULL;
+ }
+ ctap_increment_rk_store();
+ ctap_store_rk(index, &rk);
+ dump_hex1(TAG_GREEN, rk.id.rpIdHash, 32);
+ }
+done_rk:
+
+ printf1(TAG_GREEN, "MADE credId: "); dump_hex1(TAG_GREEN, (uint8_t*) &authData->attest.id, sizeof(CredentialId));
+
+ ctap_generate_cose_key(&cose_key, (uint8_t*)&authData->attest.id, sizeof(CredentialId), credInfo->publicKeyCredentialType, credInfo->COSEAlgorithmIdentifier);
+
+ auth_data_sz = sizeof(CTAP_authData) + cbor_encoder_get_buffer_size(&cose_key, cose_key_buf);
+
+ }
+
+
+
+
+
+ *len = auth_data_sz;
+ return 0;
}
+
/**
- * @param sig[in] Location to deposit signature (must be 64 bytes)
- * @param out_encoded_sig[in] Location to deposit der signature (must be 72 bytes)
- * @return Length of DER encoded signature.
+ *
+ * @param in_sigbuf IN location to deposit signature (must be 64 bytes)
+ * @param out_sigder OUT location to deposit der signature (must be 72 bytes)
+ * @return length of der signature
* // FIXME add tests for maximum and minimum length of the input and output
*/
-static int _encode_der_sig(const uint8_t* sig, uint8_t* out_encoded_sig)
+int ctap_encode_der_sig(const uint8_t * const in_sigbuf, uint8_t * const out_sigder)
{
// Need to caress into dumb der format ..
+ uint8_t i;
uint8_t lead_s = 0; // leading zeros
uint8_t lead_r = 0;
- for (int i=0; i < 32; i++) {
- if (sig[i] == 0) {
- lead_r++;
- }
- else {
- break;
- }
- }
+ for (i=0; i < 32; i++)
+ if (in_sigbuf[i] == 0) lead_r++;
+ else break;
- for (int i=0; i < 32; i++) {
- if (sig[i+32] == 0) {
- lead_s++;
- }
- else {
- break;
- }
- }
+ for (i=0; i < 32; i++)
+ if (in_sigbuf[i+32] == 0) lead_s++;
+ else break;
- int8_t pad_s = ((sig[32 + lead_s] & 0x80) == 0x80);
- int8_t pad_r = ((sig[0 + lead_r] & 0x80) == 0x80);
+ int8_t pad_s = ((in_sigbuf[32 + lead_s] & 0x80) == 0x80);
+ int8_t pad_r = ((in_sigbuf[0 + lead_r] & 0x80) == 0x80);
- memset(out_encoded_sig, 0, 72);
- out_encoded_sig[0] = 0x30;
- out_encoded_sig[1] = 0x44 + pad_s + pad_r - lead_s - lead_r;
+ memset(out_sigder, 0, 72);
+ out_sigder[0] = 0x30;
+ out_sigder[1] = 0x44 + pad_s + pad_r - lead_s - lead_r;
// R ingredient
- out_encoded_sig[2] = 0x02;
- out_encoded_sig[3 + pad_r] = 0;
- out_encoded_sig[3] = 0x20 + pad_r - lead_r;
- memmove(out_encoded_sig + 4 + pad_r, sig + lead_r, 32u - lead_r);
+ out_sigder[2] = 0x02;
+ out_sigder[3 + pad_r] = 0;
+ out_sigder[3] = 0x20 + pad_r - lead_r;
+ memmove(out_sigder + 4 + pad_r, in_sigbuf + lead_r, 32u - lead_r);
// S ingredient
- out_encoded_sig[4 + 32 + pad_r - lead_r] = 0x02;
- out_encoded_sig[5 + 32 + pad_r + pad_s - lead_r] = 0;
- out_encoded_sig[5 + 32 + pad_r - lead_r] = 0x20 + pad_s - lead_s;
- memmove(out_encoded_sig + 6 + 32 + pad_r + pad_s - lead_r, sig + 32u + lead_s, 32u - lead_s);
+ out_sigder[4 + 32 + pad_r - lead_r] = 0x02;
+ out_sigder[5 + 32 + pad_r + pad_s - lead_r] = 0;
+ out_sigder[5 + 32 + pad_r - lead_r] = 0x20 + pad_s - lead_s;
+ memmove(out_sigder + 6 + 32 + pad_r + pad_s - lead_r, in_sigbuf + 32u + lead_s, 32u - lead_s);
return 0x46 + pad_s + pad_r - lead_r - lead_s;
}
-/**
- * Computes the EC256
- * See [WebAuthn], 8.2 "Signing procedure"
- * require load_key prior to this
- * @param[in] auth_data Authenticator data for the attestation.
- * @param[in] auth_data_len Length of auth_data.
- * @param[in] client_data_hash Hash of the serialized client data.
- * @param[out] sigbuf_out Buffer in which to store the computed signature
- */
-static bool _calculate_signature(const uint8_t* privkey, uint8_t* auth_data, size_t auth_data_len, uint8_t* client_data_hash, uint8_t* sigbuf_out)
+// require load_key prior to this
+// @data data to hash before signature
+// @clientDataHash for signature
+// @tmp buffer for hash. (can be same as data if data >= 32 bytes)
+// @sigbuf OUT location to deposit signature (must be 64 bytes)
+// @sigder OUT location to deposit der signature (must be 72 bytes)
+// @return length of der signature
+int ctap_calculate_signature(uint8_t * data, int datalen, uint8_t * clientDataHash, uint8_t * hashbuf, uint8_t * sigbuf, uint8_t * sigder)
{
- uint8_t hash_buf[SHA256_LEN];
-
- sha256_context_t ctx;
- sha256_reset(&ctx);
- noise_sha256_update(&ctx, auth_data, auth_data_len);
- noise_sha256_update(&ctx, client_data_hash, CLIENT_DATA_HASH_SIZE);
- sha256_finish(&ctx, hash_buf);
- if (!securechip_ecc_unsafe_sign(privkey, hash_buf, sigbuf_out)) {
- return false;
- }
- return true;
+ // calculate attestation sig
+ crypto_sha256_init();
+ crypto_sha256_update(data, datalen);
+ crypto_sha256_update(clientDataHash, CLIENT_DATA_HASH_SIZE);
+ crypto_sha256_final(hashbuf);
+
+ crypto_ecc256_sign(hashbuf, 32, sigbuf);
+ return ctap_encode_der_sig(sigbuf,sigder);
}
-/**
- * Adds the encoding of an attestation statement into a CBOR encoder.
- *
- * @param map[in] Encoder in which to append the attestation statement.
- * @param signature[in] Signature to add to the statement.
- * @param len[in] Length of signature.
- * @return Error code (or 0 for success).
- */
-static uint8_t _add_attest_statement(CborEncoder* map, const uint8_t* signature, int len)
+uint8_t ctap_add_attest_statement(CborEncoder * map, uint8_t * sigder, int len)
{
int ret;
- /* TODO: simo: generate another cert? */
- const uint8_t *cert = FIDO2_ATT_CERT;
- uint16_t cert_size = FIDO2_ATT_CERT_SIZE;
-
+ uint8_t cert[1024];
+ uint16_t cert_size = device_attestation_cert_der_get_size();
+ if (cert_size > sizeof(cert)){
+ printf2(TAG_ERR,"Certificate is too large for CTAP2 buffer\r\n");
+ return CTAP2_ERR_PROCESSING;
+ }
+ device_attestation_read_cert_der(cert);
+
CborEncoder stmtmap;
CborEncoder x5carr;
- ret = cbor_encode_int(map, CTAP_RESP_ATT_STMT);
+ ret = cbor_encode_int(map,RESP_attStmt);
check_ret(ret);
ret = cbor_encoder_create_map(map, &stmtmap, 3);
check_ret(ret);
{
ret = cbor_encode_text_stringz(&stmtmap,"alg");
check_ret(ret);
- ret = cbor_encode_int(&stmtmap, COSE_ALG_ES256);
+ ret = cbor_encode_int(&stmtmap,COSE_ALG_ES256);
check_ret(ret);
}
{
ret = cbor_encode_text_stringz(&stmtmap,"sig");
check_ret(ret);
- ret = cbor_encode_byte_string(&stmtmap, signature, len);
+ ret = cbor_encode_byte_string(&stmtmap, sigder, len);
check_ret(ret);
}
{
@@ -539,7 +665,7 @@ static uint8_t _add_attest_statement(CborEncoder* map, const uint8_t* signature,
ret = cbor_encoder_create_array(&stmtmap, &x5carr, 1);
check_ret(ret);
{
- ret = cbor_encode_byte_string(&x5carr, cert, cert_size);
+ ret = cbor_encode_byte_string(&x5carr, cert, device_attestation_cert_der_get_size());
check_ret(ret);
ret = cbor_encoder_close_container(&stmtmap, &x5carr);
check_ret(ret);
@@ -551,419 +677,182 @@ static uint8_t _add_attest_statement(CborEncoder* map, const uint8_t* signature,
return 0;
}
-/**
- * Computes the sha256 hash of the given RP id.
- * @param rp_hash_out Buffer in which to store the computed hash.
- * Must be SHA256_LEN bytes wide.
- */
-static void _compute_rpid_hash(ctap_rp_id_t* rp, uint8_t* rp_hash_out) {
- if (wally_sha256(rp->id, rp->size, rp_hash_out, SHA256_LEN) != WALLY_OK) {
- Abort("wally_sha256 failed");
- }
-}
-
-
-/**
- * Asks the user for confirmation when
- * a stored FIDO2 credential is about
- * to be overwritten with a new one for
- * the same user.
- */
-static bool _confirm_overwrite_credential(void) {
- /* TODO */
- return true;
-}
-
-/**
- * Check if any of the keys in a MakeCredential's
- * exclude_list belong to our device.
- *
- * @param req MakeCredential request to analyze.
- * @return Verification status:
- * - 0 if no invalid key was found;
- * - CTAP2_ERR_CREDENTIAL_EXCLUDED if an excluded key belongs to us;
- * - other errors if we failed to parse the exclude list.
- */
-static uint8_t _verify_exclude_list(ctap_make_credential_req_t* req)
+// Return 1 if credential belongs to this token
+int ctap_authenticate_credential(struct rpId * rp, CTAP_credentialDescriptor * desc)
{
- for (size_t i = 0; i < req->exclude_list_size; i++) {
- u2f_keyhandle_t excl_cred;
- bool cred_valid;
- uint8_t ret = ctap_parse_credential_descriptor(&req->exclude_list, &excl_cred, &cred_valid);
- if (!cred_valid || ret == CTAP2_ERR_CBOR_UNEXPECTED_TYPE) {
- /* Skip credentials that fail to parse. */
- continue;
- }
- if (ret != CborNoError) {
- return ret;
- }
-
- uint8_t privkey[HMAC_SHA256_LEN];
- UTIL_CLEANUP_32(privkey);
- bool key_is_ours = u2f_keyhandle_verify(req->rp.id, (uint8_t*)&excl_cred, sizeof(excl_cred), privkey);
- if (key_is_ours)
- {
- return true;
- }
-
- ret = cbor_value_advance(&req->exclude_list);
- check_ret(ret);
- }
- return false;
-}
+ uint8_t rpIdHash[32];
+ uint8_t tag[16];
-static bool _ask_generic_authorization(void) {
- const confirm_params_t params = {
- .title = "FIDO2",
- .body = "Proceed?",
- };
- return workflow_confirm_blocking(&params);
-}
+ switch(desc->type)
+ {
+ case PUB_KEY_CRED_PUB_KEY:
+ crypto_sha256_init();
+ crypto_sha256_update(rp->id, rp->size);
+ crypto_sha256_final(rpIdHash);
-/**
- * Called after the user has confirmed (or declined) the
- * creation of a new credential.
- */
-static void _make_credential_allow_cb(bool result, void* param) {
- (void)param;
- if (result) {
- _state.data.make_cred.state = CTAP_MAKE_CREDENTIAL_FINISHED;
- } else {
- _state.data.make_cred.state = CTAP_MAKE_CREDENTIAL_FAILED;
+ printf1(TAG_RED,"rpId: %s\r\n", rp->id); dump_hex1(TAG_RED,rp->id, rp->size);
+ if (memcmp(desc->credential.id.rpIdHash, rpIdHash, 32) != 0)
+ {
+ return 0;
+ }
+ make_auth_tag(rpIdHash, desc->credential.id.nonce, desc->credential.id.count, tag);
+ return (memcmp(desc->credential.id.tag, tag, CREDENTIAL_TAG_SIZE) == 0);
+ break;
+ case PUB_KEY_CRED_CTAP1:
+ crypto_sha256_init();
+ crypto_sha256_update(rp->id, rp->size);
+ crypto_sha256_final(rpIdHash);
+ return u2f_authenticate_credential((struct u2f_key_handle *)&desc->credential.id, U2F_KEY_HANDLE_SIZE,rpIdHash);
+ break;
+ case PUB_KEY_CRED_CUSTOM:
+ return is_extension_request(getAssertionState.customCredId, getAssertionState.customCredIdSize);
+ break;
+ default:
+ printf1(TAG_ERR, "PUB_KEY_CRED_UNKNOWN %x\r\n",desc->type);
+ break;
}
-}
-/**
- * Asks the user whether he wants to proceed
- * with the creation of a new credential.
- * @param req MakeCredential CTAP request.
- * @return Confirmation workflow.
- */
-static workflow_t* _make_credential_allow(ctap_make_credential_req_t* req)
-{
- char prompt_buf[100];
- size_t prompt_size;
- if (req->rp.name && req->rp.name[0] != '\0') {
- /* There is a human-readable name attached to this domain. */
- prompt_size = snprintf(prompt_buf, 100, "Create credential for\n%s\n(%.*s)\n",
- req->rp.name, (int)req->rp.size, req->rp.id);
- } else {
- prompt_size = snprintf(prompt_buf, 100, "Create credential for\n%.*s\n",
- (int)req->rp.size, req->rp.id);
- }
- if (prompt_size >= 100) {
- prompt_buf[99] = '\0';
- }
- const confirm_params_t params = {
- .title = "FIDO2",
- .body = prompt_buf,
- };
- return workflow_confirm(&params, _make_credential_allow_cb, NULL);
+ return 0;
}
-static void _make_credential_unlock_cb(bool result, void* param) {
- (void)param;
- //screen_sprintf_debug(1000, "UNLOCK CB %d", result);
- if (!result) {
- /*
- * User didn't authenticate.
- * Let's count this as a "user denied" error.
- */
- _state.data.make_cred.state = CTAP_MAKE_CREDENTIAL_FAILED;
- return;
- }
- _state.data.make_cred.state = CTAP_MAKE_CREDENTIAL_UNLOCKED;
-}
-static void _make_credential_init_state(ctap_make_credential_req_t* req)
-{
- _state.data.make_cred.state = CTAP_MAKE_CREDENTIAL_STARTED;
- memcpy(&_state.data.make_cred.req, req, sizeof(*req));
-}
-static void _make_credential_free_state(void)
+uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int length)
{
-}
-
-static uint8_t ctap_make_credential(CborEncoder * encoder, const in_buffer_t* in_buffer) {
- ctap_make_credential_req_t MC;
+ CTAP_makeCredential MC;
int ret;
+ unsigned int i;
+ uint8_t auth_data_buf[310];
+ CTAP_credentialDescriptor * excl_cred = (CTAP_credentialDescriptor *) auth_data_buf;
+ uint8_t * sigbuf = auth_data_buf + 32;
+ uint8_t * sigder = auth_data_buf + 32 + 64;
- ret = ctap_parse_make_credential(&MC,encoder, in_buffer);
+ ret = ctap_parse_make_credential(&MC,encoder,request,length);
- if (ret != 0) {
+ if (ret != 0)
+ {
+ printf2(TAG_ERR,"error, parse_make_credential failed\n");
return ret;
}
- if (MC.pin_auth_empty) {
- /*
- * pin_auth was present and was an empty string.
- * The client is asking us if we support pin
- * (and asks to check for user presence before we move on).
- */
- bool result = _ask_generic_authorization();
- if (!result) {
- return CTAP2_ERR_OPERATION_DENIED;
- }
- /* We don't support PIN semantics. */
- return CTAP2_ERR_PIN_NOT_SET;
+ if (MC.pinAuthEmpty)
+ {
+ check_retr( ctap2_user_presence_test(CTAP2_UP_DELAY_MS) );
+ return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_AUTH_INVALID : CTAP2_ERR_PIN_NOT_SET;
+ }
+ if ((MC.paramsParsed & MC_requiredMask) != MC_requiredMask)
+ {
+ printf2(TAG_ERR,"error, required parameter(s) for makeCredential are missing\n");
+ return CTAP2_ERR_MISSING_PARAMETER;
}
- if (MC.pin_auth_present) {
- /* We don't support pin_auth. */
- return CTAP2_ERR_PIN_AUTH_INVALID;
+ if (ctap_is_pin_set() == 1 && MC.pinAuthPresent == 0)
+ {
+ printf2(TAG_ERR,"pinAuth is required\n");
+ return CTAP2_ERR_PIN_REQUIRED;
+ }
+ else
+ {
+ if (ctap_is_pin_set() || (MC.pinAuthPresent))
+ {
+ ret = verify_pin_auth(MC.pinAuth, MC.clientDataHash);
+ check_retr(ret);
+ }
}
- if (MC.up == 1 || MC.up == 0) {
- /*
- * The UP flag can't be set for authenticatorMakeCredential.
- * It must always be unset (0xFF).
- */
+ if (MC.up == 1 || MC.up == 0)
+ {
return CTAP2_ERR_INVALID_OPTION;
}
- _make_credential_init_state(&MC);
- workflow_stack_start_workflow(workflow_unlock(_make_credential_unlock_cb, NULL));
- return CTAP1_ERR_SUCCESS;
-}
-
-/**
- * Generates a new credential in response to a MakeCredential request.
- * Only called when the user has already accepted and identified with the device.
- *
- * @return CTAP status code.
- */
-
-static int _make_credential_complete(buffer_t* out_buf)
-{
- ctap_make_credential_state_t* state = &_state.data.make_cred;
- /*
- * The exclude list contains a list of credentials that we
- * must check. If any credential was generated by our device,
- * we must return with an error. This allows the server to avoid
- * us creating more than one credential for the same user/device pair.
- */
- int ret = _verify_exclude_list(&state->req);
- if (ret != CborNoError) {
- return CTAP2_ERR_CBOR_PARSING;
- }
-
- /* Update the U2F counter. */
- uint32_t u2f_counter;
- if (!securechip_u2f_counter_inc(&u2f_counter)) {
- workflow_status_blocking("Failed to create key.", false);
- return CTAP2_ERR_OPERATION_DENIED;
- }
-
- ctap_auth_data_t auth_data;
- _compute_rpid_hash(&state->req.rp, auth_data.head.rp_id_hash);
-
- /* Generate the key. */
- memset((uint8_t*)&auth_data.attest.id, 0, sizeof(u2f_keyhandle_t));
- uint8_t* nonce = auth_data.attest.id.nonce;
- uint8_t* mac = auth_data.attest.id.mac;
- uint8_t pubkey[64];
- uint8_t privkey[HMAC_SHA256_LEN];
- UTIL_CLEANUP_32(privkey);
- bool key_create_success = u2f_keyhandle_create_key(state->req.rp.id, nonce, privkey, mac, pubkey);
- if (!key_create_success) {
- /* TODO: simo: do something. */
- Abort("Failed to create new FIDO2 key.");
- }
-
- /*
- * Find where to store this key.
- * If it's new, store it in the first
- * available location. Otherwise, overwrite
- * the existing key (after confirming with the user).
- */
- if (state->req.cred_info.rk) {
- ctap_resident_key_t rk_to_store;
- memset(&rk_to_store, 0, sizeof(rk_to_store));
- memcpy(&rk_to_store.key_handle, &auth_data.attest.id, sizeof(rk_to_store.key_handle));
- memcpy(&rk_to_store.rp_id_hash, auth_data.head.rp_id_hash, sizeof(auth_data.head.rp_id_hash));
- _copy_or_truncate((char*)rk_to_store.rp_id, sizeof(rk_to_store.rp_id), (const char*)state->req.rp.id);
- _copy_or_truncate((char*)rk_to_store.user_name, sizeof(rk_to_store.user_name), (const char*)state->req.cred_info.user.name);
- _copy_or_truncate((char*)rk_to_store.display_name, sizeof(rk_to_store.display_name), (const char*)state->req.cred_info.user.display_name);
- rk_to_store.valid = CTAP_RESIDENT_KEY_VALID;
- rk_to_store.creation_time = u2f_counter;
- if (state->req.cred_info.user.id_size > CTAP_USER_ID_MAX_SIZE) {
- /* We can't store such a big user ID.
- * But we can't even truncate it... So nothing we can do, alas.
- */
- return CTAP2_ERR_REQUEST_TOO_LARGE;
- }
- //screen_sprintf_debug(2000, "UID (%u): %02x%02x",
- //state->req.cred_info.user.id_size,
- //state->req.cred_info.user.id[0], state->req.cred_info.user.id[state->req.cred_info.user.id_size - 1]
- //);
- rk_to_store.user_id_size = state->req.cred_info.user.id_size;
- memcpy(rk_to_store.user_id, state->req.cred_info.user.id, state->req.cred_info.user.id_size);
-
- int store_location = 0;
- bool must_overwrite = false;
- bool free_spot_found = false;
-
- for (int i = 0; i < MEMORY_CTAP_RESIDENT_KEYS_MAX; i++) {
- /* Check if we want to overwrite */
- ctap_resident_key_t this_key;
- bool mem_result = memory_get_ctap_resident_key(i, &this_key);
- if (!mem_result) {
- /* Skip on error */
- continue;
- }
- if (this_key.valid != CTAP_RESIDENT_KEY_VALID) {
- /* Skip invalid keys, mark spot as free */
- if (!free_spot_found) {
- store_location = i;
- free_spot_found = true;
- }
- continue;
- }
- if (_is_matching_rk(&rk_to_store, &this_key)) {
- /* Found a matching key. Need to overwrite. */
- free_spot_found = true;
- must_overwrite = true;
- store_location = i;
- break;
- }
- }
- if (!free_spot_found) {
- workflow_status_blocking("Out of memory for resident keys", false);
- return CTAP2_ERR_KEY_STORE_FULL;
- }
- if (must_overwrite) {
- if (!_confirm_overwrite_credential()) {
- workflow_status_blocking("Operation cancelled", false);
- return CTAP2_ERR_OPERATION_DENIED;
- }
+ // crypto_aes256_init(CRYPTO_TRANSPORT_KEY, NULL);
+ for (i = 0; i < MC.excludeListSize; i++)
+ {
+ ret = parse_credential_descriptor(&MC.excludeList, excl_cred);
+ if (ret == CTAP2_ERR_CBOR_UNEXPECTED_TYPE)
+ {
+ continue;
}
- memory_store_ctap_resident_key(store_location, &rk_to_store);
- //screen_sprintf_debug(500, "Stored key #%d\n", store_location);
- //uint8_t* cred_raw = (uint8_t*)&rk_to_store.key_handle;
- //screen_sprintf_debug(3000, "KH: %02x..%02x",
- //cred_raw[0], cred_raw[15]);
- } else {
- //screen_print_debug("Not stored key\n", 500);
- }
-
- /*
- * Now create the response.
- * This is an attestation object, as defined
- * in [WebAuthn], 6.4 (Figure 5).
- */
- CborEncoder encoder;
- CborEncoder attest_obj;
- memset(&encoder,0,sizeof(CborEncoder));
- cbor_encoder_init(&encoder, out_buf->data, out_buf->max_len, 0);
- ret = cbor_encoder_create_map(&encoder, &attest_obj, 3);
- check_ret(ret);
+ check_retr(ret);
- /*
- * First comes the Authenticator Data.
- * (Note: the rpId has already been stored at the start of auth_data...)
- */
- auth_data.head.flags = CTAP_AUTH_DATA_FLAG_ATTESTED_CRED_DATA_INCLUDED |
- CTAP_AUTH_DATA_FLAG_USER_VERIFIED | CTAP_AUTH_DATA_FLAG_USER_PRESENT;
+ printf1(TAG_GREEN, "checking credId: "); dump_hex1(TAG_GREEN, (uint8_t*) &excl_cred->credential.id, sizeof(CredentialId));
- _encode_u2f_counter(u2f_counter, (uint8_t*)&auth_data.head.signCount);
-
- device_read_aaguid(auth_data.attest.aaguid);
-
- /* Encode the length of the key handle in big endian. */
- uint16_t key_length = sizeof(u2f_keyhandle_t);
- auth_data.attest.cred_len[0] = (key_length & 0xFF00) >> 8;
- auth_data.attest.cred_len[1] = (key_length & 0x00FF);
+ if (ctap_authenticate_credential(&MC.rp, excl_cred))
+ {
+ printf1(TAG_MC, "Cred %d failed!\r\n",i);
+ return CTAP2_ERR_CREDENTIAL_EXCLUDED;
+ }
- CborEncoder cose_key;
- uint8_t* cose_key_buf = auth_data.other;
- cbor_encoder_init(&cose_key, cose_key_buf, sizeof(auth_data.other), 0);
- ret = ctap_add_cose_key(&cose_key, pubkey, pubkey + 32, COSE_ALG_ES256);
- if (ret != CborNoError) {
- return ret;
+ ret = cbor_value_advance(&MC.excludeList);
+ check_ret(ret);
}
- size_t cose_key_len = cbor_encoder_get_buffer_size(&cose_key, cose_key_buf);
- size_t actual_auth_data_len = sizeof(auth_data) - sizeof(auth_data.other) + cose_key_len;
- /* FUTURE: manage extensions if we want to. */
- /*
- * 3 fields in an attestation object:
- * - fmt
- * - authData
- * - attStmt
- */
+ CborEncoder map;
+ ret = cbor_encoder_create_map(encoder, &map, 3);
+ check_ret(ret);
+
{
- ret = cbor_encode_int(&attest_obj, CTAP_RESP_FMT);
+ ret = cbor_encode_int(&map,RESP_fmt);
check_ret(ret);
- ret = cbor_encode_text_stringz(&attest_obj, "packed");
+ ret = cbor_encode_text_stringz(&map, "packed");
check_ret(ret);
}
+ uint32_t auth_data_sz = sizeof(auth_data_buf);
+
+ ret = ctap_make_auth_data(&MC.rp, &map, auth_data_buf, &auth_data_sz,
+ &MC.credInfo);
+ check_retr(ret);
+
+ {
+ unsigned int ext_encoder_buf_size = sizeof(auth_data_buf) - auth_data_sz;
+ uint8_t * ext_encoder_buf = auth_data_buf + auth_data_sz;
+
+ ret = ctap_make_extensions(&MC.extensions, ext_encoder_buf, &ext_encoder_buf_size);
+ check_retr(ret);
+ if (ext_encoder_buf_size)
+ {
+ ((CTAP_authData *)auth_data_buf)->head.flags |= (1 << 7);
+ auth_data_sz += ext_encoder_buf_size;
+ }
+ }
{
- ret = cbor_encode_int(&attest_obj, CTAP_RESP_AUTH_DATA);
+ ret = cbor_encode_int(&map,RESP_authData);
check_ret(ret);
- ret = cbor_encode_byte_string(&attest_obj, (uint8_t*)&auth_data, actual_auth_data_len);
+ ret = cbor_encode_byte_string(&map, auth_data_buf, auth_data_sz);
check_ret(ret);
}
- /* Compute the attestation statement. */
- uint8_t sigbuf[32];
- bool sig_success = _calculate_signature(FIDO2_ATT_PRIV_KEY, (uint8_t*)&auth_data, actual_auth_data_len, state->req.client_data_hash, sigbuf);
- if (!sig_success) {
- return CTAP1_ERR_OTHER;
- }
- uint8_t attest_signature[72];
- int attest_sig_size = _encode_der_sig(sigbuf, attest_signature);
+ crypto_ecc256_load_attestation_key();
+ int sigder_sz = ctap_calculate_signature(auth_data_buf, auth_data_sz, MC.clientDataHash, auth_data_buf, sigbuf, sigder);
+ printf1(TAG_MC,"der sig [%d]: ", sigder_sz); dump_hex1(TAG_MC, sigder, sigder_sz);
- ret = _add_attest_statement(&attest_obj, attest_signature, attest_sig_size);
- if (ret != CborNoError) {
- return ret;
- }
+ ret = ctap_add_attest_statement(&map, sigder, sigder_sz);
+ check_retr(ret);
- ret = cbor_encoder_close_container(&encoder, &attest_obj);
+ ret = cbor_encoder_close_container(encoder, &map);
check_ret(ret);
- //workflow_status_create("Registration\ncompleted.", true);
- out_buf->len = cbor_encoder_get_buffer_size(&encoder, out_buf->data);
return CTAP1_ERR_SUCCESS;
}
-static ctap_request_result_t _make_credential_continue(buffer_t* out_buf) {
- ctap_request_result_t result = {.status = 0, .request_completed = true};
- ctap_make_credential_state_t* state = &_state.data.make_cred;
-
- switch (state->state) {
- case CTAP_MAKE_CREDENTIAL_UNLOCKED:
- /*
- * Request permission to the user.
- * This must be done before checking for excluded credentials etc.
- * so that we don't reveal the existance of credentials without
- * the user's consent.
- */
- workflow_stack_start_workflow(_make_credential_allow(&_state.data.make_cred.req));
- state->state = CTAP_MAKE_CREDENTIAL_WAIT_CONFIRM;
- result.request_completed = false;
- return result;
- case CTAP_MAKE_CREDENTIAL_FINISHED:
- result.status = _make_credential_complete(out_buf);
- return result;
- case CTAP_MAKE_CREDENTIAL_FAILED:
- workflow_status_blocking("Operation cancelled", false);
- result.status = CTAP2_ERR_OPERATION_DENIED;
- return result;
- case CTAP_MAKE_CREDENTIAL_STARTED:
- case CTAP_MAKE_CREDENTIAL_WAIT_CONFIRM:
- result.request_completed = false;
- return result;
- default:
- Abort("Invalid make_credential state");
- }
-}
-
-static uint8_t ctap_add_credential_descriptor(CborEncoder* map, u2f_keyhandle_t* key_handle)
+/*static int pick_first_authentic_credential(CTAP_getAssertion * GA)*/
+/*{*/
+ /*int i;*/
+ /*for (i = 0; i < GA->credLen; i++)*/
+ /*{*/
+ /*if (GA->creds[i].credential.enc.count != 0)*/
+ /*{*/
+ /*return i;*/
+ /*}*/
+ /*}*/
+ /*return -1;*/
+/*}*/
+
+static uint8_t ctap_add_credential_descriptor(CborEncoder * map, CTAP_credentialDescriptor * cred)
{
CborEncoder desc;
- int ret = cbor_encode_int(map, CTAP_RESP_CREDENTIAL);
+ int ret = cbor_encode_int(map, RESP_credential);
check_ret(ret);
ret = cbor_encoder_create_map(map, &desc, 2);
@@ -973,8 +862,8 @@ static uint8_t ctap_add_credential_descriptor(CborEncoder* map, u2f_keyhandle_t*
ret = cbor_encode_text_string(&desc, "id", 2);
check_ret(ret);
- ret = cbor_encode_byte_string(&desc, (uint8_t*)key_handle,
- sizeof(*key_handle));
+ ret = cbor_encode_byte_string(&desc, (uint8_t*)&cred->credential.id,
+ get_credential_id_size(cred));
check_ret(ret);
}
@@ -993,40 +882,49 @@ static uint8_t ctap_add_credential_descriptor(CborEncoder* map, u2f_keyhandle_t*
return 0;
}
-/**
- * Comparator function used to qsort() the credentials.
- * @return >0 if b is more recent than a, 0 if they have the same age (should never happen!),
- * <0 otherwise.
- */
-static int _compare_display_credentials(const void * _a, const void * _b)
-{
- const ctap_credential_display_t* a = (const ctap_credential_display_t* )_a;
- const ctap_credential_display_t* b = (const ctap_credential_display_t* )_b;
- return b->creation_time - a->creation_time;
-}
-
-/**
- * Adds the "publickKeyCredentialUserEntity" field to a CBOR
- * object, containing the specified user id as its only field.
- *
- * @param user_id must be at least user_id_size wide.
- * @param user_id_size Length of user_id.
- */
-static uint8_t _encode_user_id(CborEncoder* map, const uint8_t* user_id, size_t user_id_size)
+uint8_t ctap_add_user_entity(CborEncoder * map, CTAP_userEntity * user)
{
CborEncoder entity;
- int ret = cbor_encode_int(map, CTAP_RESP_PUBKEY_CREDENTIAL_USER_ENTITY);
+ int ret = cbor_encode_int(map, RESP_publicKeyCredentialUserEntity);
check_ret(ret);
- ret = cbor_encoder_create_map(map, &entity, 1);
+ int dispname = (user->name[0] != 0) && getAssertionState.user_verified;
+
+ if (dispname)
+ ret = cbor_encoder_create_map(map, &entity, 4);
+ else
+ ret = cbor_encoder_create_map(map, &entity, 1);
check_ret(ret);
{
ret = cbor_encode_text_string(&entity, "id", 2);
check_ret(ret);
- ret = cbor_encode_byte_string(&entity, user_id, user_id_size);
+ ret = cbor_encode_byte_string(&entity, user->id, user->id_size);
+ check_ret(ret);
+ }
+
+ if (dispname)
+ {
+
+ ret = cbor_encode_text_string(&entity, "icon", 4);
+ check_ret(ret);
+
+ ret = cbor_encode_text_stringz(&entity, (const char *)user->icon);
+ check_ret(ret);
+
+ ret = cbor_encode_text_string(&entity, "name", 4);
+ check_ret(ret);
+
+ ret = cbor_encode_text_stringz(&entity, (const char *)user->name);
+ check_ret(ret);
+
+ ret = cbor_encode_text_string(&entity, "displayName", 11);
+ check_ret(ret);
+
+ ret = cbor_encode_text_stringz(&entity, (const char *)user->displayName);
check_ret(ret);
+
}
ret = cbor_encoder_close_container(map, &entity);
@@ -1035,468 +933,1124 @@ static uint8_t _encode_user_id(CborEncoder* map, const uint8_t* user_id, size_t
return 0;
}
-/**
- * Fills a getAssertion response, as defined in the FIDO2 specs, 5.2.
- *
- * The response map contains:
- * - Credential descriptor
- * - Auth data
- * - Signature
- * - User ID (if present)
- *
- * Note that we don't include any user data as there is no need for that
- * (the user has already been selected on the device).
- */
-static uint8_t ctap_end_get_assertion(CborEncoder* encoder, u2f_keyhandle_t* key_handle, uint8_t* auth_data_buf, unsigned int auth_data_buf_sz, uint8_t* privkey, uint8_t* client_data_hash, const uint8_t* user_id, size_t user_id_size)
+static int cred_cmp_func(const void * _a, const void * _b)
{
- int ret;
- uint8_t signature[64];
- uint8_t encoded_sig[72];
- int encoded_sig_size;
+ CTAP_credentialDescriptor * a = (CTAP_credentialDescriptor * )_a;
+ CTAP_credentialDescriptor * b = (CTAP_credentialDescriptor * )_b;
+ return b->credential.id.count - a->credential.id.count;
+}
- CborEncoder map;
- int map_size = 3;
- if (user_id_size) {
- map_size++;
- }
-
- ret = cbor_encoder_create_map(encoder, &map, map_size);
- check_ret(ret);
+static void add_existing_user_info(CTAP_credentialDescriptor * cred)
+{
+ CTAP_residentKey rk;
+ int index = STATE.rk_stored;
+ int i;
+ for (i = 0; i < index; i++)
+ {
+ ctap_load_rk(i, &rk);
+ if (is_matching_rk(&rk, (CTAP_residentKey *)&cred->credential))
+ {
+ printf1(TAG_GREEN, "found rk match for allowList item (%d)\r\n", i);
+ memmove(&cred->credential.user, &rk.user, sizeof(CTAP_userEntity));
+ return;
+ }
- ret = ctap_add_credential_descriptor(&map, key_handle); // 1
- if (ret != CborNoError) {
- return ret;
}
+ printf1(TAG_GREEN, "NO rk match for allowList item \r\n");
+}
+
+// @return the number of valid credentials
+// sorts the credentials. Most recent creds will be first, invalid ones last.
+int ctap_filter_invalid_credentials(CTAP_getAssertion * GA)
+{
+ int i;
+ int count = 0;
+ uint8_t rpIdHash[32];
+ CTAP_residentKey rk;
+ for (i = 0; i < GA->credLen; i++)
{
- ret = cbor_encode_int(&map, CTAP_RESP_AUTH_DATA); // 2
- check_ret(ret);
- ret = cbor_encode_byte_string(&map, auth_data_buf, auth_data_buf_sz);
- check_ret(ret);
- }
+ if (! ctap_authenticate_credential(&GA->rp, &GA->creds[i]))
+ {
+ printf1(TAG_GA, "CRED #%d is invalid\n", GA->creds[i].credential.id.count);
+#ifdef ENABLE_U2F_EXTENSIONS
+ if (is_extension_request((uint8_t*)&GA->creds[i].credential.id, sizeof(CredentialId)))
+ {
+ printf1(TAG_EXT, "CRED #%d is extension\n", GA->creds[i].credential.id.count);
+ count++;
+ }
+ else
+#endif
+ {
+ GA->creds[i].credential.id.count = 0; // invalidate
+ }
- bool sig_success = _calculate_signature(privkey, auth_data_buf, auth_data_buf_sz, client_data_hash, signature);
- if (!sig_success) {
- return CTAP1_ERR_OTHER;
+ }
+ else
+ {
+ // add user info if it exists
+ add_existing_user_info(&GA->creds[i]);
+ count++;
+ }
}
- encoded_sig_size = _encode_der_sig(signature, encoded_sig);
+ // No allowList, so use all matching RK's matching rpId
+ if (!GA->credLen)
{
- ret = cbor_encode_int(&map, CTAP_RESP_SIGNATURE); // 3
- check_ret(ret);
- ret = cbor_encode_byte_string(&map, encoded_sig, encoded_sig_size);
- check_ret(ret);
- }
- if (user_id_size)
- {
- ret = _encode_user_id(&map, user_id, user_id_size); // 4
- if (ret != CborNoError) {
- return ret;
+ crypto_sha256_init();
+ crypto_sha256_update(GA->rp.id,GA->rp.size);
+ crypto_sha256_final(rpIdHash);
+
+ printf1(TAG_GREEN, "true rpIdHash: "); dump_hex1(TAG_GREEN, rpIdHash, 32);
+ for(i = 0; i < STATE.rk_stored; i++)
+ {
+ ctap_load_rk(i, &rk);
+ printf1(TAG_GREEN, "rpIdHash%d: ", i); dump_hex1(TAG_GREEN, rk.id.rpIdHash, 32);
+ if (memcmp(rk.id.rpIdHash, rpIdHash, 32) == 0)
+ {
+ printf1(TAG_GA, "RK %d is a rpId match!\r\n", i);
+ if (count == ALLOW_LIST_MAX_SIZE-1)
+ {
+ printf2(TAG_ERR, "not enough ram allocated for matching RK's (%d). Skipping.\r\n", count);
+ break;
+ }
+ GA->creds[count].type = PUB_KEY_CRED_PUB_KEY;
+ memmove(&(GA->creds[count].credential), &rk, sizeof(CTAP_residentKey));
+ count++;
+ }
}
+ GA->credLen = count;
}
- ret = cbor_encoder_close_container(encoder, &map);
- return 0;
+
+ printf1(TAG_GA, "qsort length: %d\n", GA->credLen);
+ qsort(GA->creds, GA->credLen, sizeof(CTAP_credentialDescriptor), cred_cmp_func);
+ return count;
}
-/**
- * Selects one of the matching credentials in the given credential list.
- *
- * @param GA getAssertion request that must be examined. Must contain
- * an allow list.
- * @param chosen_credential_out Will be filled with a pointer to the chosen credential,
- * or NULL if no key was found.
- * @param chosen_privkey Will be filled with the the private key corresponding to chosen_credential.
- * Must be at least HMAC_SHA256_LEN bytes wide.
- */
-static void _authenticate_with_allow_list(ctap_get_assertion_req_t* GA, u2f_keyhandle_t** chosen_credential_out, uint8_t* chosen_privkey)
+
+static void save_credential_list(CTAP_authDataHeader * head, uint8_t * clientDataHash, CTAP_credentialDescriptor * creds, uint32_t count)
{
- /*
- * We can just pick the first credential that we're able to authenticate with.
- * No need to ask the user to select one if many credentials match.
- * See Client to Authenticator Protocol, 5.2, point 9.
- */
- for (int i = 0; i < GA->cred_len; i++) {
- u2f_keyhandle_t* this_key = GA->creds + i;
- bool key_valid = u2f_keyhandle_verify(GA->rp.id, (uint8_t*)this_key, sizeof(*this_key), chosen_privkey);
- if (key_valid) {
- /* Found an applicable credential. */
- *chosen_credential_out = this_key;
- return;
+ if(count)
+ {
+ if (count > ALLOW_LIST_MAX_SIZE-1)
+ {
+ printf2(TAG_ERR, "ALLOW_LIST_MAX_SIZE Exceeded\n");
+ exit(1);
}
+ memmove(getAssertionState.clientDataHash, clientDataHash, CLIENT_DATA_HASH_SIZE);
+ memmove(&getAssertionState.authData, head, sizeof(CTAP_authDataHeader));
+ memmove(getAssertionState.creds, creds, sizeof(CTAP_credentialDescriptor) * (count));
+
}
- /* No keys were found. */
- util_zero(chosen_privkey, HMAC_SHA256_LEN);
- *chosen_credential_out = NULL;
+ getAssertionState.count = count;
+ printf1(TAG_GA,"saved %d credentials\n",count);
}
-/**
- * Called when the user has selected one of the credentials from
- * the available credential list for an authentication request.
- *
- * This function will decode the selected authentication key and
- * move to the CTAP_GET_ASSERTION_SELECTED_CREDENTIAL state.
- */
-static void _auth_credential_selected(int selected_cred, void* param)
+static CTAP_credentialDescriptor * pop_credential()
{
- (void)param;
- ctap_get_assertion_state_t* state = &_state.data.get_assertion;
-
- if (selected_cred < 0) {
- /* User aborted. */
- state->state = CTAP_GET_ASSERTION_DENIED;
- return;
- }
-
- /* Now load the credential that was selected in the output buffer. */
- ctap_resident_key_t selected_key;
- //screen_sprintf_debug(500, "Selected cred #%d", cred_idx[selected_cred]);
- bool mem_result = memory_get_ctap_resident_key(state->cred_list.creds[selected_cred].mem_id, &selected_key);
-
- if (!mem_result) {
- /* Shouldn't happen, but if it does we effectively don't have any valid credential to provide. */
- state->state = CTAP_GET_ASSERTION_NO_CREDENTIALS;
- return;
- }
- /* Sanity check the stored credential. */
- if (selected_key.valid != CTAP_RESIDENT_KEY_VALID ||
- selected_key.user_id_size > CTAP_USER_ID_MAX_SIZE) {
- state->state = CTAP_GET_ASSERTION_NO_CREDENTIALS;
- return;
- }
- memcpy(&state->auth_credential, &selected_key.key_handle, sizeof(selected_key.key_handle));
- state->user_id_size = selected_key.user_id_size;
- memcpy(state->user_id, selected_key.user_id, state->user_id_size);
-
- /* Sanity check the key and extract the private key. */
- bool key_valid = u2f_keyhandle_verify(state->req.rp.id, (const uint8_t*)&state->auth_credential, sizeof(state->auth_credential), state->auth_privkey);
- if (!key_valid) {
- workflow_status_blocking("Internal error. Keyhandle verification failed.", false);
- state->state = CTAP_GET_ASSERTION_NO_CREDENTIALS;
- return;
- }
- state->state = CTAP_GET_ASSERTION_SELECTED_CREDENTIAL;
+ if (getAssertionState.count > 0)
+ {
+ getAssertionState.count--;
+ return &getAssertionState.creds[getAssertionState.count];
+ }
+ else
+ {
+ return NULL;
+ }
}
-
-/**
- * Selects one of the stored credentials for authentication.
- *
- * @param GA getAssertion request that must be examined. Must contain
- * an allow list.
- * @param chosen_credential_out Will be filled with the chosen credential.
- * @param chosen_privkey Will be filled with the the private key corresponding to chosen_credential.
- * Must be at least HMAC_SHA256_LEN bytes wide.
- * @param user_id_out Will be filled with the stored User ID corresponding to the
- * chosen credential. Must be CTAP_STORAGE_USER_NAME_LIMIT bytes long.
- * @param user_id_size_out Will be filled with the size of user_id.
- *
- * @return true if authentication was successful (operation should continue), false otherwise.
- */
-static bool _authenticate_with_rk(ctap_get_assertion_req_t* GA)
+// adds 2 to map, or 3 if add_user is true
+uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cred, uint8_t * auth_data_buf, unsigned int auth_data_buf_sz, uint8_t * clientDataHash)
{
- ctap_get_assertion_state_t* state = &_state.data.get_assertion;
- state->cred_list.n_elems = 0;
-
- /*
- * Compute the hash of the RP id so that we
- * can match it against the keys we have in memory.
- */
- uint8_t rp_id_hash[SHA256_LEN];
- _compute_rpid_hash(&GA->rp, rp_id_hash);
- /* Check all keys that match this RP. */
- for (int i = 0; i < MEMORY_CTAP_RESIDENT_KEYS_MAX; i++) {
- ctap_resident_key_t this_key;
- bool mem_result = memory_get_ctap_resident_key(i, &this_key);
- if (!mem_result || this_key.valid != CTAP_RESIDENT_KEY_VALID) {
- continue;
- }
- if (!memcmp(this_key.rp_id_hash, rp_id_hash, SHA256_LEN)) {
- /*
- * This key matches the RP! Add its user information to
- * our list.
- */
- ctap_credential_display_t* this_cred = state->cred_list.creds + state->cred_list.n_elems;
- this_cred->mem_id = i;
- memcpy(this_cred->username, this_key.user_name, sizeof(this_key.user_name));
- memcpy(this_cred->display_name, this_key.display_name, sizeof(this_key.display_name));
- state->cred_list.n_elems++;
- if (state->cred_list.n_elems == CTAP_CREDENTIAL_LIST_MAX_SIZE) {
- /* No more space */
- break;
- }
- }
- }
- if (state->cred_list.n_elems == 0) {
- return false;
- }
- /* Sort credentials by creation time. */
- qsort(state->cred_list.creds, state->cred_list.n_elems, sizeof(*state->cred_list.creds), _compare_display_credentials);
- if (state->cred_list.n_elems > 1) {
- workflow_t* wf = workflow_select_ctap_credential(&state->cred_list, _auth_credential_selected, NULL);
- workflow_stack_start_workflow(wf);
- state->state = CTAP_GET_ASSERTION_SELECT_CREDENTIAL;
- } else {
- _auth_credential_selected(0, NULL);
+ int ret;
+ uint8_t sigbuf[64];
+ uint8_t sigder[72];
+ int sigder_sz;
+
+ ret = ctap_add_credential_descriptor(map, cred); // 1
+ check_retr(ret);
+
+ {
+ ret = cbor_encode_int(map,RESP_authData); // 2
+ check_ret(ret);
+ ret = cbor_encode_byte_string(map, auth_data_buf, auth_data_buf_sz);
+ check_ret(ret);
}
- return true;
-}
-/**
- * @param auth_data_buf Must be at least sizeof(ctap_auth_data_t) bytes wide.
- * @param data_buf_len_out Will be filled with the actual auth data size.
- */
-static uint8_t _make_authentication_response(ctap_get_assertion_req_t* GA, uint8_t* auth_data_buf, size_t* data_buf_len_out) {
- ctap_auth_data_header_t* auth_data_header = (ctap_auth_data_header_t*)auth_data_buf;
+ unsigned int cred_size = get_credential_id_size(cred);
+ crypto_ecc256_load_key((uint8_t*)&cred->credential.id, cred_size, NULL, 0);
- auth_data_header->flags = 0;
- if (GA->up) {
- auth_data_header->flags |= CTAP_AUTH_DATA_FLAG_USER_PRESENT; // User presence
+#ifdef ENABLE_U2F_EXTENSIONS
+ if ( extend_fido2(&cred->credential.id, sigder) )
+ {
+ sigder_sz = 72;
}
- if (GA->uv) {
- auth_data_header->flags |= CTAP_AUTH_DATA_FLAG_USER_VERIFIED; // User presence
+ else
+#endif
+ {
+ sigder_sz = ctap_calculate_signature(auth_data_buf, auth_data_buf_sz, clientDataHash, auth_data_buf, sigbuf, sigder);
}
- _compute_rpid_hash(&GA->rp, auth_data_header->rp_id_hash);
+ {
+ ret = cbor_encode_int(map, RESP_signature); // 3
+ check_ret(ret);
+ ret = cbor_encode_byte_string(map, sigder, sigder_sz);
+ check_ret(ret);
+ }
- /* Update the U2F counter. */
- uint32_t u2f_counter;
- if (!securechip_u2f_counter_inc(&u2f_counter)) {
- return CTAP2_ERR_OPERATION_DENIED;
+ if (cred->credential.user.id_size)
+ {
+ printf1(TAG_GREEN, "adding user details to output\r\n");
+ ret = ctap_add_user_entity(map, &cred->credential.user); // 4
+ check_retr(ret);
}
- _encode_u2f_counter(u2f_counter, (uint8_t*)&auth_data_header->signCount);
- uint32_t actual_auth_data_size = sizeof(ctap_auth_data_header_t);
- /* FUTURE: manage extensions if we want to. */
- *data_buf_len_out = actual_auth_data_size;
- return CTAP1_ERR_SUCCESS;
+ return 0;
}
-static void _get_assertion_init_state(ctap_get_assertion_req_t* req)
+uint8_t ctap_get_next_assertion(CborEncoder * encoder)
{
- _state.data.get_assertion.state = CTAP_GET_ASSERTION_STARTED;
- memcpy(&_state.data.get_assertion.req, req, sizeof(*req));
-}
+ int ret;
+ CborEncoder map;
+ CTAP_authDataHeader authData;
+ memmove(&authData, &getAssertionState.authData, sizeof(CTAP_authDataHeader));
-static void _get_assertion_free_state(void)
-{
- util_zero(_state.data.get_assertion.auth_privkey,
- sizeof(_state.data.get_assertion.auth_privkey)
- );
+ CTAP_credentialDescriptor * cred = pop_credential();
+
+ if (cred == NULL)
+ {
+ return CTAP2_ERR_NOT_ALLOWED;
+ }
+
+ auth_data_update_count(&authData);
+
+ if (cred->credential.user.id_size)
+ {
+ printf1(TAG_GREEN, "adding user info to assertion response\r\n");
+ ret = cbor_encoder_create_map(encoder, &map, 4);
+ }
+ else
+ {
+ printf1(TAG_GREEN, "NOT adding user info to assertion response\r\n");
+ ret = cbor_encoder_create_map(encoder, &map, 3);
+ }
+
+ check_ret(ret);
+
+ // if only one account for this RP, null out the user details
+ if (!getAssertionState.user_verified)
+ {
+ printf1(TAG_GREEN, "Not verified, nulling out user details on response\r\n");
+ memset(cred->credential.user.name, 0, USER_NAME_LIMIT);
+ }
+
+ ret = ctap_end_get_assertion(&map, cred, (uint8_t *)&authData, sizeof(CTAP_authDataHeader), getAssertionState.clientDataHash);
+ check_retr(ret);
+
+ ret = cbor_encoder_close_container(encoder, &map);
+ check_ret(ret);
+
+ return 0;
}
-static uint8_t ctap_get_assertion(const in_buffer_t* in_buffer)
+uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
{
- ctap_get_assertion_req_t req;
+ CTAP_getAssertion GA;
- int ret = ctap_parse_get_assertion(&req, in_buffer);
+ uint8_t auth_data_buf[sizeof(CTAP_authDataHeader) + 80];
+ int ret = ctap_parse_get_assertion(&GA,request,length);
- if (ret != 0) {
+ if (ret != 0)
+ {
+ printf2(TAG_ERR,"error, parse_get_assertion failed\n");
return ret;
}
- if (req.pin_auth_empty) {
- bool result = _ask_generic_authorization();
- if (!result) {
- return CTAP2_ERR_OPERATION_DENIED;
- }
- return CTAP2_ERR_PIN_NOT_SET;
+ if (GA.pinAuthEmpty)
+ {
+ check_retr( ctap2_user_presence_test(CTAP2_UP_DELAY_MS) );
+ return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_AUTH_INVALID : CTAP2_ERR_PIN_NOT_SET;
}
- if (req.pin_auth_present) {
- /* We don't support pin_auth. */
- return CTAP2_ERR_PIN_AUTH_INVALID;
+ if (GA.pinAuthPresent)
+ {
+ ret = verify_pin_auth(GA.pinAuth, GA.clientDataHash);
+ check_retr(ret);
+ getAssertionState.user_verified = 1;
+ }
+ else
+ {
+ getAssertionState.user_verified = 0;
}
- if (!req.rp.size || !req.client_data_hash_present) {
- /* Both parameters are mandatory. */
+ if (!GA.rp.size || !GA.clientDataHashPresent)
+ {
return CTAP2_ERR_MISSING_PARAMETER;
}
+ CborEncoder map;
- /*
- * Ask the user to confirm that he wants to authenticate.
- * This must be done before we check for credentials so that
- * we don't disclose the existance of credentials before the
- * user has proven his identity (See 5.2, point 7).
- */
- _get_assertion_init_state(&req);
- workflow_stack_start_workflow(workflow_unlock(_get_assertion_unlock_cb, NULL));
- return CTAP1_ERR_SUCCESS;
-}
+ int map_size = 3;
-/**
- * Generates a new assertion in response to a GetAssertion request.
- * Only called when the user has already accepted and identified with the device.
- */
-static ctap_request_result_t _get_assertion_select_credential(void)
-{
- ctap_get_assertion_state_t* state = &_state.data.get_assertion;
-
- if (state->req.cred_len) {
- // allowlist is present -> check all the credentials that were actually generated by us.
- u2f_keyhandle_t* chosen_credential = NULL;
- _authenticate_with_allow_list(&state->req, &chosen_credential, state->auth_privkey);
- if (!chosen_credential) {
- /* No credential selected (or no credential was known to the device). */
- ctap_request_result_t result = {.status = CTAP2_ERR_NO_CREDENTIALS, .request_completed = true};
- return result;
- }
- memcpy(&state->auth_credential, chosen_credential, sizeof(state->auth_credential));
- state->state = CTAP_GET_ASSERTION_SELECTED_CREDENTIAL;
- } else {
- // No allowList, so use all matching RK's matching rpId
- bool rk_result = _authenticate_with_rk(&state->req);
- if (!rk_result) {
- ctap_request_result_t result = {.status = CTAP2_ERR_NO_CREDENTIALS, .request_completed = true};
- return result;
- }
- }
- ctap_request_result_t result = {.status = 0, .request_completed = false};
- return result;
-}
+ printf1(TAG_GA, "ALLOW_LIST has %d creds\n", GA.credLen);
+ int validCredCount = ctap_filter_invalid_credentials(&GA);
-static uint8_t _get_assertion_complete(buffer_t* out_buf)
-{
- size_t actual_auth_data_size;
- uint8_t auth_data_buf[sizeof(ctap_auth_data_header_t) + 80];
- ctap_get_assertion_state_t* state = &_state.data.get_assertion;
- uint8_t ret = _make_authentication_response(&state->req, auth_data_buf, &actual_auth_data_size);
- if (ret != CborNoError) {
- return ret;
+ if (validCredCount == 0)
+ {
+ printf2(TAG_ERR,"Error, no authentic credential\n");
+ return CTAP2_ERR_NO_CREDENTIALS;
+ }
+ else if (validCredCount > 1)
+ {
+ map_size += 1;
}
- /* Encode the resulting assertion in the output buffer. */
- CborEncoder encoder;
- memset(&encoder, 0, sizeof(CborEncoder));
- cbor_encoder_init(&encoder, out_buf->data, out_buf->max_len, 0);
- ret = ctap_end_get_assertion(&encoder, &state->auth_credential, auth_data_buf, actual_auth_data_size, state->auth_privkey, state->req.client_data_hash, state->user_id, state->user_id_size);
- if (ret != CborNoError) {
- return ret;
+
+ if (GA.creds[validCredCount - 1].credential.user.id_size)
+ {
+ map_size += 1;
+ }
+ if (GA.extensions.hmac_secret_present == EXT_HMAC_SECRET_PARSED)
+ {
+ printf1(TAG_GA, "hmac-secret is present\r\n");
}
- out_buf->len = cbor_encoder_get_buffer_size(&encoder, out_buf->data);
+ ret = cbor_encoder_create_map(encoder, &map, map_size);
+ check_ret(ret);
- return CTAP1_ERR_SUCCESS;
-}
+ // if only one account for this RP, null out the user details
+ if (validCredCount < 2 || !getAssertionState.user_verified)
+ {
+ printf1(TAG_GREEN, "Only one account, nulling out user details on response\r\n");
+ memset(&GA.creds[0].credential.user.name, 0, USER_NAME_LIMIT);
+ }
-static ctap_request_result_t _get_assertion_continue(buffer_t* out_buf)
-{
- ctap_request_result_t result = {.status = 0, .request_completed = true};
- ctap_get_assertion_state_t* state = &_state.data.get_assertion;
- switch (state->state) {
- case CTAP_GET_ASSERTION_CONFIRMED:
- result = _get_assertion_select_credential();
- return result;
- case CTAP_GET_ASSERTION_DENIED:
- result.status = CTAP2_ERR_OPERATION_DENIED;
- return result;
- case CTAP_GET_ASSERTION_NO_CREDENTIALS:
- workflow_status_blocking("No credentials found on this device.", false);
- result.status = CTAP2_ERR_NO_CREDENTIALS;
- return result;
- case CTAP_GET_ASSERTION_UNLOCKED:
- /*
- * Request permission to the user.
- * This must be done before checking for excluded credentials etc.
- * so that we don't reveal the existance of credentials without
- * the user's consent.
- */
- workflow_stack_start_workflow(_get_assertion_confirm(&_state.data.get_assertion.req.rp));
- state->state = CTAP_MAKE_CREDENTIAL_WAIT_CONFIRM;
- result.request_completed = false;
- return result;
- case CTAP_GET_ASSERTION_SELECTED_CREDENTIAL:
- result.status = _get_assertion_complete(out_buf);
- return result;
- case CTAP_GET_ASSERTION_STARTED:
- case CTAP_GET_ASSERTION_WAIT_CONFIRM:
- case CTAP_GET_ASSERTION_SELECT_CREDENTIAL:
- result.request_completed = false;
- return result;
- default:
- Abort("Invalid get_assertion state.");
+ printf1(TAG_GA,"resulting order of creds:\n");
+ int j;
+ for (j = 0; j < GA.credLen; j++)
+ {
+ printf1(TAG_GA,"CRED ID (# %d)\n", GA.creds[j].credential.id.count);
}
-}
-void ctap_response_init(ctap_response_t* resp)
-{
- memset(resp, 0, sizeof(*resp));
- resp->data_size = CTAP_RESPONSE_BUFFER_SIZE;
-}
+ CTAP_credentialDescriptor * cred = &GA.creds[validCredCount - 1];
-ctap_request_result_t ctap_request(const in_buffer_t* in_buf, buffer_t* out_buf)
-{
- CborEncoder encoder;
- memset(&encoder,0,sizeof(CborEncoder));
+ GA.extensions.hmac_secret.credential = &cred->credential;
- uint8_t cmd = *in_buf->data;
- in_buffer_t in_req_data = {
- .data = in_buf->data + 1,
- .len = in_buf->len - 1
- };
+ uint32_t auth_data_buf_sz = sizeof(auth_data_buf);
- cbor_encoder_init(&encoder, out_buf->data, out_buf->max_len, 0);
- ctap_request_result_t result = {.status = 0, .request_completed = true};
+#ifdef ENABLE_U2F_EXTENSIONS
+ if ( is_extension_request((uint8_t*)&GA.creds[validCredCount - 1].credential.id, sizeof(CredentialId)) )
+ {
+ auth_data_buf_sz = sizeof(CTAP_authDataHeader);
- switch(cmd)
+ crypto_sha256_init();
+ crypto_sha256_update(GA.rp.id, GA.rp.size);
+ crypto_sha256_final(((CTAP_authDataHeader *)auth_data_buf)->rpIdHash);
+
+ ((CTAP_authDataHeader *)auth_data_buf)->flags = (1 << 0);
+ ((CTAP_authDataHeader *)auth_data_buf)->flags |= (1 << 2);
+ }
+ else
+#endif
{
- case CTAP_REQ_MAKE_CREDENTIAL:
- result.status = ctap_make_credential(&encoder, &in_req_data);
- if (result.status == CTAP1_ERR_SUCCESS) {
- /* MakeCredential started successfully, don't reply yet. */
- _state.blocking_op = CTAP_BLOCKING_OP_MAKE_CRED;
- result.request_completed = false;
- }
- break;
- case CTAP_REQ_GET_ASSERTION:
- result.status = ctap_get_assertion(&in_req_data);
- if (result.status == CTAP1_ERR_SUCCESS) {
- _state.blocking_op = CTAP_BLOCKING_OP_GET_ASSERTION;
- result.request_completed = false;
+ device_disable_up(GA.up == 0);
+ ret = ctap_make_auth_data(&GA.rp, &map, auth_data_buf, &auth_data_buf_sz, NULL);
+ device_disable_up(false);
+ check_retr(ret);
+
+ ((CTAP_authDataHeader *)auth_data_buf)->flags &= ~(1 << 2);
+ ((CTAP_authDataHeader *)auth_data_buf)->flags |= (getAssertionState.user_verified << 2);
+
+ {
+ unsigned int ext_encoder_buf_size = sizeof(auth_data_buf) - auth_data_buf_sz;
+ uint8_t * ext_encoder_buf = auth_data_buf + auth_data_buf_sz;
+
+ ret = ctap_make_extensions(&GA.extensions, ext_encoder_buf, &ext_encoder_buf_size);
+ check_retr(ret);
+ if (ext_encoder_buf_size)
+ {
+ ((CTAP_authDataHeader *)auth_data_buf)->flags |= (1 << 7);
+ auth_data_buf_sz += ext_encoder_buf_size;
}
- break;
- case CTAP_REQ_CANCEL:
- break;
- case CTAP_REQ_GET_INFO:
- result.status = ctap_get_info(&encoder);
- out_buf->len = cbor_encoder_get_buffer_size(&encoder, out_buf->data);
- break;
- case CTAP_REQ_CLIENT_PIN:
- case CTAP_REQ_RESET:
- case CTAP_REQ_GET_NEXT_ASSERTION:
- result.status = CTAP2_ERR_NOT_ALLOWED;
- break;
- default:
- result.status = CTAP1_ERR_INVALID_COMMAND;
+ }
+
}
- if (result.status != CTAP1_ERR_SUCCESS || !result.request_completed) {
- out_buf->len = 0;
+ save_credential_list((CTAP_authDataHeader*)auth_data_buf, GA.clientDataHash, GA.creds, validCredCount-1); // skip last one
+
+ ret = ctap_end_get_assertion(&map, cred, auth_data_buf, auth_data_buf_sz, GA.clientDataHash); // 1,2,3,4
+ check_retr(ret);
+
+ if (validCredCount > 1)
+ {
+ ret = cbor_encode_int(&map, RESP_numberOfCredentials); // 5
+ check_ret(ret);
+ ret = cbor_encode_int(&map, validCredCount);
+ check_ret(ret);
}
- return result;
+
+ ret = cbor_encoder_close_container(encoder, &map);
+ check_ret(ret);
+
+ return 0;
}
-ctap_request_result_t ctap_retry(buffer_t* out_buf)
+// Return how many trailing zeros in a buffer
+static int trailing_zeros(uint8_t * buf, int indx)
{
- ctap_request_result_t result = {.status = 0, .request_completed = true};
-
- switch (_state.blocking_op) {
- case CTAP_BLOCKING_OP_MAKE_CRED:
- result = _make_credential_continue(out_buf);
- if (result.request_completed) {
- _state.blocking_op = CTAP_BLOCKING_OP_NONE;
- _make_credential_free_state();
+ int c = 0;
+ while(0==buf[indx] && indx)
+ {
+ indx--;
+ c++;
+ }
+ return c;
+}
+
+uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platform_pubkey, uint8_t * pinAuth, uint8_t * pinHashEnc)
+{
+ uint8_t shared_secret[32];
+ uint8_t hmac[32];
+ int ret;
+
+// Validate incoming data packet len
+ if (len < 64)
+ {
+ return CTAP1_ERR_OTHER;
+ }
+
+// Validate device's state
+ if (ctap_is_pin_set()) // Check first, prevent SCA
+ {
+ if (ctap_device_locked())
+ {
+ return CTAP2_ERR_PIN_BLOCKED;
+ }
+ if (ctap_device_boot_locked())
+ {
+ return CTAP2_ERR_PIN_AUTH_BLOCKED;
+ }
+ }
+
+// calculate shared_secret
+ crypto_ecc256_shared_secret(platform_pubkey, KEY_AGREEMENT_PRIV, shared_secret);
+
+ crypto_sha256_init();
+ crypto_sha256_update(shared_secret, 32);
+ crypto_sha256_final(shared_secret);
+
+ crypto_sha256_hmac_init(shared_secret, 32, hmac);
+ crypto_sha256_update(pinEnc, len);
+ if (pinHashEnc != NULL)
+ {
+ crypto_sha256_update(pinHashEnc, 16);
+ }
+ crypto_sha256_hmac_final(shared_secret, 32, hmac);
+
+ if (memcmp(hmac, pinAuth, 16) != 0)
+ {
+ printf2(TAG_ERR,"pinAuth failed for update pin\n");
+ dump_hex1(TAG_ERR, hmac,16);
+ dump_hex1(TAG_ERR, pinAuth,16);
+ return CTAP2_ERR_PIN_AUTH_INVALID;
+ }
+
+// decrypt new PIN with shared secret
+ crypto_aes256_init(shared_secret, NULL);
+
+ while((len & 0xf) != 0) // round up to nearest AES block size multiple
+ {
+ len++;
+ }
+
+ crypto_aes256_decrypt(pinEnc, len);
+
+// validate new PIN (length)
+
+ ret = trailing_zeros(pinEnc, NEW_PIN_ENC_MIN_SIZE - 1);
+ ret = NEW_PIN_ENC_MIN_SIZE - ret;
+
+ if (ret < NEW_PIN_MIN_SIZE || ret >= NEW_PIN_MAX_SIZE)
+ {
+ printf2(TAG_ERR,"new PIN is too short or too long [%d bytes]\n", ret);
+ return CTAP2_ERR_PIN_POLICY_VIOLATION;
+ }
+ else
+ {
+ printf1(TAG_CP,"new pin: %s [%d bytes]\n", pinEnc, ret);
+ dump_hex1(TAG_CP, pinEnc, ret);
+ }
+
+// validate device's state, decrypt and compare pinHashEnc (user provided current PIN hash) with stored PIN_CODE_HASH
+
+ if (ctap_is_pin_set())
+ {
+ if (ctap_device_locked())
+ {
+ return CTAP2_ERR_PIN_BLOCKED;
+ }
+ if (ctap_device_boot_locked())
+ {
+ return CTAP2_ERR_PIN_AUTH_BLOCKED;
+ }
+ crypto_aes256_reset_iv(NULL);
+ crypto_aes256_decrypt(pinHashEnc, 16);
+
+ uint8_t pinHashEncSalted[32];
+ crypto_sha256_init();
+ crypto_sha256_update(pinHashEnc, 16);
+ crypto_sha256_update(STATE.PIN_SALT, sizeof(STATE.PIN_SALT));
+ crypto_sha256_final(pinHashEncSalted);
+
+ if (memcmp(pinHashEncSalted, STATE.PIN_CODE_HASH, 16) != 0)
+ {
+ ctap_reset_key_agreement();
+ ctap_decrement_pin_attempts();
+ if (ctap_device_boot_locked())
+ {
+ return CTAP2_ERR_PIN_AUTH_BLOCKED;
+ }
+ return CTAP2_ERR_PIN_INVALID;
+ }
+ else
+ {
+ ctap_reset_pin_attempts();
+ }
+ }
+
+// set new PIN (update and store PIN_CODE_HASH)
+ ctap_update_pin(pinEnc, ret);
+
+ return 0;
+}
+
+uint8_t ctap_add_pin_if_verified(uint8_t * pinTokenEnc, uint8_t * platform_pubkey, uint8_t * pinHashEnc)
+{
+ uint8_t shared_secret[32];
+
+ crypto_ecc256_shared_secret(platform_pubkey, KEY_AGREEMENT_PRIV, shared_secret);
+
+ crypto_sha256_init();
+ crypto_sha256_update(shared_secret, 32);
+ crypto_sha256_final(shared_secret);
+
+ crypto_aes256_init(shared_secret, NULL);
+
+ crypto_aes256_decrypt(pinHashEnc, 16);
+
+ uint8_t pinHashEncSalted[32];
+ crypto_sha256_init();
+ crypto_sha256_update(pinHashEnc, 16);
+ crypto_sha256_update(STATE.PIN_SALT, sizeof(STATE.PIN_SALT));
+ crypto_sha256_final(pinHashEncSalted);
+ if (memcmp(pinHashEncSalted, STATE.PIN_CODE_HASH, 16) != 0)
+ {
+ printf2(TAG_ERR,"Pin does not match!\n");
+ printf2(TAG_ERR,"platform-pin-hash: "); dump_hex1(TAG_ERR, pinHashEnc, 16);
+ printf2(TAG_ERR,"authentic-pin-hash: "); dump_hex1(TAG_ERR, STATE.PIN_CODE_HASH, 16);
+ printf2(TAG_ERR,"shared-secret: "); dump_hex1(TAG_ERR, shared_secret, 32);
+ printf2(TAG_ERR,"platform-pubkey: "); dump_hex1(TAG_ERR, platform_pubkey, 64);
+ printf2(TAG_ERR,"device-pubkey: "); dump_hex1(TAG_ERR, KEY_AGREEMENT_PUB, 64);
+ // Generate new keyAgreement pair
+ ctap_reset_key_agreement();
+ ctap_decrement_pin_attempts();
+ if (ctap_device_boot_locked())
+ {
+ return CTAP2_ERR_PIN_AUTH_BLOCKED;
+ }
+ return CTAP2_ERR_PIN_INVALID;
+ }
+
+ ctap_reset_pin_attempts();
+ crypto_aes256_reset_iv(NULL);
+
+ memmove(pinTokenEnc, PIN_TOKEN, PIN_TOKEN_SIZE);
+ crypto_aes256_encrypt(pinTokenEnc, PIN_TOKEN_SIZE);
+
+ return 0;
+}
+
+uint8_t ctap_client_pin(CborEncoder * encoder, uint8_t * request, int length)
+{
+ CTAP_clientPin CP;
+ CborEncoder map;
+ uint8_t pinTokenEnc[PIN_TOKEN_SIZE];
+ int ret = ctap_parse_client_pin(&CP,request,length);
+
+
+ switch(CP.subCommand)
+ {
+ case CP_cmdSetPin:
+ case CP_cmdChangePin:
+ case CP_cmdGetPinToken:
+ if (ctap_device_locked())
+ {
+ return CTAP2_ERR_PIN_BLOCKED;
+ }
+ if (ctap_device_boot_locked())
+ {
+ return CTAP2_ERR_PIN_AUTH_BLOCKED;
+ }
+ }
+
+ if (ret != 0)
+ {
+ printf2(TAG_ERR,"error, parse_client_pin failed\n");
+ return ret;
+ }
+
+ if (CP.pinProtocol != 1 || CP.subCommand == 0)
+ {
+ return CTAP1_ERR_OTHER;
+ }
+
+ int num_map = (CP.getRetries ? 1 : 0);
+
+ switch(CP.subCommand)
+ {
+ case CP_cmdGetRetries:
+ printf1(TAG_CP,"CP_cmdGetRetries\n");
+ ret = cbor_encoder_create_map(encoder, &map, 1);
+ check_ret(ret);
+
+ CP.getRetries = 1;
+
+ break;
+ case CP_cmdGetKeyAgreement:
+ printf1(TAG_CP,"CP_cmdGetKeyAgreement\n");
+ num_map++;
+ ret = cbor_encoder_create_map(encoder, &map, num_map);
+ check_ret(ret);
+
+ ret = cbor_encode_int(&map, RESP_keyAgreement);
+ check_ret(ret);
+
+ if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_FAST);
+ crypto_ecc256_compute_public_key(KEY_AGREEMENT_PRIV, KEY_AGREEMENT_PUB);
+ if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
+
+ ret = ctap_add_cose_key(&map, KEY_AGREEMENT_PUB, KEY_AGREEMENT_PUB+32, PUB_KEY_CRED_PUB_KEY, COSE_ALG_ECDH_ES_HKDF_256);
+ check_retr(ret);
+
+ break;
+ case CP_cmdSetPin:
+ printf1(TAG_CP,"CP_cmdSetPin\n");
+
+ if (ctap_is_pin_set())
+ {
+ return CTAP2_ERR_NOT_ALLOWED;
+ }
+ if (!CP.newPinEncSize || !CP.pinAuthPresent || !CP.keyAgreementPresent)
+ {
+ return CTAP2_ERR_MISSING_PARAMETER;
+ }
+
+ ret = ctap_update_pin_if_verified(CP.newPinEnc, CP.newPinEncSize, (uint8_t*)&CP.keyAgreement.pubkey, CP.pinAuth, NULL);
+ check_retr(ret);
+ break;
+ case CP_cmdChangePin:
+ printf1(TAG_CP,"CP_cmdChangePin\n");
+
+ if (! ctap_is_pin_set())
+ {
+ return CTAP2_ERR_PIN_NOT_SET;
+ }
+
+ if (!CP.newPinEncSize || !CP.pinAuthPresent || !CP.keyAgreementPresent || !CP.pinHashEncPresent)
+ {
+ return CTAP2_ERR_MISSING_PARAMETER;
+ }
+
+ ret = ctap_update_pin_if_verified(CP.newPinEnc, CP.newPinEncSize, (uint8_t*)&CP.keyAgreement.pubkey, CP.pinAuth, CP.pinHashEnc);
+ check_retr(ret);
+ break;
+ case CP_cmdGetPinToken:
+ if (!ctap_is_pin_set())
+ {
+ return CTAP2_ERR_PIN_NOT_SET;
+ }
+ num_map++;
+ ret = cbor_encoder_create_map(encoder, &map, num_map);
+ check_ret(ret);
+
+ printf1(TAG_CP,"CP_cmdGetPinToken\n");
+ if (CP.keyAgreementPresent == 0 || CP.pinHashEncPresent == 0)
+ {
+ printf2(TAG_ERR,"Error, missing keyAgreement or pinHashEnc for cmdGetPin\n");
+ return CTAP2_ERR_MISSING_PARAMETER;
+ }
+ ret = cbor_encode_int(&map, RESP_pinToken);
+ check_ret(ret);
+
+ /*ret = ctap_add_pin_if_verified(&map, (uint8_t*)&CP.keyAgreement.pubkey, CP.pinHashEnc);*/
+ ret = ctap_add_pin_if_verified(pinTokenEnc, (uint8_t*)&CP.keyAgreement.pubkey, CP.pinHashEnc);
+ check_retr(ret);
+
+ ret = cbor_encode_byte_string(&map, pinTokenEnc, PIN_TOKEN_SIZE);
+ check_ret(ret);
+
+
+
+ break;
+
+ default:
+ printf2(TAG_ERR,"Error, invalid client pin subcommand\n");
+ return CTAP1_ERR_OTHER;
+ }
+
+ if (CP.getRetries)
+ {
+ ret = cbor_encode_int(&map, RESP_retries);
+ check_ret(ret);
+ ret = cbor_encode_int(&map, ctap_leftover_pin_attempts());
+ check_ret(ret);
+ }
+
+ if (num_map || CP.getRetries)
+ {
+ ret = cbor_encoder_close_container(encoder, &map);
+ check_ret(ret);
+ }
+
+ return 0;
+}
+
+void ctap_response_init(CTAP_RESPONSE * resp)
+{
+ memset(resp, 0, sizeof(CTAP_RESPONSE));
+ resp->data_size = CTAP_RESPONSE_BUFFER_SIZE;
+}
+
+
+uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
+{
+ CborEncoder encoder;
+ memset(&encoder,0,sizeof(CborEncoder));
+ uint8_t status = 0;
+ uint8_t cmd = *pkt_raw;
+ pkt_raw++;
+ length--;
+
+ uint8_t * buf = resp->data;
+
+ cbor_encoder_init(&encoder, buf, resp->data_size, 0);
+
+ printf1(TAG_CTAP,"cbor input structure: %d bytes\n", length);
+ printf1(TAG_DUMP,"cbor req: "); dump_hex1(TAG_DUMP, pkt_raw, length);
+
+ switch(cmd)
+ {
+ case CTAP_MAKE_CREDENTIAL:
+ case CTAP_GET_ASSERTION:
+ if (ctap_device_locked())
+ {
+ status = CTAP2_ERR_PIN_BLOCKED;
+ goto done;
+ }
+ if (ctap_device_boot_locked())
+ {
+ status = CTAP2_ERR_PIN_AUTH_BLOCKED;
+ goto done;
+ }
+ break;
+ }
+
+ switch(cmd)
+ {
+ case CTAP_MAKE_CREDENTIAL:
+ printf1(TAG_CTAP,"CTAP_MAKE_CREDENTIAL\n");
+ timestamp();
+ status = ctap_make_credential(&encoder, pkt_raw, length);
+ printf1(TAG_TIME,"make_credential time: %d ms\n", timestamp());
+
+ resp->length = cbor_encoder_get_buffer_size(&encoder, buf);
+ dump_hex1(TAG_DUMP, buf, resp->length);
+
+ break;
+ case CTAP_GET_ASSERTION:
+ printf1(TAG_CTAP,"CTAP_GET_ASSERTION\n");
+ timestamp();
+ status = ctap_get_assertion(&encoder, pkt_raw, length);
+ printf1(TAG_TIME,"get_assertion time: %d ms\n", timestamp());
+
+ resp->length = cbor_encoder_get_buffer_size(&encoder, buf);
+
+ printf1(TAG_DUMP,"cbor [%d]: \n", resp->length);
+ dump_hex1(TAG_DUMP,buf, resp->length);
+ break;
+ case CTAP_CANCEL:
+ printf1(TAG_CTAP,"CTAP_CANCEL\n");
+ break;
+ case CTAP_GET_INFO:
+ printf1(TAG_CTAP,"CTAP_GET_INFO\n");
+ status = ctap_get_info(&encoder);
+
+ resp->length = cbor_encoder_get_buffer_size(&encoder, buf);
+
+ dump_hex1(TAG_DUMP, buf, resp->length);
+
+ break;
+ case CTAP_CLIENT_PIN:
+ printf1(TAG_CTAP,"CTAP_CLIENT_PIN\n");
+ status = ctap_client_pin(&encoder, pkt_raw, length);
+
+ resp->length = cbor_encoder_get_buffer_size(&encoder, buf);
+ dump_hex1(TAG_DUMP, buf, resp->length);
+ break;
+ case CTAP_RESET:
+ printf1(TAG_CTAP,"CTAP_RESET\n");
+ status = ctap2_user_presence_test(CTAP2_UP_DELAY_MS);
+ if (status == CTAP1_ERR_SUCCESS)
+ {
+ ctap_reset();
}
break;
- case CTAP_BLOCKING_OP_GET_ASSERTION:
- result = _get_assertion_continue(out_buf);
- if (result.request_completed) {
- _state.blocking_op = CTAP_BLOCKING_OP_NONE;
- _get_assertion_free_state();
+ case GET_NEXT_ASSERTION:
+ printf1(TAG_CTAP,"CTAP_NEXT_ASSERTION\n");
+ if (getAssertionState.lastcmd == CTAP_GET_ASSERTION)
+ {
+ status = ctap_get_next_assertion(&encoder);
+ resp->length = cbor_encoder_get_buffer_size(&encoder, buf);
+ dump_hex1(TAG_DUMP, buf, resp->length);
+ if (status == 0)
+ {
+ cmd = CTAP_GET_ASSERTION; // allow for next assertion
+ }
+ }
+ else
+ {
+ printf2(TAG_ERR, "unwanted GET_NEXT_ASSERTION. lastcmd == 0x%02x\n", getAssertionState.lastcmd);
+ status = CTAP2_ERR_NOT_ALLOWED;
}
break;
- case CTAP_BLOCKING_OP_NONE:
default:
- Abort("Invalid status in ctap_retry");
+ status = CTAP1_ERR_INVALID_COMMAND;
+ printf2(TAG_ERR,"error, invalid cmd: 0x%02x\n", cmd);
+ }
+
+done:
+ device_set_status(CTAPHID_STATUS_IDLE);
+ getAssertionState.lastcmd = cmd;
+
+ if (status != CTAP1_ERR_SUCCESS)
+ {
+ resp->length = 0;
+ }
+
+ printf1(TAG_CTAP,"cbor output structure: %d bytes. Return 0x%02x\n", resp->length, status);
+
+ return status;
+}
+
+
+
+static void ctap_state_init()
+{
+ // Set to 0xff instead of 0x00 to be easier on flash
+ memset(&STATE, 0xff, sizeof(AuthenticatorState));
+ // Fresh RNG for key
+ ctap_generate_rng(STATE.key_space, KEY_SPACE_BYTES);
+
+ STATE.is_initialized = INITIALIZED_MARKER;
+ STATE.remaining_tries = PIN_LOCKOUT_ATTEMPTS;
+ STATE.is_pin_set = 0;
+ STATE.rk_stored = 0;
+ STATE.data_version = STATE_VERSION;
+
+ ctap_reset_rk();
+
+ if (ctap_generate_rng(STATE.PIN_SALT, sizeof(STATE.PIN_SALT)) != 1) {
+ printf2(TAG_ERR, "Error, rng failed\n");
+ exit(1);
+ }
+
+ printf1(TAG_STOR, "Generated PIN SALT: ");
+ dump_hex1(TAG_STOR, STATE.PIN_SALT, sizeof STATE.PIN_SALT);
+}
+
+/** Overwrite master secret from external source.
+ * @param keybytes an array of KEY_SPACE_BYTES length.
+ *
+ * This function should only be called from a privilege mode.
+*/
+void ctap_load_external_keys(uint8_t * keybytes){
+ memmove(STATE.key_space, keybytes, KEY_SPACE_BYTES);
+ authenticator_write_state(&STATE);
+ crypto_load_master_secret(STATE.key_space);
+}
+
+#include "version.h"
+void ctap_init()
+{
+ printf1(TAG_ERR,"Current firmware version address: %p\r\n", &firmware_version);
+ printf1(TAG_ERR,"Current firmware version: %d.%d.%d.%d (%02x.%02x.%02x.%02x)\r\n",
+ firmware_version.major, firmware_version.minor, firmware_version.patch, firmware_version.reserved,
+ firmware_version.major, firmware_version.minor, firmware_version.patch, firmware_version.reserved
+ );
+ crypto_ecc256_init();
+
+ int is_init = authenticator_read_state(&STATE);
+
+ device_set_status(CTAPHID_STATUS_IDLE);
+
+ if (is_init)
+ {
+ printf1(TAG_STOR,"Auth state is initialized\n");
+ }
+ else
+ {
+ ctap_state_init();
+ authenticator_write_state(&STATE);
+ }
+
+ do_migration_if_required(&STATE);
+
+ crypto_load_master_secret(STATE.key_space);
+
+ if (ctap_is_pin_set())
+ {
+ printf1(TAG_STOR, "attempts_left: %d\n", STATE.remaining_tries);
+ }
+ else
+ {
+ printf1(TAG_STOR,"pin not set.\n");
+ }
+ if (ctap_device_locked())
+ {
+ printf1(TAG_ERR, "DEVICE LOCKED!\n");
+ }
+
+ if (ctap_generate_rng(PIN_TOKEN, PIN_TOKEN_SIZE) != 1)
+ {
+ printf2(TAG_ERR,"Error, rng failed\n");
+ exit(1);
+ }
+
+ ctap_reset_key_agreement();
+
+#ifdef BRIDGE_TO_WALLET
+ wallet_init();
+#endif
+
+
+}
+
+uint8_t ctap_is_pin_set()
+{
+ return STATE.is_pin_set == 1;
+}
+
+/**
+ * Set new PIN, by updating PIN hash. Save state.
+ * Globals: STATE
+ * @param pin new PIN (raw)
+ * @param len pin array length
+ */
+void ctap_update_pin(uint8_t * pin, int len)
+{
+ if (len >= NEW_PIN_ENC_MIN_SIZE || len < 4)
+ {
+ printf2(TAG_ERR, "Update pin fail length\n");
+ exit(1);
+ }
+
+ crypto_sha256_init();
+ crypto_sha256_update(pin, len);
+ uint8_t intermediateHash[32];
+ crypto_sha256_final(intermediateHash);
+
+ crypto_sha256_init();
+ crypto_sha256_update(intermediateHash, 16);
+ memset(intermediateHash, 0, sizeof(intermediateHash));
+ crypto_sha256_update(STATE.PIN_SALT, sizeof(STATE.PIN_SALT));
+ crypto_sha256_final(STATE.PIN_CODE_HASH);
+
+ STATE.is_pin_set = 1;
+
+ authenticator_write_state(&STATE);
+
+ printf1(TAG_CTAP, "New pin set: %s [%d]\n", pin, len);
+ dump_hex1(TAG_ERR, STATE.PIN_CODE_HASH, sizeof(STATE.PIN_CODE_HASH));
+}
+
+uint8_t ctap_decrement_pin_attempts()
+{
+ if (PIN_BOOT_ATTEMPTS_LEFT > 0)
+ {
+ PIN_BOOT_ATTEMPTS_LEFT--;
+ }
+ if (! ctap_device_locked())
+ {
+ STATE.remaining_tries--;
+ ctap_flush_state();
+ printf1(TAG_CP, "ATTEMPTS left: %d\n", STATE.remaining_tries);
+
+ if (ctap_device_locked())
+ {
+ lock_device_permanently();
+ }
}
- return result;
+ else
+ {
+ printf1(TAG_CP, "Device locked!\n");
+ return -1;
+ }
+ return 0;
+}
+
+int8_t ctap_device_locked()
+{
+ return STATE.remaining_tries <= 0;
+}
+
+int8_t ctap_device_boot_locked()
+{
+ return PIN_BOOT_ATTEMPTS_LEFT <= 0;
+}
+
+int8_t ctap_leftover_pin_attempts()
+{
+ return STATE.remaining_tries;
+}
+
+void ctap_reset_pin_attempts()
+{
+ STATE.remaining_tries = PIN_LOCKOUT_ATTEMPTS;
+ PIN_BOOT_ATTEMPTS_LEFT = PIN_BOOT_ATTEMPTS;
+ ctap_flush_state();
+}
+
+void ctap_reset_state()
+{
+ memset(&getAssertionState, 0, sizeof(getAssertionState));
+}
+
+uint16_t ctap_keys_stored()
+{
+ int total = 0;
+ int i;
+ for (i = 0; i < MAX_KEYS; i++)
+ {
+ if (STATE.key_lens[i] != 0xffff)
+ {
+ total += 1;
+ }
+ else
+ {
+ break;
+ }
+ }
+ return total;
+}
+
+static uint16_t key_addr_offset(int index)
+{
+ uint16_t offset = 0;
+ int i;
+ for (i = 0; i < index; i++)
+ {
+ if (STATE.key_lens[i] != 0xffff) offset += STATE.key_lens[i];
+ }
+ return offset;
+}
+
+uint16_t ctap_key_len(uint8_t index)
+{
+ int i = ctap_keys_stored();
+ if (index >= i || index >= MAX_KEYS)
+ {
+ return 0;
+ }
+ if (STATE.key_lens[index] == 0xffff) return 0;
+ return STATE.key_lens[index];
+
+}
+
+int8_t ctap_store_key(uint8_t index, uint8_t * key, uint16_t len)
+{
+ int i = ctap_keys_stored();
+ uint16_t offset;
+ if (i >= MAX_KEYS || index >= MAX_KEYS || !len)
+ {
+ return ERR_NO_KEY_SPACE;
+ }
+
+ if (STATE.key_lens[index] != 0xffff)
+ {
+ return ERR_KEY_SPACE_TAKEN;
+ }
+
+ offset = key_addr_offset(index);
+
+ if ((offset + len) > KEY_SPACE_BYTES)
+ {
+ return ERR_NO_KEY_SPACE;
+ }
+
+ STATE.key_lens[index] = len;
+
+ memmove(STATE.key_space + offset, key, len);
+
+ ctap_flush_state();
+
+ return 0;
+}
+
+int8_t ctap_load_key(uint8_t index, uint8_t * key)
+{
+ int i = ctap_keys_stored();
+ uint16_t offset;
+ uint16_t len;
+ if (index >= i || index >= MAX_KEYS)
+ {
+ return ERR_NO_KEY_SPACE;
+ }
+
+ if (STATE.key_lens[index] == 0xffff)
+ {
+ return ERR_KEY_SPACE_EMPTY;
+ }
+
+ offset = key_addr_offset(index);
+ len = ctap_key_len(index);
+
+ if ((offset + len) > KEY_SPACE_BYTES)
+ {
+ return ERR_NO_KEY_SPACE;
+ }
+
+ memmove(key, STATE.key_space + offset, len);
+
+ return 0;
+}
+
+static void ctap_reset_key_agreement()
+{
+ ctap_generate_rng(KEY_AGREEMENT_PRIV, sizeof(KEY_AGREEMENT_PRIV));
+}
+
+void ctap_reset()
+{
+ ctap_state_init();
+
+ authenticator_write_state(&STATE);
+
+ if (ctap_generate_rng(PIN_TOKEN, PIN_TOKEN_SIZE) != 1)
+ {
+ printf2(TAG_ERR,"Error, rng failed\n");
+ exit(1);
+ }
+
+ ctap_reset_state();
+ ctap_reset_key_agreement();
+
+ crypto_load_master_secret(STATE.key_space);
+}
+
+void lock_device_permanently() {
+ memset(PIN_TOKEN, 0, sizeof(PIN_TOKEN));
+ memset(STATE.PIN_CODE_HASH, 0, sizeof(STATE.PIN_CODE_HASH));
+
+ printf1(TAG_CP, "Device locked!\n");
+
+ authenticator_write_state(&STATE);
}
diff --git src/fido2/ctap.h src/fido2/ctap.h
index c3435b4..1872df4 100644
--- src/fido2/ctap.h
+++ src/fido2/ctap.h
@@ -7,41 +7,49 @@
#ifndef _CTAP_H
#define _CTAP_H
-#include <crypto/sha2/sha256.h>
-#include <hardfault.h>
-#include <u2f/u2f_keyhandle.h>
-#include <util.h>
-
-#ifdef assert
-#undef assert
-#endif
-
-/*
- * TinyCBOR will use the default definition for assert.
- * The USB driver will include its own definition though...
- *
- * Replace it with our custom definition (i.e. Abort if condition
- * is false).
- */
-#define assert(cond) \
- do { \
- if (!(cond)) { \
- Abort("Assertion failed:\n" #cond); \
- } \
- } while(0);
-
-#include <cbor.h>
-
-#undef assert
-
-#include <usb/usb_packet.h>
-
-/**
- * Authenticator Status, transmitted through keepalive messages.
- */
-#define CTAPHID_STATUS_IDLE 0
-#define CTAPHID_STATUS_PROCESSING 1
-#define CTAPHID_STATUS_UPNEEDED 2
+#include "cbor.h"
+
+#define CTAP_MAKE_CREDENTIAL 0x01
+#define CTAP_GET_ASSERTION 0x02
+#define CTAP_CANCEL 0x03
+#define CTAP_GET_INFO 0x04
+#define CTAP_CLIENT_PIN 0x06
+#define CTAP_RESET 0x07
+#define GET_NEXT_ASSERTION 0x08
+#define CTAP_VENDOR_FIRST 0x40
+#define CTAP_VENDOR_LAST 0xBF
+
+#define MC_clientDataHash 0x01
+#define MC_rp 0x02
+#define MC_user 0x03
+#define MC_pubKeyCredParams 0x04
+#define MC_excludeList 0x05
+#define MC_extensions 0x06
+#define MC_options 0x07
+#define MC_pinAuth 0x08
+#define MC_pinProtocol 0x09
+
+#define GA_rpId 0x01
+#define GA_clientDataHash 0x02
+#define GA_allowList 0x03
+#define GA_extensions 0x04
+#define GA_options 0x05
+#define GA_pinAuth 0x06
+#define GA_pinProtocol 0x07
+
+#define CP_pinProtocol 0x01
+#define CP_subCommand 0x02
+ #define CP_cmdGetRetries 0x01
+ #define CP_cmdGetKeyAgreement 0x02
+ #define CP_cmdSetPin 0x03
+ #define CP_cmdChangePin 0x04
+ #define CP_cmdGetPinToken 0x05
+#define CP_keyAgreement 0x03
+#define CP_pinAuth 0x04
+#define CP_newPinEnc 0x05
+#define CP_pinHashEnc 0x06
+#define CP_getKeyAgreement 0x07
+#define CP_getRetries 0x08
#define EXT_HMAC_SECRET_COSE_KEY 0x01
#define EXT_HMAC_SECRET_SALT_ENC 0x02
@@ -50,195 +58,140 @@
#define EXT_HMAC_SECRET_REQUESTED 0x01
#define EXT_HMAC_SECRET_PARSED 0x02
-#define CLIENT_DATA_HASH_SIZE (SHA256_LEN)
+#define RESP_versions 0x1
+#define RESP_extensions 0x2
+#define RESP_aaguid 0x3
+#define RESP_options 0x4
+#define RESP_maxMsgSize 0x5
+#define RESP_pinProtocols 0x6
+
+#define RESP_fmt 0x01
+#define RESP_authData 0x02
+#define RESP_attStmt 0x03
+
+#define RESP_credential 0x01
+#define RESP_signature 0x03
+#define RESP_publicKeyCredentialUserEntity 0x04
+#define RESP_numberOfCredentials 0x05
+
+#define RESP_keyAgreement 0x01
+#define RESP_pinToken 0x02
+#define RESP_retries 0x03
+
+#define PARAM_clientDataHash (1 << 0)
+#define PARAM_rp (1 << 1)
+#define PARAM_user (1 << 2)
+#define PARAM_pubKeyCredParams (1 << 3)
+#define PARAM_excludeList (1 << 4)
+#define PARAM_extensions (1 << 5)
+#define PARAM_options (1 << 6)
+#define PARAM_pinAuth (1 << 7)
+#define PARAM_pinProtocol (1 << 8)
+#define PARAM_rpId (1 << 9)
+#define PARAM_allowList (1 << 10)
+
+#define MC_requiredMask (0x0f)
+
+
+#define CLIENT_DATA_HASH_SIZE 32 //sha256 hash
#define DOMAIN_NAME_MAX_SIZE 253
#define RP_NAME_LIMIT 32 // application limit, name parameter isn't needed.
-#define CTAP_USER_ID_MAX_SIZE 64
-
-/**
- * Maximum length of the CTAP username getting stored
- * in a resident credential.
- * Can be longer than 32B, but we only store this
- * data for displaying it when authenticating.
- * So if the actual length is longer we can just display
- * a truncated string.
- */
-#define CTAP_STORAGE_USER_NAME_LIMIT (20)
-
-/**
- * Maximum length of the CTAP username getting stored
- * in a resident credential.
- * Can be longer than 32B, but we only store this
- * data for displaying it when authenticating.
- * So if the actual length is longer we can just display
- * a truncated string.
- */
-#define CTAP_STORAGE_RP_ID_MAX_SIZE (20)
-
-/**
- * Maximum length of the CTAP username getting stored
- * in a resident credential. It could be truncated.
- */
-#define CTAP_STORAGE_DISPLAY_NAME_LIMIT (20)
-
-/** Maximum length of the CTAP username. */
-#define CTAP_USER_NAME_LIMIT (64)
+#define USER_ID_MAX_SIZE 64
+#define USER_NAME_LIMIT 65 // Must be minimum of 64 bytes but can be more.
#define DISPLAY_NAME_LIMIT 32 // Must be minimum of 64 bytes but can be more.
#define ICON_LIMIT 128 // Must be minimum of 64 bytes but can be more.
#define CTAP_MAX_MESSAGE_SIZE 1200
#define CREDENTIAL_RK_FLASH_PAD 2 // size of RK should be 8-byte aligned to store in flash easily.
- #define CREDENTIAL_TAG_SIZE 16
- #define CREDENTIAL_NONCE_SIZE (16 + CREDENTIAL_RK_FLASH_PAD)
- #define CREDENTIAL_COUNTER_SIZE (4)
- #define CREDENTIAL_ENC_SIZE 176 // pad to multiple of 16 bytes
+#define CREDENTIAL_TAG_SIZE 16
+#define CREDENTIAL_NONCE_SIZE (16 + CREDENTIAL_RK_FLASH_PAD)
+#define CREDENTIAL_COUNTER_SIZE (4)
+#define CREDENTIAL_ENC_SIZE 176 // pad to multiple of 16 bytes
- #define PUB_KEY_CRED_PUB_KEY 0x01
- #define PUB_KEY_CRED_CTAP1 0x41
- #define PUB_KEY_CRED_CUSTOM 0x42
- #define PUB_KEY_CRED_UNKNOWN 0x3F
+#define PUB_KEY_CRED_PUB_KEY 0x01
+#define PUB_KEY_CRED_CTAP1 0x41
+#define PUB_KEY_CRED_CUSTOM 0x42
+#define PUB_KEY_CRED_UNKNOWN 0x3F
- #define CREDENTIAL_IS_SUPPORTED 1
- #define CREDENTIAL_NOT_SUPPORTED 0
+#define CREDENTIAL_IS_SUPPORTED 1
+#define CREDENTIAL_NOT_SUPPORTED 0
- #define CTAP_CREDENTIAL_LIST_MAX_SIZE 20
+#define ALLOW_LIST_MAX_SIZE 20
- #define NEW_PIN_ENC_MAX_SIZE 256 // includes NULL terminator
- #define NEW_PIN_ENC_MIN_SIZE 64
- #define NEW_PIN_MAX_SIZE 64
- #define NEW_PIN_MIN_SIZE 4
+#define NEW_PIN_ENC_MAX_SIZE 256 // includes NULL terminator
+#define NEW_PIN_ENC_MIN_SIZE 64
+#define NEW_PIN_MAX_SIZE 64
+#define NEW_PIN_MIN_SIZE 4
- #define CTAP_RESPONSE_BUFFER_SIZE 4096
+#define CTAP_RESPONSE_BUFFER_SIZE 4096
- #define PIN_LOCKOUT_ATTEMPTS 8 // Number of attempts total
- #define PIN_BOOT_ATTEMPTS 3 // number of attempts per boot
+#define PIN_LOCKOUT_ATTEMPTS 8 // Number of attempts total
+#define PIN_BOOT_ATTEMPTS 3 // number of attempts per boot
- #define CTAP2_UP_DELAY_MS 29000
+#define CTAP2_UP_DELAY_MS 29000
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wpacked"
- #pragma GCC diagnostic ignored "-Wattributes"
-
-typedef struct {
- uint8_t id[CTAP_USER_ID_MAX_SIZE];
+typedef struct
+{
+ uint8_t id[USER_ID_MAX_SIZE];
uint8_t id_size;
- uint8_t name[CTAP_USER_NAME_LIMIT];
- uint8_t display_name[DISPLAY_NAME_LIMIT];
+ uint8_t name[USER_NAME_LIMIT];
+ uint8_t displayName[DISPLAY_NAME_LIMIT];
uint8_t icon[ICON_LIMIT];
-} ctap_user_entity_t;
-
-#define CTAP_RESIDENT_KEY_VALID (0x01)
-
-/**
- * Invalid keys have their "valid" field
- * set to 0xFF so that erased flash is invalid.
- */
-#define CTAP_RESIDENT_KEY_INVALID (0xff)
-
-typedef struct __attribute__((__packed__)) {
- uint8_t valid;
-
- /** Key handle (credential ID) */
- u2f_keyhandle_t key_handle;
-
- /**
- * Human-readable ID of the RP that created the credential.
- * This is a NULL-terminated string.
- */
- uint8_t rp_id[CTAP_STORAGE_RP_ID_MAX_SIZE];
- /**
- * sha256 hash of the original RP id.
- * This is necessary (together with the ID) so
- * that we can make check RP ids for matching values
- * even if the actual RP id is longer than 32 bytes.
- */
- uint8_t rp_id_hash[32];
- /**
- * User ID that the RP has assigned to our user.
- * We need to store this and send it back together
- * with our keyhandle when we're asked to authenticate.
- */
- uint8_t user_id[CTAP_USER_ID_MAX_SIZE];
- /**
- * Size of user_id.
- */
- uint8_t user_id_size;
- /**
- * Username belonging to this credential.
- * This is a NULL terminated string.
- * Side effect: if a credential is created
- * which matches the first CTAP_STORAGE_USER_NAME_LIMIT
- * characters of the user and display name of an existing
- * credential, the latter is going to be overwritten.
- * Can this be used for evil purposes? (it shouldn't).
- */
- uint8_t user_name[CTAP_STORAGE_USER_NAME_LIMIT];
- /**
- * Display name of the user. Same considerations apply
- * as for the user_name.
- */
- uint8_t display_name[CTAP_STORAGE_DISPLAY_NAME_LIMIT];
- /**
- * Creation "time" of the key.
- * This is the value that the U2F counter had when the
- * key got created. We must not store any real timestamp,
- * but we must be able to sort keys by creation time.
- */
- uint32_t creation_time;
-} ctap_resident_key_t;
-
-/**
- * Attested credential data, defined
- * in [WebAuthn] 6.4.1.
- */
-typedef struct __attribute__((packed)) {
- /** The AAGUID of the authenticator. */
+}__attribute__((packed)) CTAP_userEntity;
+
+typedef struct {
+ uint8_t tag[CREDENTIAL_TAG_SIZE];
+ uint8_t nonce[CREDENTIAL_NONCE_SIZE];
+ uint8_t rpIdHash[32];
+ uint32_t count;
+}__attribute__((packed)) CredentialId;
+
+struct Credential {
+ CredentialId id;
+ CTAP_userEntity user;
+};
+typedef struct Credential CTAP_residentKey;
+
+typedef struct
+{
+ uint8_t type;
+ struct Credential credential;
+} CTAP_credentialDescriptor;
+
+typedef struct
+{
uint8_t aaguid[16];
- /** Length of the credential ID (big-endian) */
- uint8_t cred_len[2];
- /** Credential ID */
- u2f_keyhandle_t id;
-} ctap_attest_data_t;
-
-/**
- * Authenticator data structure, to use
- * for authentication operations. It is
- * missing the attestedCredentialData
- * field. Defined in The WebAuthn specs, 6.1.
- */
-typedef struct __attribute__((packed)) {
- uint8_t rp_id_hash[32];
+ uint8_t credLenH;
+ uint8_t credLenL;
+ CredentialId id;
+} __attribute__((packed)) CTAP_attestHeader;
+
+typedef struct
+{
+ uint8_t rpIdHash[32];
uint8_t flags;
uint32_t signCount;
-} ctap_auth_data_header_t;
-
-/**
- * Authenticator data structure, including
- * the attestedCredentialData field.
- * Defined in The WebAuthn specs, 6.1.
- */
-typedef struct __attribute__((packed)) {
- ctap_auth_data_header_t head;
- ctap_attest_data_t attest;
- /* COSE-encoded pubkey and extension data */
- uint8_t other[310 - sizeof(ctap_auth_data_header_t) - sizeof(ctap_attest_data_t)];
-} ctap_auth_data_t;
-
-#pragma GCC diagnostic pop
+} __attribute__((packed)) CTAP_authDataHeader;
+
+typedef struct
+{
+ CTAP_authDataHeader head;
+ CTAP_attestHeader attest;
+} __attribute__((packed)) CTAP_authData;
typedef struct
{
uint8_t data[CTAP_RESPONSE_BUFFER_SIZE];
uint16_t data_size;
uint16_t length;
-} ctap_response_t;
+} CTAP_RESPONSE;
-typedef struct {
+struct rpId
+{
uint8_t id[DOMAIN_NAME_MAX_SIZE + 1]; // extra for NULL termination
- /* TODO change to id_size */
size_t size;
uint8_t name[RP_NAME_LIMIT];
-} ctap_rp_id_t;
+};
typedef struct
{
@@ -257,112 +210,154 @@ typedef struct
uint8_t saltEnc[64];
uint8_t saltAuth[32];
COSE_key keyAgreement;
- u2f_keyhandle_t* key_handle;
+ struct Credential * credential;
} CTAP_hmac_secret;
typedef struct
{
uint8_t hmac_secret_present;
CTAP_hmac_secret hmac_secret;
-} ctap_extensions_t;
+} CTAP_extensions;
typedef struct
{
- ctap_user_entity_t user;
+ CTAP_userEntity user;
uint8_t publicKeyCredentialType;
int32_t COSEAlgorithmIdentifier;
uint8_t rk;
-} ctap_cred_info_t;
+} CTAP_credInfo;
typedef struct
{
- uint32_t param_parsed;
- uint8_t client_data_hash[CLIENT_DATA_HASH_SIZE];
- ctap_rp_id_t rp;
+ uint32_t paramsParsed;
+ uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
+ struct rpId rp;
- ctap_cred_info_t cred_info;
+ CTAP_credInfo credInfo;
- CborValue exclude_list;
- size_t exclude_list_size;
+ CborValue excludeList;
+ size_t excludeListSize;
uint8_t uv;
uint8_t up;
- uint8_t pin_auth[16];
- uint8_t pin_auth_present;
- // pin_auth_empty is true iff an empty bytestring was provided as pin_auth.
- // This is exclusive with |pin_auth_present|. It exists because an empty
- // pin_auth is a special signal to block for touch. See
+ uint8_t pinAuth[16];
+ uint8_t pinAuthPresent;
+ // pinAuthEmpty is true iff an empty bytestring was provided as pinAuth.
+ // This is exclusive with |pinAuthPresent|. It exists because an empty
+ // pinAuth is a special signal to block for touch. See
// https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#using-pinToken-in-authenticatorMakeCredential
- uint8_t pin_auth_empty;
- int pin_protocol;
- ctap_extensions_t extensions;
+ uint8_t pinAuthEmpty;
+ int pinProtocol;
+ CTAP_extensions extensions;
-} ctap_make_credential_req_t;
+} CTAP_makeCredential;
typedef struct
{
- uint32_t param_parsed;
- uint8_t client_data_hash[CLIENT_DATA_HASH_SIZE];
- uint8_t client_data_hash_present;
+ uint32_t paramsParsed;
+ uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
+ uint8_t clientDataHashPresent;
+
+ struct rpId rp;
- ctap_rp_id_t rp;
+ int credLen;
uint8_t rk;
uint8_t uv;
uint8_t up;
- /* TODO remove pin_auth, we don't use it anyway. */
- uint8_t pin_auth[16];
- uint8_t pin_auth_present;
- /**
- * pin_auth_empty is true iff an empty bytestring was provided as pin_auth.
- * This is exclusive with |pin_auth_present|. It exists because an empty
- * pin_auth is a special signal to block for touch. See
- * https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#using-pinToken-in-authenticatorGetAssertion
- */
- uint8_t pin_auth_empty;
- int pin_protocol;
-
- /**
- * List of allowed credential descriptors for authentication.
- * If this parameter is present, then the authenticator MUST
- * use one of these credentials to authenticate.
- */
- u2f_keyhandle_t creds[CTAP_CREDENTIAL_LIST_MAX_SIZE];
- /** Number of credential descriptors present in this request. */
- int cred_len;
+ uint8_t pinAuth[16];
+ uint8_t pinAuthPresent;
+ // pinAuthEmpty is true iff an empty bytestring was provided as pinAuth.
+ // This is exclusive with |pinAuthPresent|. It exists because an empty
+ // pinAuth is a special signal to block for touch. See
+ // https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#using-pinToken-in-authenticatorGetAssertion
+ uint8_t pinAuthEmpty;
+ int pinProtocol;
+ CTAP_credentialDescriptor * creds;
uint8_t allowListPresent;
- ctap_extensions_t extensions;
-} ctap_get_assertion_req_t;
+ CTAP_extensions extensions;
-typedef struct {
- /** CTAP_* success/error code. */
- uint8_t status;
- /** If true, a response for this request can be sent. */
- bool request_completed;
-} ctap_request_result_t;
+} CTAP_getAssertion;
+
+typedef struct
+{
+ int pinProtocol;
+ int subCommand;
+ COSE_key keyAgreement;
+ uint8_t keyAgreementPresent;
+ uint8_t pinAuth[16];
+ uint8_t pinAuthPresent;
+ uint8_t newPinEnc[NEW_PIN_ENC_MAX_SIZE];
+ int newPinEncSize;
+ uint8_t pinHashEnc[16];
+ uint8_t pinHashEncPresent;
+ _Bool getKeyAgreement;
+ _Bool getRetries;
+} CTAP_clientPin;
+
+
+struct _getAssertionState {
+ CTAP_authDataHeader authData;
+ uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
+ CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE];
+ uint8_t lastcmd;
+ uint32_t count;
+ uint32_t index;
+ uint32_t time;
+ uint8_t user_verified;
+ uint8_t customCredId[256];
+ uint8_t customCredIdSize;
+};
+
+void ctap_response_init(CTAP_RESPONSE * resp);
+
+uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp);
+
+// Encodes R,S signature to 2 der sequence of two integers. Sigder must be at least 72 bytes.
+// @return length of der signature
+int ctap_encode_der_sig(uint8_t const * const in_sigbuf, uint8_t * const out_sigder);
-void ctap_response_init(ctap_response_t* resp);
+// Run ctap related power-up procedures (init pinToken, generate shared secret)
+void ctap_init();
-ctap_request_result_t ctap_request(const in_buffer_t* in_buf, buffer_t* out_buf);
+// Resets state between different accesses of different applications
+void ctap_reset_state();
-/**
- * Polls an outstanding operation for completion.
- *
- * @param out_data Buffer to fill with a response (if any is ready).
- * @param out_len[out] Length of the response contained in out_data.
- * @return Request status.
- */
-ctap_request_result_t ctap_retry(buffer_t* out_buf);
+uint8_t ctap_add_pin_if_verified(uint8_t * pinTokenEnc, uint8_t * platform_pubkey, uint8_t * pinHashEnc);
+uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platform_pubkey, uint8_t * pinAuth, uint8_t * pinHashEnc);
-// Run ctap related power-up procedures (init pinToken, generate shared secret)
-void ctap_init(void);
+void ctap_update_pin(uint8_t * pin, int len);
+uint8_t ctap_decrement_pin_attempts();
+int8_t ctap_leftover_pin_attempts();
+void ctap_reset_pin_attempts();
+uint8_t ctap_is_pin_set();
+uint8_t ctap_pin_matches(uint8_t * pin, int len);
+void ctap_reset();
+int8_t ctap_device_locked();
+int8_t ctap_device_boot_locked();
+
+// Key storage API
+
+// Return length of key at index. 0 if not exist.
+uint16_t ctap_key_len(uint8_t index);
-void make_auth_tag(uint8_t* rp_id_hash, uint8_t* nonce, uint32_t count, uint8_t* tag);
+// See error codes in storage.h
+int8_t ctap_store_key(uint8_t index, uint8_t * key, uint16_t len);
+int8_t ctap_load_key(uint8_t index, uint8_t * key);
+uint16_t ctap_key_len(uint8_t index);
-#endif // _CTAP_H
+#define PIN_TOKEN_SIZE 16
+extern uint8_t PIN_TOKEN[PIN_TOKEN_SIZE];
+extern uint8_t KEY_AGREEMENT_PUB[64];
+
+void lock_device_permanently();
+
+void ctap_load_external_keys(uint8_t * keybytes);
+
+#endif
diff --git src/fido2/ctap_parse.c src/fido2/ctap_parse.c
index ad52797..4a2f316 100644
--- src/fido2/ctap_parse.c
+++ src/fido2/ctap_parse.c
@@ -4,70 +4,79 @@
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
-#include <inttypes.h>
#include <stdint.h>
#include "cbor.h"
#include "ctap.h"
+#include "u2f.h"
#include "ctap_parse.h"
#include "ctap_errors.h"
#include "cose_key.h"
#include "util.h"
+#include "log.h"
-#include <u2f/u2f_keyhandle.h>
-
-#define check_retr(r) \
- do { \
- if ((r) != CborNoError) { \
- return r; \
- } \
- } while(0);
-
-/**
- * Field tags used in MakeCredential requests.
- */
-#define MAKE_CREDENTIAL_TAG_CLIENT_DATA_HASH (0x01)
-#define MAKE_CREDENTIAL_TAG_RELYING_PARTY (0x02)
-#define MAKE_CREDENTIAL_TAG_USER (0x03)
-#define MAKE_CREDENTIAL_TAG_PUB_KEY_CRED_PARAMS (0x04)
-#define MAKE_CREDENTIAL_TAG_EXCLUDE_LIST (0x05)
-#define MAKE_CREDENTIAL_TAG_EXTENSIONS (0x06)
-#define MAKE_CREDENTIAL_TAG_OPTIONS (0x07)
-#define MAKE_CREDENTIAL_TAG_PIN_AUTH (0x08)
-#define MAKE_CREDENTIAL_TAG_PIN_PROTOCOL (0x09)
-
-/**
- * Field tags used in MakeCredential requests.
- */
-#define GET_ASSERTION_TAG_RPID (0x01)
-#define GET_ASSERTION_TAG_CLIENT_DATA_HASH (0x02)
-#define GET_ASSERTION_TAG_ALLOW_LIST (0x03)
-#define GET_ASSERTION_TAG_EXTENSIONS (0x04)
-#define GET_ASSERTION_TAG_OPTIONS (0x05)
-#define GET_ASSERTION_TAG_PIN_AUTH (0x06)
-#define GET_ASSERTION_TAG_PIN_PROTOCOL (0x07)
-
-/**
- * Parameters contained in the requests.
- */
-#define PARAM_CLIENT_DATA_HASH (1 << 0)
-#define PARAM_RP (1 << 1)
-#define PARAM_USER (1 << 2)
-#define PARAM_PUB_KEY_CRED_PARAMS (1 << 3)
-#define PARAM_EXCLUDE_LIST (1 << 4)
-#define PARAM_EXTENSIONS (1 << 5)
-#define PARAM_OPTIONS (1 << 6)
-#define PARAM_PIN_AUTH (1 << 7)
-#define PARAM_PIN_PROTOCOL (1 << 8)
-#define PARAM_RP_ID (1 << 9)
-#define PARAM_ALLOW_LIST (1 << 10)
-
-/** Required parameters for a MakeCredential request. */
-static const uint8_t MAKE_CREDENTIAL_REQUIRED_PARAM_MASK =
- PARAM_CLIENT_DATA_HASH | PARAM_RP | PARAM_USER | PARAM_PUB_KEY_CRED_PARAMS;
-
-static uint8_t _parse_user(ctap_make_credential_req_t * MC, CborValue * val)
+extern struct _getAssertionState getAssertionState;
+
+void _check_ret(CborError ret, int line, const char * filename)
+{
+ if (ret != CborNoError)
+ {
+ printf1(TAG_ERR,"CborError: 0x%x: %s: %d: %s\n", ret, filename, line, cbor_error_string(ret));
+ /*exit(1);*/
+ }
+}
+
+const char * cbor_value_get_type_string(const CborValue *value)
+{
+ switch(cbor_value_get_type(value))
+ {
+ case CborIntegerType:
+ return "CborIntegerType";
+ break;
+ case CborByteStringType:
+ return "CborByteStringType";
+ break;
+ case CborTextStringType:
+ return "CborTextStringType";
+ break;
+ case CborArrayType:
+ return "CborArrayType";
+ break;
+ case CborMapType:
+ return "CborMapType";
+ break;
+ case CborTagType:
+ return "CborTagType";
+ break;
+ case CborSimpleType:
+ return "CborSimpleType";
+ break;
+ case CborBooleanType:
+ return "CborBooleanType";
+ break;
+ case CborNullType:
+ return "CborNullType";
+ break;
+ case CborUndefinedType:
+ return "CborUndefinedType";
+ break;
+ case CborHalfFloatType:
+ return "CborHalfFloatType";
+ break;
+ case CborFloatType:
+ return "CborFloatType";
+ break;
+ case CborDoubleType:
+ return "CborDoubleType";
+ break;
+ default:
+ return "Invalid type";
+ }
+}
+
+
+uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
{
size_t sz, map_length;
uint8_t key[24];
@@ -78,6 +87,7 @@ static uint8_t _parse_user(ctap_make_credential_req_t * MC, CborValue * val)
if (cbor_value_get_type(val) != CborMapType)
{
+ printf2(TAG_ERR,"error, wrong type\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
@@ -91,6 +101,7 @@ static uint8_t _parse_user(ctap_make_credential_req_t * MC, CborValue * val)
{
if (cbor_value_get_type(&map) != CborTextStringType)
{
+ printf2(TAG_ERR,"Error, expecting text string type for user map key, got %s\n", cbor_value_get_type_string(&map));
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
@@ -99,6 +110,7 @@ static uint8_t _parse_user(ctap_make_credential_req_t * MC, CborValue * val)
if (ret == CborErrorOutOfMemory)
{
+ printf2(TAG_ERR,"Error, rp map key is too large\n");
return CTAP2_ERR_LIMIT_EXCEEDED;
}
@@ -113,74 +125,83 @@ static uint8_t _parse_user(ctap_make_credential_req_t * MC, CborValue * val)
if (cbor_value_get_type(&map) != CborByteStringType)
{
+ printf2(TAG_ERR,"Error, expecting byte string type for rp map value\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
- sz = CTAP_USER_ID_MAX_SIZE;
- ret = cbor_value_copy_byte_string(&map, MC->cred_info.user.id, &sz, NULL);
+ sz = USER_ID_MAX_SIZE;
+ ret = cbor_value_copy_byte_string(&map, MC->credInfo.user.id, &sz, NULL);
if (ret == CborErrorOutOfMemory)
{
+ printf2(TAG_ERR,"Error, USER_ID is too large\n");
return CTAP2_ERR_LIMIT_EXCEEDED;
}
- MC->cred_info.user.id_size = sz;
+ MC->credInfo.user.id_size = sz;
check_ret(ret);
}
else if (strcmp((const char *)key, "name") == 0)
{
if (cbor_value_get_type(&map) != CborTextStringType)
{
+ printf2(TAG_ERR,"Error, expecting text string type for user.name value\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
- sz = CTAP_USER_NAME_LIMIT;
- ret = cbor_value_copy_text_string(&map, (char *)MC->cred_info.user.name, &sz, NULL);
+ sz = USER_NAME_LIMIT;
+ ret = cbor_value_copy_text_string(&map, (char *)MC->credInfo.user.name, &sz, NULL);
if (ret != CborErrorOutOfMemory)
{ // Just truncate the name it's okay
check_ret(ret);
}
- MC->cred_info.user.name[CTAP_USER_NAME_LIMIT - 1] = 0;
+ MC->credInfo.user.name[USER_NAME_LIMIT - 1] = 0;
}
else if (strcmp((const char *)key, "displayName") == 0)
{
if (cbor_value_get_type(&map) != CborTextStringType)
{
+ printf2(TAG_ERR,"Error, expecting text string type for user.displayName value\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
sz = DISPLAY_NAME_LIMIT;
- ret = cbor_value_copy_text_string(&map, (char *)MC->cred_info.user.display_name, &sz, NULL);
+ ret = cbor_value_copy_text_string(&map, (char *)MC->credInfo.user.displayName, &sz, NULL);
if (ret != CborErrorOutOfMemory)
{ // Just truncate the name it's okay
check_ret(ret);
}
- MC->cred_info.user.display_name[DISPLAY_NAME_LIMIT - 1] = 0;
+ MC->credInfo.user.displayName[DISPLAY_NAME_LIMIT - 1] = 0;
}
else if (strcmp((const char *)key, "icon") == 0)
{
if (cbor_value_get_type(&map) != CborTextStringType)
{
+ printf2(TAG_ERR,"Error, expecting text string type for user.icon value\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
sz = ICON_LIMIT;
- ret = cbor_value_copy_text_string(&map, (char *)MC->cred_info.user.icon, &sz, NULL);
+ ret = cbor_value_copy_text_string(&map, (char *)MC->credInfo.user.icon, &sz, NULL);
if (ret != CborErrorOutOfMemory)
{ // Just truncate the name it's okay
check_ret(ret);
}
- MC->cred_info.user.icon[ICON_LIMIT - 1] = 0;
+ MC->credInfo.user.icon[ICON_LIMIT - 1] = 0;
}
+ else
+ {
+ printf1(TAG_PARSE,"ignoring key %s for user map\n", key);
+ }
ret = cbor_value_advance(&map);
check_ret(ret);
}
- MC->param_parsed |= PARAM_USER;
+ MC->paramsParsed |= PARAM_user;
return 0;
}
-static uint8_t _parse_pub_key_cred_param(CborValue * val, uint8_t * cred_type, int32_t * alg_type)
+uint8_t parse_pub_key_cred_param(CborValue * val, uint8_t * cred_type, int32_t * alg_type)
{
CborValue cred;
CborValue alg;
@@ -190,6 +211,7 @@ static uint8_t _parse_pub_key_cred_param(CborValue * val, uint8_t * cred_type, i
if (cbor_value_get_type(val) != CborMapType)
{
+ printf2(TAG_ERR,"error, expecting map type, got %s\n", cbor_value_get_type_string(val));
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
@@ -200,10 +222,12 @@ static uint8_t _parse_pub_key_cred_param(CborValue * val, uint8_t * cred_type, i
if (cbor_value_get_type(&cred) != CborTextStringType)
{
+ printf2(TAG_ERR,"Error, parse_pub_key could not find credential param\n");
return CTAP2_ERR_MISSING_PARAMETER;
}
if (cbor_value_get_type(&alg) != CborIntegerType)
{
+ printf2(TAG_ERR,"Error, parse_pub_key could not find alg param\n");
return CTAP2_ERR_MISSING_PARAMETER;
}
@@ -228,7 +252,7 @@ static uint8_t _parse_pub_key_cred_param(CborValue * val, uint8_t * cred_type, i
}
// Check if public key credential+algorithm type is supported
-static int _pub_key_cred_param_supported(uint8_t cred, int32_t alg)
+static int pub_key_cred_param_supported(uint8_t cred, int32_t alg)
{
if (cred == PUB_KEY_CRED_PUB_KEY)
{
@@ -241,7 +265,7 @@ static int _pub_key_cred_param_supported(uint8_t cred, int32_t alg)
return CREDENTIAL_NOT_SUPPORTED;
}
-static uint8_t _parse_pub_key_cred_params(ctap_make_credential_req_t * MC, CborValue * val)
+uint8_t parse_pub_key_cred_params(CTAP_makeCredential * MC, CborValue * val)
{
size_t arr_length;
uint8_t cred_type;
@@ -253,6 +277,7 @@ static uint8_t _parse_pub_key_cred_params(ctap_make_credential_req_t * MC, CborV
if (cbor_value_get_type(val) != CborArrayType)
{
+ printf2(TAG_ERR,"error, expecting array type\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
@@ -264,7 +289,7 @@ static uint8_t _parse_pub_key_cred_params(ctap_make_credential_req_t * MC, CborV
for (i = 0; i < arr_length; i++)
{
- if ((ret = _parse_pub_key_cred_param(&arr, &cred_type, &alg_type)) != 0)
+ if ((ret = parse_pub_key_cred_param(&arr, &cred_type, &alg_type)) != 0)
{
return ret;
}
@@ -277,13 +302,13 @@ static uint8_t _parse_pub_key_cred_params(ctap_make_credential_req_t * MC, CborV
for (i = 0; i < arr_length; i++)
{
- if ((ret = _parse_pub_key_cred_param(&arr, &cred_type, &alg_type)) == 0)
+ if ((ret = parse_pub_key_cred_param(&arr, &cred_type, &alg_type)) == 0)
{
- if (_pub_key_cred_param_supported(cred_type, alg_type) == CREDENTIAL_IS_SUPPORTED)
+ if (pub_key_cred_param_supported(cred_type, alg_type) == CREDENTIAL_IS_SUPPORTED)
{
- MC->cred_info.publicKeyCredentialType = cred_type;
- MC->cred_info.COSEAlgorithmIdentifier = alg_type;
- MC->param_parsed |= PARAM_PUB_KEY_CRED_PARAMS;
+ MC->credInfo.publicKeyCredentialType = cred_type;
+ MC->credInfo.COSEAlgorithmIdentifier = alg_type;
+ MC->paramsParsed |= PARAM_pubKeyCredParams;
return 0;
}
}
@@ -291,10 +316,11 @@ static uint8_t _parse_pub_key_cred_params(ctap_make_credential_req_t * MC, CborV
check_ret(ret);
}
+ printf2(TAG_ERR,"Error, no public key credential parameters are supported!\n");
return CTAP2_ERR_UNSUPPORTED_ALGORITHM;
}
-static uint8_t _parse_fixed_byte_string(CborValue * map, uint8_t * dst, unsigned int len)
+uint8_t parse_fixed_byte_string(CborValue * map, uint8_t * dst, unsigned int len)
{
size_t sz;
int ret;
@@ -305,25 +331,28 @@ static uint8_t _parse_fixed_byte_string(CborValue * map, uint8_t * dst, unsigned
check_ret(ret);
if (sz != len)
{
+ printf2(TAG_ERR, "error byte string is different length (%d vs %d)\r\n", len, sz);
return CTAP1_ERR_INVALID_LENGTH;
}
}
else
{
+ printf2(TAG_ERR, "error, CborByteStringType expected\r\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
return 0;
}
-static uint8_t parse_verify_exclude_list(CborValue * val)
+uint8_t parse_verify_exclude_list(CborValue * val)
{
unsigned int i;
int ret;
CborValue arr;
size_t size;
- u2f_keyhandle_t cred;
+ CTAP_credentialDescriptor cred;
if (cbor_value_get_type(val) != CborArrayType)
{
+ printf2(TAG_ERR,"error, exclude list is not a map\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = cbor_value_get_array_length(val, &size);
@@ -332,11 +361,7 @@ static uint8_t parse_verify_exclude_list(CborValue * val)
check_ret(ret);
for (i = 0; i < size; i++)
{
- bool cred_valid;
- ret = ctap_parse_credential_descriptor(&arr, &cred, &cred_valid);
- if (!cred_valid) {
- return CTAP2_ERR_INVALID_CBOR;
- }
+ ret = parse_credential_descriptor(&arr, &cred);
check_ret(ret);
ret = cbor_value_advance(&arr);
check_ret(ret);
@@ -345,7 +370,7 @@ static uint8_t parse_verify_exclude_list(CborValue * val)
return 0;
}
-static uint8_t _parse_rp_id(ctap_rp_id_t * rp, CborValue * val)
+uint8_t parse_rp_id(struct rpId * rp, CborValue * val)
{
size_t sz = DOMAIN_NAME_MAX_SIZE;
if (cbor_value_get_type(val) != CborTextStringType)
@@ -355,6 +380,7 @@ static uint8_t _parse_rp_id(ctap_rp_id_t * rp, CborValue * val)
int ret = cbor_value_copy_text_string(val, (char*)rp->id, &sz, NULL);
if (ret == CborErrorOutOfMemory)
{
+ printf2(TAG_ERR,"Error, RP_ID is too large\n");
return CTAP2_ERR_LIMIT_EXCEEDED;
}
check_ret(ret);
@@ -363,7 +389,7 @@ static uint8_t _parse_rp_id(ctap_rp_id_t * rp, CborValue * val)
return 0;
}
-static uint8_t _parse_rp(ctap_rp_id_t * rp, CborValue * val)
+uint8_t parse_rp(struct rpId * rp, CborValue * val)
{
size_t sz, map_length;
char key[8];
@@ -374,6 +400,7 @@ static uint8_t _parse_rp(ctap_rp_id_t * rp, CborValue * val)
if (cbor_value_get_type(val) != CborMapType)
{
+ printf2(TAG_ERR,"error, wrong type\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
@@ -389,6 +416,7 @@ static uint8_t _parse_rp(ctap_rp_id_t * rp, CborValue * val)
{
if (cbor_value_get_type(&map) != CborTextStringType)
{
+ printf2(TAG_ERR,"Error, expecting text string type for rp map key, got %s\n", cbor_value_get_type_string(&map));
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
@@ -397,6 +425,7 @@ static uint8_t _parse_rp(ctap_rp_id_t * rp, CborValue * val)
if (ret == CborErrorOutOfMemory)
{
+ printf2(TAG_ERR,"Error, rp map key is too large\n");
return CTAP2_ERR_LIMIT_EXCEEDED;
}
check_ret(ret);
@@ -407,12 +436,13 @@ static uint8_t _parse_rp(ctap_rp_id_t * rp, CborValue * val)
if (cbor_value_get_type(&map) != CborTextStringType)
{
+ printf2(TAG_ERR,"Error, expecting text string type for rp map value\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
if (strcmp(key, "id") == 0)
{
- ret = _parse_rp_id(rp, &map);
+ ret = parse_rp_id(rp, &map);
if (ret != 0)
{
return ret;
@@ -428,6 +458,10 @@ static uint8_t _parse_rp(ctap_rp_id_t * rp, CborValue * val)
}
rp->name[RP_NAME_LIMIT - 1] = 0;
}
+ else
+ {
+ printf1(TAG_PARSE,"ignoring key %s for RP map\n", key);
+ }
ret = cbor_value_advance(&map);
check_ret(ret);
@@ -435,6 +469,7 @@ static uint8_t _parse_rp(ctap_rp_id_t * rp, CborValue * val)
}
if (rp->size == 0)
{
+ printf2(TAG_ERR,"Error, no RPID provided\n");
return CTAP2_ERR_MISSING_PARAMETER;
}
@@ -442,7 +477,7 @@ static uint8_t _parse_rp(ctap_rp_id_t * rp, CborValue * val)
return 0;
}
-static uint8_t _parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8_t * up)
+uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8_t * up)
{
size_t sz, map_length;
char key[8];
@@ -453,6 +488,7 @@ static uint8_t _parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8
if (cbor_value_get_type(val) != CborMapType)
{
+ printf2(TAG_ERR,"error, wrong type\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
@@ -467,6 +503,7 @@ static uint8_t _parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8
{
if (cbor_value_get_type(&map) != CborTextStringType)
{
+ printf2(TAG_ERR,"Error, expecting text string type for options map key, got %s\n", cbor_value_get_type_string(&map));
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
sz = sizeof(key);
@@ -474,6 +511,7 @@ static uint8_t _parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8
if (ret == CborErrorOutOfMemory)
{
+ printf2(TAG_ERR,"Error, rp map key is too large\n");
return CTAP2_ERR_LIMIT_EXCEEDED;
}
check_ret(ret);
@@ -484,6 +522,7 @@ static uint8_t _parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8
if (cbor_value_get_type(&map) != CborBooleanType)
{
+ printf2(TAG_ERR,"Error, expecting bool type for option map value\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
@@ -491,116 +530,34 @@ static uint8_t _parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8
{
ret = cbor_value_get_boolean(&map, &b);
check_ret(ret);
+ printf1(TAG_GA, "rk: %d\r\n",b);
*rk = b;
}
else if (strncmp(key, "uv",2) == 0)
{
ret = cbor_value_get_boolean(&map, &b);
check_ret(ret);
+ printf1(TAG_GA, "uv: %d\r\n",b);
*uv = b;
}
else if (strncmp(key, "up",2) == 0)
{
ret = cbor_value_get_boolean(&map, &b);
check_ret(ret);
+ printf1(TAG_GA, "up: %d\r\n",b);
*up = b;
}
- ret = cbor_value_advance(&map);
- check_ret(ret);
- }
- return 0;
-}
-
-static uint8_t _parse_cose_key(CborValue * it, COSE_key * cose)
-{
- CborValue map;
- size_t map_length;
- int ret,key;
- unsigned int i;
- int xkey = 0,ykey = 0;
- cose->kty = 0;
- cose->crv = 0;
-
-
- CborType type = cbor_value_get_type(it);
- if (type != CborMapType)
- {
- return CTAP2_ERR_INVALID_CBOR_TYPE;
- }
-
- ret = cbor_value_enter_container(it,&map);
- check_ret(ret);
-
- ret = cbor_value_get_map_length(it, &map_length);
- check_ret(ret);
-
-
- for (i = 0; i < map_length; i++)
- {
- if (cbor_value_get_type(&map) != CborIntegerType)
+ else
{
- return CTAP2_ERR_INVALID_CBOR_TYPE;
- }
-
- ret = cbor_value_get_int_checked(&map, &key);
- check_ret(ret);
-
- ret = cbor_value_advance(&map);
- check_ret(ret);
-
- switch(key)
- {
- case COSE_KEY_LABEL_KTY:
- if (cbor_value_get_type(&map) == CborIntegerType)
- {
- ret = cbor_value_get_int_checked(&map, &cose->kty);
- check_ret(ret);
- }
- else
- {
- return CTAP2_ERR_INVALID_CBOR_TYPE;
- }
- break;
- case COSE_KEY_LABEL_ALG:
- break;
- case COSE_KEY_LABEL_CRV:
- if (cbor_value_get_type(&map) == CborIntegerType)
- {
- ret = cbor_value_get_int_checked(&map, &cose->crv);
- check_ret(ret);
- }
- else
- {
- return CTAP2_ERR_INVALID_CBOR_TYPE;
- }
- break;
- case COSE_KEY_LABEL_X:
- ret = _parse_fixed_byte_string(&map, cose->pubkey.x, 32);
- check_retr(ret);
- xkey = 1;
-
- break;
- case COSE_KEY_LABEL_Y:
- ret = _parse_fixed_byte_string(&map, cose->pubkey.y, 32);
- check_retr(ret);
- ykey = 1;
-
- break;
- default:
- break;
+ printf2(TAG_PARSE,"ignoring option specified %s\n", key);
}
-
ret = cbor_value_advance(&map);
check_ret(ret);
}
- if (xkey == 0 || ykey == 0 || cose->kty == 0 || cose->crv == 0)
- {
- return CTAP2_ERR_MISSING_PARAMETER;
- }
return 0;
}
-static uint8_t ctap_parse_hmac_secret(CborValue * val, CTAP_hmac_secret * hs)
+uint8_t ctap_parse_hmac_secret(CborValue * val, CTAP_hmac_secret * hs)
{
size_t map_length;
size_t salt_len;
@@ -612,6 +569,7 @@ static uint8_t ctap_parse_hmac_secret(CborValue * val, CTAP_hmac_secret * hs)
if (cbor_value_get_type(val) != CborMapType)
{
+ printf2(TAG_ERR,"error, wrong type\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
@@ -625,6 +583,7 @@ static uint8_t ctap_parse_hmac_secret(CborValue * val, CTAP_hmac_secret * hs)
{
if (cbor_value_get_type(&map) != CborIntegerType)
{
+ printf2(TAG_ERR,"Error, expecting CborIntegerTypefor hmac-secret map key, got %s\n", cbor_value_get_type_string(&map));
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = cbor_value_get_int(&map, &key);
@@ -636,7 +595,7 @@ static uint8_t ctap_parse_hmac_secret(CborValue * val, CTAP_hmac_secret * hs)
switch(key)
{
case EXT_HMAC_SECRET_COSE_KEY:
- ret = _parse_cose_key(&map, &hs->keyAgreement);
+ ret = parse_cose_key(&map, &hs->keyAgreement);
check_retr(ret);
parsed_count++;
break;
@@ -657,8 +616,6 @@ static uint8_t ctap_parse_hmac_secret(CborValue * val, CTAP_hmac_secret * hs)
check_ret(ret);
parsed_count++;
break;
- default:
- Abort("ctap_parse_hmac_secret: bad key");
}
ret = cbor_value_advance(&map);
@@ -667,6 +624,7 @@ static uint8_t ctap_parse_hmac_secret(CborValue * val, CTAP_hmac_secret * hs)
if (parsed_count != 3)
{
+ printf2(TAG_ERR, "ctap_parse_hmac_secret missing parameter. Got %d.\r\n", parsed_count);
return CTAP2_ERR_MISSING_PARAMETER;
}
@@ -674,7 +632,7 @@ static uint8_t ctap_parse_hmac_secret(CborValue * val, CTAP_hmac_secret * hs)
}
-static uint8_t ctap_parse_extensions(CborValue* val, ctap_extensions_t* ext)
+uint8_t ctap_parse_extensions(CborValue * val, CTAP_extensions * ext)
{
CborValue map;
size_t sz, map_length;
@@ -685,6 +643,7 @@ static uint8_t ctap_parse_extensions(CborValue* val, ctap_extensions_t* ext)
if (cbor_value_get_type(val) != CborMapType)
{
+ printf2(TAG_ERR,"error, wrong type\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
@@ -698,6 +657,7 @@ static uint8_t ctap_parse_extensions(CborValue* val, ctap_extensions_t* ext)
{
if (cbor_value_get_type(&map) != CborTextStringType)
{
+ printf2(TAG_ERR,"Error, expecting text string type for options map key, got %s\n", cbor_value_get_type_string(&map));
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
sz = sizeof(key);
@@ -705,8 +665,9 @@ static uint8_t ctap_parse_extensions(CborValue* val, ctap_extensions_t* ext)
if (ret == CborErrorOutOfMemory)
{
- cbor_value_advance(&map);
- cbor_value_advance(&map);
+ printf2(TAG_ERR,"Error, rp map key is too large. Ignoring.\n");
+ check_ret( cbor_value_advance(&map) );
+ check_ret( cbor_value_advance(&map) );
continue;
}
check_ret(ret);
@@ -723,12 +684,18 @@ static uint8_t ctap_parse_extensions(CborValue* val, ctap_extensions_t* ext)
ret = cbor_value_get_boolean(&map, &b);
check_ret(ret);
if (b) ext->hmac_secret_present = EXT_HMAC_SECRET_REQUESTED;
+ printf1(TAG_CTAP, "set hmac_secret_present to %d\r\n", b);
}
else if (cbor_value_get_type(&map) == CborMapType)
{
ret = ctap_parse_hmac_secret(&map, &ext->hmac_secret);
check_retr(ret);
ext->hmac_secret_present = EXT_HMAC_SECRET_PARSED;
+ printf1(TAG_CTAP, "parsed hmac_secret request\r\n");
+ }
+ else
+ {
+ printf1(TAG_RED, "warning: hmac_secret request ignored for being wrong type\r\n");
}
}
@@ -738,9 +705,8 @@ static uint8_t ctap_parse_extensions(CborValue* val, ctap_extensions_t* ext)
return 0;
}
-uint8_t ctap_parse_make_credential(ctap_make_credential_req_t * MC, CborEncoder * encoder, const in_buffer_t* in_buffer)
+uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length)
{
- (void)encoder;
int ret;
unsigned int i;
int key;
@@ -748,14 +714,15 @@ uint8_t ctap_parse_make_credential(ctap_make_credential_req_t * MC, CborEncoder
CborParser parser;
CborValue it,map;
- memset(MC, 0, sizeof(*MC));
+ memset(MC, 0, sizeof(CTAP_makeCredential));
MC->up = 0xff;
- ret = cbor_parser_init(in_buffer->data, in_buffer->len, CborValidateCanonicalFormat, &parser, &it);
+ ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it);
check_retr(ret);
CborType type = cbor_value_get_type(&it);
if (type != CborMapType)
{
+ printf2(TAG_ERR,"Error, expecting cbor map\n");
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
}
@@ -765,12 +732,14 @@ uint8_t ctap_parse_make_credential(ctap_make_credential_req_t * MC, CborEncoder
ret = cbor_value_get_map_length(&it, &map_length);
check_ret(ret);
+ printf1(TAG_MC,"map has %d elements\n",map_length);
for (i = 0; i < map_length; i++)
{
type = cbor_value_get_type(&map);
if (type != CborIntegerType)
{
+ printf2(TAG_ERR,"Error, expecting int for map key\n");
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
}
ret = cbor_value_get_int_checked(&map, &key);
@@ -783,50 +752,64 @@ uint8_t ctap_parse_make_credential(ctap_make_credential_req_t * MC, CborEncoder
switch(key)
{
- case MAKE_CREDENTIAL_TAG_CLIENT_DATA_HASH:
+ case MC_clientDataHash:
+ printf1(TAG_MC,"CTAP_clientDataHash\n");
- ret = _parse_fixed_byte_string(&map, MC->client_data_hash, CLIENT_DATA_HASH_SIZE);
+ ret = parse_fixed_byte_string(&map, MC->clientDataHash, CLIENT_DATA_HASH_SIZE);
if (ret == 0)
{
- MC->param_parsed |= PARAM_CLIENT_DATA_HASH;
+ MC->paramsParsed |= PARAM_clientDataHash;
}
+ printf1(TAG_MC," "); dump_hex1(TAG_MC,MC->clientDataHash, 32);
break;
- case MAKE_CREDENTIAL_TAG_RELYING_PARTY:
+ case MC_rp:
+ printf1(TAG_MC,"CTAP_rp\n");
- ret = _parse_rp(&MC->rp, &map);
+ ret = parse_rp(&MC->rp, &map);
if (ret == 0)
{
- MC->param_parsed |= PARAM_RP;
+ MC->paramsParsed |= PARAM_rp;
}
+ printf1(TAG_MC," ID: %s\n", MC->rp.id);
+ printf1(TAG_MC," name: %s\n", MC->rp.name);
break;
- case MAKE_CREDENTIAL_TAG_USER:
+ case MC_user:
+ printf1(TAG_MC,"CTAP_user\n");
- ret = _parse_user(MC, &map);
+ ret = parse_user(MC, &map);
+ printf1(TAG_MC," ID: "); dump_hex1(TAG_MC, MC->credInfo.user.id, MC->credInfo.user.id_size);
+ printf1(TAG_MC," name: %s\n", MC->credInfo.user.name);
break;
- case MAKE_CREDENTIAL_TAG_PUB_KEY_CRED_PARAMS:
+ case MC_pubKeyCredParams:
+ printf1(TAG_MC,"CTAP_pubKeyCredParams\n");
- ret = _parse_pub_key_cred_params(MC, &map);
+ ret = parse_pub_key_cred_params(MC, &map);
+ printf1(TAG_MC," cred_type: 0x%02x\n", MC->credInfo.publicKeyCredentialType);
+ printf1(TAG_MC," alg_type: %d\n", MC->credInfo.COSEAlgorithmIdentifier);
break;
- case MAKE_CREDENTIAL_TAG_EXCLUDE_LIST:
+ case MC_excludeList:
+ printf1(TAG_MC,"CTAP_excludeList\n");
ret = parse_verify_exclude_list(&map);
check_ret(ret);
- ret = cbor_value_enter_container(&map, &MC->exclude_list);
+ ret = cbor_value_enter_container(&map, &MC->excludeList);
check_ret(ret);
- ret = cbor_value_get_array_length(&map, &MC->exclude_list_size);
+ ret = cbor_value_get_array_length(&map, &MC->excludeListSize);
check_ret(ret);
+ printf1(TAG_MC,"CTAP_excludeList done\n");
break;
- case MAKE_CREDENTIAL_TAG_EXTENSIONS:
+ case MC_extensions:
+ printf1(TAG_MC,"CTAP_extensions\n");
type = cbor_value_get_type(&map);
if (type != CborMapType)
{
@@ -836,22 +819,24 @@ uint8_t ctap_parse_make_credential(ctap_make_credential_req_t * MC, CborEncoder
check_retr(ret);
break;
- case MAKE_CREDENTIAL_TAG_OPTIONS:
- ret = _parse_options(&map, &MC->cred_info.rk, &MC->uv, &MC->up);
+ case MC_options:
+ printf1(TAG_MC,"CTAP_options\n");
+ ret = parse_options(&map, &MC->credInfo.rk, &MC->uv, &MC->up);
check_retr(ret);
break;
- case MAKE_CREDENTIAL_TAG_PIN_AUTH: {
+ case MC_pinAuth: {
+ printf1(TAG_MC,"CTAP_pinAuth\n");
size_t pinSize;
if (cbor_value_get_type(&map) == CborByteStringType &&
cbor_value_get_string_length(&map, &pinSize) == CborNoError &&
pinSize == 0)
{
- MC->pin_auth_empty = 1;
+ MC->pinAuthEmpty = 1;
break;
}
- ret = _parse_fixed_byte_string(&map, MC->pin_auth, 16);
+ ret = parse_fixed_byte_string(&map, MC->pinAuth, 16);
if (CTAP1_ERR_INVALID_LENGTH != ret) // damn microsoft
{
check_retr(ret);
@@ -860,14 +845,16 @@ uint8_t ctap_parse_make_credential(ctap_make_credential_req_t * MC, CborEncoder
{
ret = 0;
}
- MC->pin_auth_present = 1;
+ MC->pinAuthPresent = 1;
break;
}
- case MAKE_CREDENTIAL_TAG_PIN_PROTOCOL:
+ case MC_pinProtocol:
+ printf1(TAG_MC,"CTAP_pinProtocol\n");
if (cbor_value_get_type(&map) == CborIntegerType)
{
- ret = cbor_value_get_int_checked(&map, &MC->pin_protocol);
+ ret = cbor_value_get_int_checked(&map, &MC->pinProtocol);
check_ret(ret);
+ printf1(TAG_MC," == %d\n",MC->pinProtocol);
}
else
{
@@ -877,7 +864,7 @@ uint8_t ctap_parse_make_credential(ctap_make_credential_req_t * MC, CborEncoder
break;
default:
- break;
+ printf1(TAG_MC,"invalid key %d\n", key);
}
if (ret != 0)
@@ -888,87 +875,98 @@ uint8_t ctap_parse_make_credential(ctap_make_credential_req_t * MC, CborEncoder
check_ret(ret);
}
- /* Check if all the mandatory parameters are present in the request. */
- if ((MC->param_parsed & MAKE_CREDENTIAL_REQUIRED_PARAM_MASK) != MAKE_CREDENTIAL_REQUIRED_PARAM_MASK) {
- return CTAP2_ERR_MISSING_PARAMETER;
- }
return 0;
}
-uint8_t ctap_parse_credential_descriptor(CborValue* arr, u2f_keyhandle_t* cred, bool* cred_valid_out)
+uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor * cred)
{
int ret;
size_t buflen;
char type[12];
CborValue val;
+ cred->type = 0;
- if (cbor_value_get_type(arr) != CborMapType) {
+ if (cbor_value_get_type(arr) != CborMapType)
+ {
+ printf2(TAG_ERR,"Error, CborMapType expected in credential\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
- /* Fetch the key handle. */
ret = cbor_value_map_find_value(arr, "id", &val);
check_ret(ret);
- if (cbor_value_get_type(&val) != CborByteStringType) {
+ if (cbor_value_get_type(&val) != CborByteStringType)
+ {
+ printf2(TAG_ERR,"Error, No valid ID field (%s)\n", cbor_value_get_type_string(&val));
return CTAP2_ERR_MISSING_PARAMETER;
}
- buflen = sizeof(*cred);
- ret = cbor_value_copy_byte_string(&val, (uint8_t*)cred, &buflen, NULL);
+ buflen = sizeof(CredentialId);
+ ret = cbor_value_copy_byte_string(&val, (uint8_t*)&cred->credential.id, &buflen, NULL);
- if (buflen < sizeof(*cred)) {
- /* Not enough bytes to be a credential that we've generated. Skip it. */
- *cred_valid_out = false;
- return 0;
+ if (buflen == U2F_KEY_HANDLE_SIZE)
+ {
+ printf2(TAG_PARSE,"CTAP1 credential\n");
+ cred->type = PUB_KEY_CRED_CTAP1;
+ }
+ else if (buflen != sizeof(CredentialId))
+ {
+ printf2(TAG_ERR,"Ignoring credential is incorrect length, treating as custom\n");
+ cred->type = PUB_KEY_CRED_CUSTOM;
+ buflen = 256;
+ ret = cbor_value_copy_byte_string(&val, getAssertionState.customCredId, &buflen, NULL);
+ getAssertionState.customCredIdSize = buflen;
}
check_ret(ret);
- /* Now check the "type" field. */
ret = cbor_value_map_find_value(arr, "type", &val);
check_ret(ret);
- if (cbor_value_get_type(&val) != CborTextStringType) {
- *cred_valid_out = false;
+ if (cbor_value_get_type(&val) != CborTextStringType)
+ {
+ printf2(TAG_ERR,"Error, No valid type field\n");
return CTAP2_ERR_MISSING_PARAMETER;
}
buflen = sizeof(type);
ret = cbor_value_copy_text_string(&val, type, &buflen, NULL);
- if (ret == CborErrorOutOfMemory) {
- /*
- * The type string is too big, so type type of the key
- * is not something we know about.
- */
- *cred_valid_out = false;
- return 0;
- } else {
+ if (ret == CborErrorOutOfMemory)
+ {
+ cred->type = PUB_KEY_CRED_UNKNOWN;
+ }
+ else
+ {
check_ret(ret);
}
- if (strncmp(type, "public-key", 11) != 0) {
- /* Not a keytype we know. */
- *cred_valid_out = false;
- return 0;
+
+ if (strncmp(type, "public-key",11) == 0)
+ {
+ if (0 == cred->type)
+ {
+ cred->type = PUB_KEY_CRED_PUB_KEY;
+ }
}
- *cred_valid_out = true;
+ else
+ {
+ cred->type = PUB_KEY_CRED_UNKNOWN;
+ printf1(TAG_RED, "Unknown type: %s\r\n", type);
+ }
+
return 0;
}
-/**
- * Parses the list of allowed credentials into GA->creds.
- * Updates GA->creds and GA->cred_len.
- * @return CTAP status code (0 is success).
- */
-static uint8_t parse_allow_list(ctap_get_assertion_req_t* GA, CborValue * it)
+uint8_t parse_allow_list(CTAP_getAssertion * GA, CborValue * it)
{
CborValue arr;
size_t len;
int ret;
unsigned int i;
+ CTAP_credentialDescriptor * cred;
if (cbor_value_get_type(it) != CborArrayType)
{
+ printf2(TAG_ERR,"Error, expecting cbor array\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
@@ -978,46 +976,50 @@ static uint8_t parse_allow_list(ctap_get_assertion_req_t* GA, CborValue * it)
ret = cbor_value_get_array_length(it, &len);
check_ret(ret);
- GA->cred_len = 0;
+ GA->credLen = 0;
- for (i = 0; i < len; i++) {
- if (GA->cred_len >= CTAP_CREDENTIAL_LIST_MAX_SIZE) {
+ for(i = 0; i < len; i++)
+ {
+ if (i >= ALLOW_LIST_MAX_SIZE)
+ {
+ printf1(TAG_PARSE,"Error, out of memory for allow list.\n");
return CTAP2_ERR_TOO_MANY_ELEMENTS;
}
- /* Check if this is a credential we should consider. */
- bool cred_valid = false;
- u2f_keyhandle_t* cred = &GA->creds[GA->cred_len];
- ret = ctap_parse_credential_descriptor(&arr, cred, &cred_valid);
+ GA->credLen += 1;
+ cred = &GA->creds[i];
+ ret = parse_credential_descriptor(&arr,cred);
check_retr(ret);
- if (cred_valid) {
- GA->cred_len += 1;
- }
ret = cbor_value_advance(&arr);
check_ret(ret);
+
}
return 0;
}
-uint8_t ctap_parse_get_assertion(ctap_get_assertion_req_t * GA, const in_buffer_t* in_buffer)
+
+uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int length)
{
int ret;
+ unsigned int i;
int key;
size_t map_length;
CborParser parser;
CborValue it,map;
- memset(GA, 0, sizeof(ctap_get_assertion_req_t));
+ memset(GA, 0, sizeof(CTAP_getAssertion));
+ GA->creds = getAssertionState.creds; // Save stack memory
GA->up = 0xff;
- ret = cbor_parser_init(in_buffer->data, in_buffer->len, CborValidateCanonicalFormat, &parser, &it);
+ ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it);
check_ret(ret);
CborType type = cbor_value_get_type(&it);
if (type != CborMapType)
{
+ printf2(TAG_ERR,"Error, expecting cbor map\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
@@ -1027,11 +1029,14 @@ uint8_t ctap_parse_get_assertion(ctap_get_assertion_req_t * GA, const in_buffer_
ret = cbor_value_get_map_length(&it, &map_length);
check_ret(ret);
+ printf1(TAG_GA,"GA map has %d elements\n",map_length);
- for (size_t i = 0; i < map_length; i++) {
+ for (i = 0; i < map_length; i++)
+ {
type = cbor_value_get_type(&map);
if (type != CborIntegerType)
{
+ printf2(TAG_ERR,"Error, expecting int for map key\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = cbor_value_get_int_checked(&map, &key);
@@ -1044,45 +1049,53 @@ uint8_t ctap_parse_get_assertion(ctap_get_assertion_req_t * GA, const in_buffer_
switch(key)
{
- case GET_ASSERTION_TAG_CLIENT_DATA_HASH:
+ case GA_clientDataHash:
+ printf1(TAG_GA,"GA_clientDataHash\n");
- ret = _parse_fixed_byte_string(&map, GA->client_data_hash, CLIENT_DATA_HASH_SIZE);
+ ret = parse_fixed_byte_string(&map, GA->clientDataHash, CLIENT_DATA_HASH_SIZE);
check_retr(ret);
- GA->client_data_hash_present = 1;
+ GA->clientDataHashPresent = 1;
+ printf1(TAG_GA," "); dump_hex1(TAG_GA, GA->clientDataHash, 32);
break;
- case GET_ASSERTION_TAG_RPID:
+ case GA_rpId:
+ printf1(TAG_GA,"GA_rpId\n");
- ret = _parse_rp_id(&GA->rp, &map);
+ ret = parse_rp_id(&GA->rp, &map);
+ printf1(TAG_GA," ID: %s\n", GA->rp.id);
break;
- case GET_ASSERTION_TAG_ALLOW_LIST:
+ case GA_allowList:
+ printf1(TAG_GA,"GA_allowList\n");
ret = parse_allow_list(GA, &map);
check_ret(ret);
GA->allowListPresent = 1;
break;
- case GET_ASSERTION_TAG_EXTENSIONS:
+ case GA_extensions:
+ printf1(TAG_GA,"GA_extensions\n");
ret = ctap_parse_extensions(&map, &GA->extensions);
check_retr(ret);
break;
- case GET_ASSERTION_TAG_OPTIONS:
- ret = _parse_options(&map, &GA->rk, &GA->uv, &GA->up);
+ case GA_options:
+ printf1(TAG_GA,"CTAP_options\n");
+ ret = parse_options(&map, &GA->rk, &GA->uv, &GA->up);
check_retr(ret);
break;
- case GET_ASSERTION_TAG_PIN_AUTH: {
+ case GA_pinAuth: {
+ printf1(TAG_GA,"CTAP_pinAuth\n");
size_t pinSize;
if (cbor_value_get_type(&map) == CborByteStringType &&
cbor_value_get_string_length(&map, &pinSize) == CborNoError &&
pinSize == 0)
{
- GA->pin_auth_empty = 1;
+ GA->pinAuthEmpty = 1;
break;
}
- ret = _parse_fixed_byte_string(&map, GA->pin_auth, 16);
+ ret = parse_fixed_byte_string(&map, GA->pinAuth, 16);
if (CTAP1_ERR_INVALID_LENGTH != ret) // damn microsoft
{
check_retr(ret);
@@ -1094,14 +1107,15 @@ uint8_t ctap_parse_get_assertion(ctap_get_assertion_req_t * GA, const in_buffer_
}
check_retr(ret);
- GA->pin_auth_present = 1;
+ GA->pinAuthPresent = 1;
break;
}
- case GET_ASSERTION_TAG_PIN_PROTOCOL:
+ case GA_pinProtocol:
+ printf1(TAG_GA,"CTAP_pinProtocol\n");
if (cbor_value_get_type(&map) == CborIntegerType)
{
- ret = cbor_value_get_int_checked(&map, &GA->pin_protocol);
+ ret = cbor_value_get_int_checked(&map, &GA->pinProtocol);
check_ret(ret);
}
else
@@ -1111,11 +1125,10 @@ uint8_t ctap_parse_get_assertion(ctap_get_assertion_req_t * GA, const in_buffer_
break;
- default:
- Abort("ctap_parse_get_assertion: bad key.");
}
if (ret != 0)
{
+ printf2(TAG_ERR,"error, parsing failed\n");
return ret;
}
@@ -1127,4 +1140,246 @@ uint8_t ctap_parse_get_assertion(ctap_get_assertion_req_t * GA, const in_buffer_
return 0;
}
+uint8_t parse_cose_key(CborValue * it, COSE_key * cose)
+{
+ CborValue map;
+ size_t map_length;
+ int ret,key;
+ unsigned int i;
+ int xkey = 0,ykey = 0;
+ cose->kty = 0;
+ cose->crv = 0;
+
+
+ CborType type = cbor_value_get_type(it);
+ if (type != CborMapType)
+ {
+ printf2(TAG_ERR,"Error, expecting cbor map\n");
+ return CTAP2_ERR_INVALID_CBOR_TYPE;
+ }
+
+ ret = cbor_value_enter_container(it,&map);
+ check_ret(ret);
+ ret = cbor_value_get_map_length(it, &map_length);
+ check_ret(ret);
+
+ printf1(TAG_PARSE,"cose key has %d elements\n",map_length);
+
+ for (i = 0; i < map_length; i++)
+ {
+ if (cbor_value_get_type(&map) != CborIntegerType)
+ {
+ printf2(TAG_ERR,"Error, expecting int for map key\n");
+ return CTAP2_ERR_INVALID_CBOR_TYPE;
+ }
+
+ ret = cbor_value_get_int_checked(&map, &key);
+ check_ret(ret);
+
+ ret = cbor_value_advance(&map);
+ check_ret(ret);
+
+ switch(key)
+ {
+ case COSE_KEY_LABEL_KTY:
+ printf1(TAG_PARSE,"COSE_KEY_LABEL_KTY\n");
+ if (cbor_value_get_type(&map) == CborIntegerType)
+ {
+ ret = cbor_value_get_int_checked(&map, &cose->kty);
+ check_ret(ret);
+ }
+ else
+ {
+ return CTAP2_ERR_INVALID_CBOR_TYPE;
+ }
+ break;
+ case COSE_KEY_LABEL_ALG:
+ printf1(TAG_PARSE,"COSE_KEY_LABEL_ALG\n");
+ break;
+ case COSE_KEY_LABEL_CRV:
+ printf1(TAG_PARSE,"COSE_KEY_LABEL_CRV\n");
+ if (cbor_value_get_type(&map) == CborIntegerType)
+ {
+ ret = cbor_value_get_int_checked(&map, &cose->crv);
+ check_ret(ret);
+ }
+ else
+ {
+ return CTAP2_ERR_INVALID_CBOR_TYPE;
+ }
+ break;
+ case COSE_KEY_LABEL_X:
+ printf1(TAG_PARSE,"COSE_KEY_LABEL_X\n");
+ ret = parse_fixed_byte_string(&map, cose->pubkey.x, 32);
+ check_retr(ret);
+ xkey = 1;
+
+ break;
+ case COSE_KEY_LABEL_Y:
+ printf1(TAG_PARSE,"COSE_KEY_LABEL_Y\n");
+ ret = parse_fixed_byte_string(&map, cose->pubkey.y, 32);
+ check_retr(ret);
+ ykey = 1;
+
+ break;
+ default:
+ printf1(TAG_PARSE,"Warning, unrecognized cose key option %d\n", key);
+ }
+
+ ret = cbor_value_advance(&map);
+ check_ret(ret);
+ }
+ if (xkey == 0 || ykey == 0 || cose->kty == 0 || cose->crv == 0)
+ {
+ return CTAP2_ERR_MISSING_PARAMETER;
+ }
+ return 0;
+}
+
+uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length)
+{
+ int ret;
+ unsigned int i;
+ int key;
+ size_t map_length;
+ size_t sz;
+ CborParser parser;
+ CborValue it,map;
+
+ memset(CP, 0, sizeof(CTAP_clientPin));
+ ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it);
+ check_ret(ret);
+
+ CborType type = cbor_value_get_type(&it);
+ if (type != CborMapType)
+ {
+ printf2(TAG_ERR,"Error, expecting cbor map\n");
+ return CTAP2_ERR_INVALID_CBOR_TYPE;
+ }
+
+ ret = cbor_value_enter_container(&it,&map);
+ check_ret(ret);
+
+ ret = cbor_value_get_map_length(&it, &map_length);
+ check_ret(ret);
+
+ printf1(TAG_CP,"CP map has %d elements\n",map_length);
+
+ for (i = 0; i < map_length; i++)
+ {
+ type = cbor_value_get_type(&map);
+ if (type != CborIntegerType)
+ {
+ printf2(TAG_ERR,"Error, expecting int for map key\n");
+ return CTAP2_ERR_INVALID_CBOR_TYPE;
+ }
+ ret = cbor_value_get_int_checked(&map, &key);
+ check_ret(ret);
+
+ ret = cbor_value_advance(&map);
+ check_ret(ret);
+ ret = 0;
+
+ switch(key)
+ {
+ case CP_pinProtocol:
+ printf1(TAG_CP,"CP_pinProtocol\n");
+ if (cbor_value_get_type(&map) == CborIntegerType)
+ {
+ cbor_value_get_int_checked(&map, &CP->pinProtocol);
+ check_ret(ret);
+ }
+ else
+ {
+ return CTAP2_ERR_INVALID_CBOR_TYPE;
+ }
+ break;
+ case CP_subCommand:
+ printf1(TAG_CP,"CP_subCommand\n");
+ if (cbor_value_get_type(&map) == CborIntegerType)
+ {
+ cbor_value_get_int_checked(&map, &CP->subCommand);
+ check_ret(ret);
+ }
+ else
+ {
+ return CTAP2_ERR_INVALID_CBOR_TYPE;
+ }
+
+ break;
+ case CP_keyAgreement:
+ printf1(TAG_CP,"CP_keyAgreement\n");
+ ret = parse_cose_key(&map, &CP->keyAgreement);
+ check_retr(ret);
+ CP->keyAgreementPresent = 1;
+ break;
+ case CP_pinAuth:
+ printf1(TAG_CP,"CP_pinAuth\n");
+
+ ret = parse_fixed_byte_string(&map, CP->pinAuth, 16);
+ check_retr(ret);
+ CP->pinAuthPresent = 1;
+ break;
+ case CP_newPinEnc:
+ printf1(TAG_CP,"CP_newPinEnc\n");
+ if (cbor_value_get_type(&map) == CborByteStringType)
+ {
+ ret = cbor_value_calculate_string_length(&map, &sz);
+ check_ret(ret);
+ if (sz > NEW_PIN_ENC_MAX_SIZE || sz < NEW_PIN_ENC_MIN_SIZE)
+ {
+ return CTAP2_ERR_PIN_POLICY_VIOLATION;
+ }
+
+ CP->newPinEncSize = sz;
+ sz = NEW_PIN_ENC_MAX_SIZE;
+ ret = cbor_value_copy_byte_string(&map, CP->newPinEnc, &sz, NULL);
+ check_ret(ret);
+ }
+ else
+ {
+ return CTAP2_ERR_INVALID_CBOR_TYPE;
+ }
+
+ break;
+ case CP_pinHashEnc:
+ printf1(TAG_CP,"CP_pinHashEnc\n");
+
+ ret = parse_fixed_byte_string(&map, CP->pinHashEnc, 16);
+ check_retr(ret);
+ CP->pinHashEncPresent = 1;
+
+ break;
+ case CP_getKeyAgreement:
+ printf1(TAG_CP,"CP_getKeyAgreement\n");
+ if (cbor_value_get_type(&map) != CborBooleanType)
+ {
+ printf2(TAG_ERR,"Error, expecting cbor boolean\n");
+ return CTAP2_ERR_INVALID_CBOR_TYPE;
+ }
+ ret = cbor_value_get_boolean(&map, &CP->getKeyAgreement);
+ check_ret(ret);
+ break;
+ case CP_getRetries:
+ printf1(TAG_CP,"CP_getRetries\n");
+ if (cbor_value_get_type(&map) != CborBooleanType)
+ {
+ printf2(TAG_ERR,"Error, expecting cbor boolean\n");
+ return CTAP2_ERR_INVALID_CBOR_TYPE;
+ }
+ ret = cbor_value_get_boolean(&map, &CP->getRetries);
+ check_ret(ret);
+ break;
+ default:
+ printf1(TAG_CP,"Unknown key %d\n", key);
+ }
+
+ ret = cbor_value_advance(&map);
+ check_ret(ret);
+
+ }
+
+
+ return 0;
+}
diff --git src/fido2/ctap_parse.h src/fido2/ctap_parse.h
index 707ead4..38aa398 100644
--- src/fido2/ctap_parse.h
+++ src/fido2/ctap_parse.h
@@ -7,19 +7,36 @@
#ifndef _CTAP_PARSE_H
#define _CTAP_PARSE_H
-#include "ctap_errors.h"
-#define check_ret(r) \
- do { \
- if ((r) != CborNoError) { \
- return CTAP2_ERR_CBOR_PARSING; \
- } \
- } while(0);
+#define check_ret(r) _check_ret(r,__LINE__, __FILE__);\
+ if ((r) != CborNoError) return CTAP2_ERR_CBOR_PARSING;
-const char* cbor_value_get_type_string(const CborValue *value);
+#define check_retr(r) _check_ret(r,__LINE__, __FILE__);\
+ if ((r) != CborNoError) return r;
+
+
+extern void _check_ret(CborError ret, int line, const char * filename);
+
+
+const char * cbor_value_get_type_string(const CborValue *value);
+
+
+uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val);
+uint8_t parse_pub_key_cred_param(CborValue * val, uint8_t * cred_type, int32_t * alg_type);
+uint8_t parse_pub_key_cred_params(CTAP_makeCredential * MC, CborValue * val);
+uint8_t parse_fixed_byte_string(CborValue * map, uint8_t * dst, unsigned int len);
+uint8_t parse_rp_id(struct rpId * rp, CborValue * val);
+uint8_t parse_rp(struct rpId * rp, CborValue * val);
+uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8_t * up);
+
+uint8_t parse_allow_list(CTAP_getAssertion * GA, CborValue * it);
+uint8_t parse_cose_key(CborValue * it, COSE_key * cose);
+
+
+uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length);
+uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int length);
+uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length);
+uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor * cred);
-uint8_t ctap_parse_make_credential(ctap_make_credential_req_t* MC, CborEncoder * encoder, const in_buffer_t* in_buffer);
-uint8_t ctap_parse_get_assertion(ctap_get_assertion_req_t* GA, const in_buffer_t* in_buffer);
-uint8_t ctap_parse_credential_descriptor(CborValue * arr, u2f_keyhandle_t* cred, bool* cred_valid_out);
#endif
diff --git src/fido2/device.c src/fido2/device.c
index aa335f1..908c765 100644
--- src/fido2/device.c
+++ src/fido2/device.c
@@ -1,35 +1,209 @@
-#include "device.h"
-
-#include <random.h>
-#include <screen.h>
-#include <workflow/confirm.h>
-
-/*
- * Get the AAGUID (identifier of the type of device authenticating).
- */
-void device_read_aaguid(uint8_t * dst) {
- /*
- * Hack:
- * For now, return the AAGUID of a YubiKey 5 (USB-A, No NFC) - ee882879-721c-4913-9775-3dfcce97072a
- * See https://support.yubico.com/support/solutions/articles/15000028710-yubikey-hardware-fido2-aaguids
- */
- const char yubikey_aaguid[16] = {0xee, 0x88, 0x28, 0x79, 0x72, 0x1c, 0x49, 0x13, 0x97, 0x75, 0x3d, 0xfc, 0xce, 0x97, 0x07, 0x2a};
- memcpy(dst, yubikey_aaguid, 16);
-}
-
-int ctap_generate_rng(uint8_t* dst, size_t num) {
- /* Generate bytes in chunks of 32 bytes into the destination buffer. */
- size_t n_32bytes_chunks = num / 32;
- for (size_t i = 0; i < n_32bytes_chunks; ++i) {
- random_32_bytes(dst + i * 32);
- }
- /* Generate the last N bytes as needed. */
- int bytes_missing = num % 32;
- if (bytes_missing) {
- int final_word_offset = num - bytes_missing;
- uint8_t last_bytes[32];
- random_32_bytes(last_bytes);
- memcpy(dst + final_word_offset, last_bytes, bytes_missing);
+// Copyright 2019 SoloKeys Developers
+//
+// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
+// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
+// http://opensource.org/licenses/MIT>, at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+/** device.c
+ *
+ * This contains (weak) implementations
+ * to get FIDO2 working initially on a device. They probably
+ * aren't what you want to keep, but are designed to be replaced
+ * with some other platform specific implementation.
+ *
+ * For real examples, see the STM32L4 implementation and the PC implementation of device.c.
+ *
+*/
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ctaphid.h"
+#include "log.h"
+#include APP_CONFIG
+
+#define RK_NUM 50
+
+struct ResidentKeyStore {
+ CTAP_residentKey rks[RK_NUM];
+} RK_STORE;
+
+
+static bool _up_disabled = false;
+
+static uint8_t _attestation_cert_der[] =
+"\x30\x82\x01\xfb\x30\x82\x01\xa1\xa0\x03\x02\x01\x02\x02\x01\x00\x30\x0a\x06\x08"
+"\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x2c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13"
+"\x02\x55\x53\x31\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x10\x30\x0e"
+"\x06\x03\x55\x04\x0a\x0c\x07\x54\x45\x53\x54\x20\x43\x41\x30\x20\x17\x0d\x31\x38"
+"\x30\x35\x31\x30\x30\x33\x30\x36\x32\x30\x5a\x18\x0f\x32\x30\x36\x38\x30\x34\x32"
+"\x37\x30\x33\x30\x36\x32\x30\x5a\x30\x7c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13"
+"\x02\x55\x53\x31\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x0f\x30\x0d"
+"\x06\x03\x55\x04\x07\x0c\x06\x4c\x61\x75\x72\x65\x6c\x31\x15\x30\x13\x06\x03\x55"
+"\x04\x0a\x0c\x0c\x54\x45\x53\x54\x20\x43\x4f\x4d\x50\x41\x4e\x59\x31\x22\x30\x20"
+"\x06\x03\x55\x04\x0b\x0c\x19\x41\x75\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x6f\x72"
+"\x20\x41\x74\x74\x65\x73\x74\x61\x74\x69\x6f\x6e\x31\x14\x30\x12\x06\x03\x55\x04"
+"\x03\x0c\x0b\x63\x6f\x6e\x6f\x72\x70\x70\x2e\x63\x6f\x6d\x30\x59\x30\x13\x06\x07"
+"\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00"
+"\x04\x45\xa9\x02\xc1\x2e\x9c\x0a\x33\xfa\x3e\x84\x50\x4a\xb8\x02\xdc\x4d\xb9\xaf"
+"\x15\xb1\xb6\x3a\xea\x8d\x3f\x03\x03\x55\x65\x7d\x70\x3f\xb4\x02\xa4\x97\xf4\x83"
+"\xb8\xa6\xf9\x3c\xd0\x18\xad\x92\x0c\xb7\x8a\x5a\x3e\x14\x48\x92\xef\x08\xf8\xca"
+"\xea\xfb\x32\xab\x20\xa3\x62\x30\x60\x30\x46\x06\x03\x55\x1d\x23\x04\x3f\x30\x3d"
+"\xa1\x30\xa4\x2e\x30\x2c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31"
+"\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x10\x30\x0e\x06\x03\x55\x04"
+"\x0a\x0c\x07\x54\x45\x53\x54\x20\x43\x41\x82\x09\x00\xf7\xc9\xec\x89\xf2\x63\x94"
+"\xd9\x30\x09\x06\x03\x55\x1d\x13\x04\x02\x30\x00\x30\x0b\x06\x03\x55\x1d\x0f\x04"
+"\x04\x03\x02\x04\xf0\x30\x0a\x06\x08\x2a\x86\x48\xce\x3d\x04\x03\x02\x03\x48\x00"
+"\x30\x45\x02\x20\x18\x38\xb0\x45\x03\x69\xaa\xa7\xb7\x38\x62\x01\xaf\x24\x97\x5e"
+"\x7e\x74\x64\x1b\xa3\x7b\xf7\xe6\xd3\xaf\x79\x28\xdb\xdc\xa5\x88\x02\x21\x00\xcd"
+"\x06\xf1\xe3\xab\x16\x21\x8e\xd8\xc0\x14\xaf\x09\x4f\x5b\x73\xef\x5e\x9e\x4b\xe7"
+"\x35\xeb\xdd\x9b\x6d\x8f\x7d\xf3\xc4\x3a\xd7";
+
+
+__attribute__((weak)) void device_attestation_read_cert_der(uint8_t * dst){
+ memmove(dst, _attestation_cert_der, device_attestation_cert_der_get_size());
+}
+
+__attribute__((weak)) uint8_t * device_get_attestation_key(){
+ static uint8_t attestation_key[] =
+ "\xcd\x67\xaa\x31\x0d\x09\x1e\xd1\x6e\x7e\x98\x92\xaa"
+ "\x07\x0e\x19\x94\xfc\xd7\x14\xae\x7c\x40\x8f\xb9\x46"
+ "\xb7\x2e\x5f\xe7\x5d\x30";
+ return attestation_key;
+}
+
+__attribute__((weak)) uint16_t device_attestation_cert_der_get_size(){
+ return sizeof(_attestation_cert_der)-1;
+}
+
+__attribute__((weak)) void device_reboot()
+{
+ printf1(TAG_RED, "REBOOT command recieved!\r\n");
+ exit(100);
+}
+
+__attribute__((weak)) void device_set_status(uint32_t status)
+{
+ static uint32_t __device_status = 0;
+ if (status != CTAPHID_STATUS_IDLE && __device_status != status)
+ {
+ ctaphid_update_status(status);
}
+ __device_status = status;
+}
+
+
+__attribute__((weak)) void usbhid_close(){/**/}
+
+
+__attribute__((weak)) void device_init(int argc, char *argv[]){/**/}
+
+__attribute__((weak)) void device_disable_up(bool disable)
+{
+ _up_disabled = disable;
+}
+
+__attribute__((weak)) int ctap_user_presence_test(uint32_t d)
+{
+ if (_up_disabled)
+ {
+ return 2;
+ }
+ return 1;
+}
+
+__attribute__((weak)) int ctap_user_verification(uint8_t arg)
+{
return 1;
}
+
+__attribute__((weak)) uint32_t ctap_atomic_count(uint32_t amount)
+{
+ static uint32_t counter1 = 25;
+ counter1 += (amount + 1);
+ return counter1;
+}
+
+
+__attribute__((weak)) int ctap_generate_rng(uint8_t * dst, size_t num)
+{
+ int i;
+ printf1(TAG_ERR, "Insecure RNG being used.\r\n");
+ for (i = 0; i < num; i++){
+ dst[i] = (uint8_t)rand();
+ }
+}
+
+__attribute__((weak)) int device_is_nfc()
+{
+ return 0;
+}
+
+__attribute__((weak)) void device_wink()
+{
+ printf1(TAG_GREEN,"*WINK*\n");
+}
+
+__attribute__((weak)) void device_set_clock_rate(DEVICE_CLOCK_RATE param){/**/}
+
+static AuthenticatorState _tmp_state = {0};
+__attribute__((weak)) int authenticator_read_state(AuthenticatorState * s){
+ if (_tmp_state.is_initialized != INITIALIZED_MARKER){
+ return 0;
+ }
+ else {
+ memmove(s, &_tmp_state, sizeof(AuthenticatorState));
+ return 1;
+ }
+}
+
+__attribute__((weak)) void authenticator_write_state(AuthenticatorState * s){
+ memmove(&_tmp_state, s, sizeof(AuthenticatorState));
+}
+
+__attribute__((weak)) void ctap_reset_rk()
+{
+ memset(&RK_STORE,0xff,sizeof(RK_STORE));
+}
+
+__attribute__((weak)) uint32_t ctap_rk_size()
+{
+ return RK_NUM;
+}
+
+
+__attribute__((weak)) void ctap_store_rk(int index, CTAP_residentKey * rk)
+{
+ if (index < RK_NUM)
+ {
+ memmove(RK_STORE.rks + index, rk, sizeof(CTAP_residentKey));
+ }
+ else
+ {
+ printf1(TAG_ERR,"Out of bounds for store_rk\r\n");
+ }
+
+}
+
+__attribute__((weak)) void ctap_load_rk(int index, CTAP_residentKey * rk)
+{
+ memmove(rk, RK_STORE.rks + index, sizeof(CTAP_residentKey));
+}
+
+__attribute__((weak)) void ctap_overwrite_rk(int index, CTAP_residentKey * rk)
+{
+ if (index < RK_NUM)
+ {
+ memmove(RK_STORE.rks + index, rk, sizeof(CTAP_residentKey));
+ }
+ else
+ {
+ printf1(TAG_ERR,"Out of bounds for store_rk\r\n");
+ }
+}
+
+__attribute__((weak)) void device_read_aaguid(uint8_t * dst){
+ uint8_t * aaguid = (uint8_t *)"\x00\x76\x63\x1b\xd4\xa0\x42\x7f\x57\x73\x0e\xc7\x1c\x9e\x02\x79";
+ memmove(dst, aaguid, 16);
+}
+
diff --git src/fido2/device.h src/fido2/device.h
index 3197abd..20029dd 100644
--- src/fido2/device.h
+++ src/fido2/device.h
@@ -12,7 +12,7 @@
/** Return a millisecond timestamp. Does not need to be synchronized to anything.
* *Optional* to compile, but will not calculate delays correctly without a correct implementation.
*/
-uint32_t millis(void);
+uint32_t millis();
/** Called by HIDUSB layer to write bytes to the USB HID interface endpoint.
@@ -28,7 +28,7 @@ void usbhid_send(uint8_t * msg);
/** Reboot / power reset the device.
* **Optional** this is not used for FIDO2, and simply won't do anything if not implemented.
*/
-void device_reboot(void);
+void device_reboot();
/** Read AuthenticatorState from nonvolatile memory.
* @param s pointer to AuthenticatorState buffer to be overwritten with contents from NV memory.
@@ -68,7 +68,7 @@ void device_set_status(uint32_t status);
*
* *Optional* to compile and run, but just returns one by default.
*/
-int device_is_button_pressed(void);
+int device_is_button_pressed();
//
// Return 2 for disabled, 1 for user is present, 0 user not present, -1 if cancel is requested.
@@ -85,7 +85,7 @@ int device_is_button_pressed(void);
*
* *Optional*, the default implementation will return 1, unless a FIDO2 operation calls for no UP, where this will then return 2.
*/
-int ctap_user_presence_test(const char* title, const char* prompt, uint32_t delay);
+int ctap_user_presence_test(uint32_t delay);
/** Disable the next user presence test. This is called by FIDO2 layer when a transaction
* requests UP to be disabled. The next call to ctap_user_presence_test should return 2,
@@ -121,14 +121,14 @@ uint32_t ctap_atomic_count(uint32_t amount);
*
* *Optional*, if not implemented, operates on non-persistant RK's.
*/
-void ctap_reset_rk(void);
+void ctap_reset_rk();
/** Return the maximum amount of resident keys that can be stored.
* @return max number of resident keys that can be stored, including already stored RK's.
*
* *Optional*, if not implemented, returns 50.
*/
-uint32_t ctap_rk_size(void);
+uint32_t ctap_rk_size();
/** Store a resident key into an index between [ 0, ctap_rk_size() ).
* Storage should be in non-volatile memory.
@@ -138,7 +138,7 @@ uint32_t ctap_rk_size(void);
*
* *Optional*, if not implemented, operates on non-persistant RK's.
*/
-void ctap_store_rk(int index, ctap_resident_key_t* rk);
+void ctap_store_rk(int index,CTAP_residentKey * rk);
/** Read a resident key from an index into memory
* @param index to read resident key from.
@@ -146,7 +146,7 @@ void ctap_store_rk(int index, ctap_resident_key_t* rk);
*
* *Optional*, if not implemented, operates on non-persistant RK's.
*/
-void ctap_load_rk(int index, ctap_resident_key_t* rk);
+void ctap_load_rk(int index,CTAP_residentKey * rk);
/** Overwrite the RK located in index with a new RK.
* @param index to write resident key to.
@@ -154,7 +154,7 @@ void ctap_load_rk(int index, ctap_resident_key_t* rk);
*
* *Optional*, if not implemented, operates on non-persistant RK's.
*/
-void ctap_overwrite_rk(int index, ctap_resident_key_t* rk);
+void ctap_overwrite_rk(int index,CTAP_residentKey * rk);
/** Called by HID layer to indicate that a wink behavior should be performed.
@@ -162,7 +162,7 @@ void ctap_overwrite_rk(int index, ctap_resident_key_t* rk);
*
* *Optional*.
*/
-void device_wink(void);
+void device_wink();
typedef enum {
DEVICE_LOW_POWER_IDLE = 0,
@@ -191,13 +191,13 @@ void device_set_clock_rate(DEVICE_CLOCK_RATE param);
* 1 - NFC is active, and is powering the chip for a transaction.
* 2 - NFC is available, but not currently being used.
*/
-int device_is_nfc(void);
+int device_is_nfc();
/** Return pointer to attestation key.
* @return pointer to attestation private key, raw encoded. For P256, this is 32 bytes.
*/
-uint8_t * device_get_attestation_key(void);
+uint8_t * device_get_attestation_key();
/** Read the device's attestation certificate into buffer @dst.
* @param dst the destination to write the certificate.
@@ -209,16 +209,11 @@ void device_attestation_read_cert_der(uint8_t * dst);
/** Returns the size in bytes of attestation_cert_der.
* @return number of bytes in attestation_cert_der, not including any C string null byte.
*/
-uint16_t device_attestation_cert_der_get_size(void);
+uint16_t device_attestation_cert_der_get_size();
/** Read the device's 16 byte AAGUID into a buffer.
* @param dst buffer to write 16 byte AAGUID into.
* */
void device_read_aaguid(uint8_t * dst);
-/**
- * @return whether the device has been configured
- * properly and can verify the user.
- */
-bool device_is_uv_initialized(void);
#endif
diff --git src/fido2/extensions.c src/fido2/extensions.c
index 894931e..ceaed72 100644
--- src/fido2/extensions.c
+++ src/fido2/extensions.c
@@ -6,14 +6,10 @@
// copied, modified, or distributed except according to those terms.
#include <stdint.h>
-#include <string.h>
#include "extensions.h"
-#if 0
#include "u2f.h"
#include "ctap.h"
-#endif
#include "wallet.h"
-#if 0
#include "solo.h"
#include "device.h"
@@ -21,7 +17,6 @@
#define htonl(x) (((x & 0xff) << 24) | ((x & 0xff00) << 8) \
| ((x & 0xff0000) >> 8) | ((x & 0xff000000) >> 24) )
-#endif
int is_extension_request(uint8_t * kh, int len)
{
@@ -33,7 +28,6 @@ int is_extension_request(uint8_t * kh, int len)
return memcmp(req->tag, WALLET_TAG, sizeof(WALLET_TAG)-1) == 0;
}
-#if 0
int extension_needs_atomic_count(uint8_t klen, uint8_t * keyh)
{
@@ -172,4 +166,3 @@ int16_t extend_u2f(APDU_HEADER * req, uint8_t * payload, uint32_t len)
end:
return rcode;
}
-#endif
diff --git src/fido2/extensions.h src/fido2/extensions.h
index 66898c4..9bdb203 100644
--- src/fido2/extensions.h
+++ src/fido2/extensions.h
@@ -7,16 +7,13 @@
#ifndef EXTENSIONS_H_
#define EXTENSIONS_H_
#include "u2f.h"
-//#include "apdu.h"
+#include "apdu.h"
-#if 0
int16_t bridge_u2f_to_extensions(uint8_t * chal, uint8_t * appid, uint8_t klen, uint8_t * keyh);
-#endif
// return 1 if request is a wallet request
int is_extension_request(uint8_t * req, int len);
-#if 0
int16_t extend_u2f(APDU_HEADER * req, uint8_t * payload, uint32_t len);
int16_t extend_fido2(CredentialId * credid, uint8_t * output);
@@ -28,6 +25,5 @@ int is_extension_request(uint8_t * kh, int len);
void extension_writeback_init(uint8_t * buffer, uint8_t size);
void extension_writeback(uint8_t * buf, uint8_t size);
-#endif
#endif /* EXTENSIONS_H_ */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment