Skip to content

Commit

Permalink
Allow multiple cred pub key types for windows hello during credential…
Browse files Browse the repository at this point in the history
… creation

Add a member of type `fido_blob_t` named `type_winhello` to `fido_cred_t`.

Add `fido_cred_set_type_winhello` which takes a pointer to an array of
32 bit signed integers and length. The old `type` member of
`fido_cred_t` is set to the first element of this array of public key
types. It is stored as a `fido_blob_t`.

Create a copy of `decode_attcred` as `decode_attcred_multiple_cose` to
handle using the list of key types.

Create a copy of `cbor_decode_cred_authdata` as `cbor_decode_cred_authdata_multiple_cose` to
handle using the array of key types.
  • Loading branch information
bobomb committed Aug 31, 2023
1 parent 79fd7bd commit 43407bc
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 48 deletions.
124 changes: 124 additions & 0 deletions src/cbor.c
Original file line number Diff line number Diff line change
Expand Up @@ -1132,6 +1132,80 @@ decode_attcred(const unsigned char **buf, size_t *len, int cose_alg,
return (ok);
}

static int
decode_attcred_multiple_cose(const unsigned char **buf, size_t *len, fido_blob_t *cose_algs,
fido_attcred_t *attcred)
{
cbor_item_t *item = NULL;
struct cbor_load_result cbor;
uint16_t id_len;
int ok = -1;
size_t cose_count = 0;
int *cose_algs_arr = NULL;
bool cose_match = false;

fido_log_xxd(*buf, *len, "%s", __func__);

if (fido_buf_read(buf, len, &attcred->aaguid,
sizeof(attcred->aaguid)) < 0) {
fido_log_debug("%s: fido_buf_read aaguid", __func__);
return (-1);
}

if (fido_buf_read(buf, len, &id_len, sizeof(id_len)) < 0) {
fido_log_debug("%s: fido_buf_read id_len", __func__);
return (-1);
}

attcred->id.len = (size_t)be16toh(id_len);
if ((attcred->id.ptr = malloc(attcred->id.len)) == NULL)
return (-1);

fido_log_debug("%s: attcred->id.len=%zu", __func__, attcred->id.len);

if (fido_buf_read(buf, len, attcred->id.ptr, attcred->id.len) < 0) {
fido_log_debug("%s: fido_buf_read id", __func__);
return (-1);
}

if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
fido_log_debug("%s: cbor_load", __func__);
goto fail;
}

if (cbor_decode_pubkey(item, &attcred->type, &attcred->pubkey) < 0) {
fido_log_debug("%s: cbor_decode_pubkey", __func__);
goto fail;
}

cose_count = cose_algs->len / sizeof(int);
cose_algs_arr = (int*)cose_algs->ptr;
for (size_t i = 0; i < cose_count; i++) {
int cose_alg = cose_algs_arr[i];
if (attcred->type != cose_alg) {
fido_log_debug("%s: cose_algs mismatch (%d != %d)", __func__,
attcred->type, cose_alg);
} else {
cose_match = true;
}
}

if (!cose_match) {
fido_log_debug("%s: cose_alg failed to match any", __func__);
goto fail;
}

*buf += cbor.read;
*len -= cbor.read;

ok = 0;
fail:
if (item != NULL)
cbor_decref(&item);

return (ok);
}

static int
decode_cred_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg)
{
Expand Down Expand Up @@ -1337,6 +1411,56 @@ cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg,
return (FIDO_OK);
}

int
cbor_decode_cred_authdata_multiple_cose(const cbor_item_t *item, fido_blob_t *cose_algs,
fido_blob_t *authdata_cbor, fido_authdata_t *authdata,
fido_attcred_t *attcred, fido_cred_ext_t *authdata_ext)
{
const unsigned char *buf = NULL;
size_t len;
size_t alloc_len;

if (cbor_isa_bytestring(item) == false ||
cbor_bytestring_is_definite(item) == false) {
fido_log_debug("%s: cbor type", __func__);
return (-1);
}

if (authdata_cbor->ptr != NULL ||
(authdata_cbor->len = cbor_serialize_alloc(item,
&authdata_cbor->ptr, &alloc_len)) == 0) {
fido_log_debug("%s: cbor_serialize_alloc", __func__);
return (-1);
}

buf = cbor_bytestring_handle(item);
len = cbor_bytestring_length(item);
fido_log_xxd(buf, len, "%s", __func__);

if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) {
fido_log_debug("%s: fido_buf_read", __func__);
return (-1);
}

authdata->sigcount = be32toh(authdata->sigcount);

if (attcred != NULL) {
if ((authdata->flags & CTAP_AUTHDATA_ATT_CRED) == 0 ||
decode_attcred_multiple_cose(&buf, &len, cose_algs, attcred) < 0)
return (-1);
}

if (authdata_ext != NULL) {
if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0 &&
decode_cred_extensions(&buf, &len, authdata_ext) < 0)
return (-1);
}

/* XXX we should probably ensure that len == 0 at this point */

return (FIDO_OK);
}

int
cbor_decode_assert_authdata(const cbor_item_t *item, fido_blob_t *authdata_cbor,
fido_authdata_t *authdata, fido_assert_extattr_t *authdata_ext)
Expand Down
44 changes: 44 additions & 0 deletions src/cred.c
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,7 @@ fido_cred_reset_tx(fido_cred_t *cred)
fido_blob_reset(&cred->cdh);
fido_blob_reset(&cred->user.id);
fido_blob_reset(&cred->blob);
fido_blob_reset(&cred->type_winhello);

free(cred->rp.id);
free(cred->rp.name);
Expand Down Expand Up @@ -994,6 +995,37 @@ fido_cred_set_type(fido_cred_t *cred, int cose_alg)

cred->type = cose_alg;

return (FIDO_OK);
}

int fido_cred_set_type_winhello(fido_cred_t *cred, const unsigned char *ptr, size_t len)
{
int *cose_algos = NULL;
size_t count = len / sizeof(int);

if (cred->type != 0)
return (FIDO_ERR_INVALID_ARGUMENT);

if (!fido_blob_is_empty(&cred->type_winhello))
return (FIDO_ERR_INVALID_ARGUMENT);

if (fido_blob_set(&cred->type_winhello, ptr, len) < 0)
return (FIDO_ERR_INVALID_ARGUMENT);

cose_algos = (int*)cred->type_winhello.ptr;

for (size_t i = 0; i < count; i++) {
int cose_alg = cose_algos[i];

if (cose_alg != COSE_ES256 && cose_alg != COSE_ES384 &&
cose_alg != COSE_RS256 && cose_alg != COSE_EDDSA) {
fido_blob_reset(&cred->type_winhello);
return (FIDO_ERR_INVALID_ARGUMENT);
}
}

cred->type = cose_algos[0];

return (FIDO_OK);
}

Expand All @@ -1003,6 +1035,18 @@ fido_cred_type(const fido_cred_t *cred)
return (cred->type);
}

const unsigned char *
fido_cred_type_winhello_ptr(const fido_cred_t *cred)
{
return (cred->type_winhello.ptr);
}

size_t
fido_cred_type_winhello_len(const fido_cred_t *cred)
{
return (cred->type_winhello.len);
}

uint8_t
fido_cred_flags(const fido_cred_t *cred)
{
Expand Down
2 changes: 2 additions & 0 deletions src/extern.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ int cbor_decode_attstmt(const cbor_item_t *, fido_attstmt_t *);
int cbor_decode_bool(const cbor_item_t *, bool *);
int cbor_decode_cred_authdata(const cbor_item_t *, int, fido_blob_t *,
fido_authdata_t *, fido_attcred_t *, fido_cred_ext_t *);
int cbor_decode_cred_authdata_multiple_cose(const cbor_item_t *,
fido_blob_t *, fido_blob_t *, fido_authdata_t *, fido_attcred_t *, fido_cred_ext_t *);
int cbor_decode_assert_authdata(const cbor_item_t *, fido_blob_t *,
fido_authdata_t *, fido_assert_extattr_t *);
int cbor_decode_cred_id(const cbor_item_t *, fido_blob_t *);
Expand Down
3 changes: 3 additions & 0 deletions src/fido.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ const unsigned char *fido_cred_id_ptr(const fido_cred_t *);
const unsigned char *fido_cred_largeblob_key_ptr(const fido_cred_t *);
const unsigned char *fido_cred_pubkey_ptr(const fido_cred_t *);
const unsigned char *fido_cred_sig_ptr(const fido_cred_t *);
const unsigned char *fido_cred_type_winhello_ptr(const fido_cred_t *cred);
const unsigned char *fido_cred_user_id_ptr(const fido_cred_t *);
const unsigned char *fido_cred_x5c_ptr(const fido_cred_t *);

Expand Down Expand Up @@ -166,6 +167,7 @@ int fido_cred_set_rk(fido_cred_t *, fido_opt_t);
int fido_cred_set_rp(fido_cred_t *, const char *, const char *);
int fido_cred_set_sig(fido_cred_t *, const unsigned char *, size_t);
int fido_cred_set_type(fido_cred_t *, int);
int fido_cred_set_type_winhello(fido_cred_t *cred, const unsigned char *ptr, size_t len);
int fido_cred_set_uv(fido_cred_t *, fido_opt_t);
int fido_cred_type(const fido_cred_t *);
int fido_cred_set_user(fido_cred_t *, const unsigned char *, size_t,
Expand Down Expand Up @@ -224,6 +226,7 @@ size_t fido_cred_largeblob_key_len(const fido_cred_t *);
size_t fido_cred_pin_minlen(const fido_cred_t *);
size_t fido_cred_pubkey_len(const fido_cred_t *);
size_t fido_cred_sig_len(const fido_cred_t *);
size_t fido_cred_type_winhello_len(const fido_cred_t *cred);
size_t fido_cred_user_id_len(const fido_cred_t *);
size_t fido_cred_x5c_len(const fido_cred_t *);

Expand Down
37 changes: 19 additions & 18 deletions src/fido/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,24 +167,25 @@ typedef struct fido_cred_ext {
} fido_cred_ext_t;

typedef struct fido_cred {
fido_blob_t cd; /* client data */
fido_blob_t cdh; /* client data hash */
fido_rp_t rp; /* relying party */
fido_user_t user; /* user entity */
fido_blob_array_t excl; /* list of credential ids to exclude */
fido_opt_t rk; /* resident key */
fido_opt_t uv; /* user verification */
fido_cred_ext_t ext; /* extensions */
int type; /* cose algorithm */
char *fmt; /* credential format */
fido_cred_ext_t authdata_ext; /* decoded extensions */
fido_blob_t authdata_cbor; /* cbor-encoded payload */
fido_blob_t authdata_raw; /* cbor-decoded payload */
fido_authdata_t authdata; /* decoded authdata payload */
fido_attcred_t attcred; /* returned credential (key + id) */
fido_attstmt_t attstmt; /* attestation statement (x509 + sig) */
fido_blob_t largeblob_key; /* decoded large blob key */
fido_blob_t blob; /* CTAP 2.1 credBlob */
fido_blob_t cd; /* client data */
fido_blob_t cdh; /* client data hash */
fido_rp_t rp; /* relying party */
fido_user_t user; /* user entity */
fido_blob_array_t excl; /* list of credential ids to exclude */
fido_opt_t rk; /* resident key */
fido_opt_t uv; /* user verification */
fido_cred_ext_t ext; /* extensions */
int type; /* cose algorithm */
char *fmt; /* credential format */
fido_cred_ext_t authdata_ext; /* decoded extensions */
fido_blob_t authdata_cbor; /* cbor-encoded payload */
fido_blob_t authdata_raw; /* cbor-decoded payload */
fido_authdata_t authdata; /* decoded authdata payload */
fido_attcred_t attcred; /* returned credential (key + id) */
fido_attstmt_t attstmt; /* attestation statement (x509 + sig) */
fido_blob_t largeblob_key; /* decoded large blob key */
fido_blob_t blob; /* CTAP 2.1 credBlob */
fido_blob_t type_winhello; /* list of cose algorithms, windows hello supports multiple */
} fido_cred_t;

typedef struct fido_assert_extattr {
Expand Down
81 changes: 51 additions & 30 deletions src/winhello.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ struct winhello_assert {
struct winhello_cred {
WEBAUTHN_RP_ENTITY_INFORMATION rp;
WEBAUTHN_USER_ENTITY_INFORMATION user;
WEBAUTHN_COSE_CREDENTIAL_PARAMETER alg;
WEBAUTHN_COSE_CREDENTIAL_PARAMETERS cose;
WEBAUTHN_CLIENT_DATA cd;
WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS opt;
Expand Down Expand Up @@ -355,30 +354,32 @@ pack_user(wchar_t **name, wchar_t **icon, wchar_t **display_name,
}

static int
pack_cose(WEBAUTHN_COSE_CREDENTIAL_PARAMETER *alg,
WEBAUTHN_COSE_CREDENTIAL_PARAMETERS *cose, int type)
pack_cose(WEBAUTHN_COSE_CREDENTIAL_PARAMETERS *out, const fido_cred_t *cred)
{
switch (type) {
case COSE_ES256:
alg->lAlg = WEBAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256;
break;
case COSE_ES384:
alg->lAlg = WEBAUTHN_COSE_ALGORITHM_ECDSA_P384_WITH_SHA384;
break;
case COSE_EDDSA:
alg->lAlg = -8; /* XXX */;
break;
case COSE_RS256:
alg->lAlg = WEBAUTHN_COSE_ALGORITHM_RSASSA_PKCS1_V1_5_WITH_SHA256;
break;
default:
fido_log_debug("%s: type %d", __func__, type);
return -1;
if (!fido_blob_is_empty(&cred->type_winhello)) {
/* array of credential types was set */
size_t count = cred->type_winhello.len / sizeof(int);
int *cose_algos = (int*)cred->type_winhello.ptr;
WEBAUTHN_COSE_CREDENTIAL_PARAMETER *alg = calloc(count, sizeof(WEBAUTHN_COSE_CREDENTIAL_PARAMETER));

fido_log_debug("%s: cose algo count:%d", __func__, count);
for(size_t i = 0; i < count; i++) {
alg[i].lAlg = cose_algos[i];
alg[i].dwVersion = WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION;
alg[i].pwszCredentialType = WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY;
fido_log_debug("%s: cose algo:%d", __func__, cose_algos[i]);
}
out->cCredentialParameters = (DWORD)count;
out->pCredentialParameters = alg;
} else {
/* Only single credential type */
WEBAUTHN_COSE_CREDENTIAL_PARAMETER *alg = calloc(1, sizeof(WEBAUTHN_COSE_CREDENTIAL_PARAMETER));
alg->lAlg = cred->type;
alg->dwVersion = WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION;
alg->pwszCredentialType = WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY;
out->cCredentialParameters = 1;
out->pCredentialParameters = alg;
}
alg->dwVersion = WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION;
alg->pwszCredentialType = WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY;
cose->cCredentialParameters = 1;
cose->pCredentialParameters = alg;

return 0;
}
Expand Down Expand Up @@ -701,7 +702,7 @@ translate_fido_cred(struct winhello_cred *ctx, const fido_cred_t *cred,
fido_log_debug("%s: pack_user", __func__);
return FIDO_ERR_INTERNAL;
}
if (pack_cose(&ctx->alg, &ctx->cose, cred->type) < 0) {
if (pack_cose(&ctx->cose, cred) < 0) {
fido_log_debug("%s: pack_cose", __func__);
return FIDO_ERR_INTERNAL;
}
Expand Down Expand Up @@ -763,12 +764,30 @@ decode_attobj(const cbor_item_t *key, const cbor_item_t *val, void *arg)
fido_log_debug("%s: fido_blob_decode", __func__);
goto fail;
}
if (cbor_decode_cred_authdata(val, cred->type,
&cred->authdata_cbor, &cred->authdata, &cred->attcred,
&cred->authdata_ext) < 0) {
fido_log_debug("%s: cbor_decode_cred_authdata",
__func__);
goto fail;

if (!fido_blob_is_empty(&cred->type_winhello)) {
/* array of credential types was set */
if (cbor_decode_cred_authdata_multiple_cose(val, &cred->type_winhello,
&cred->authdata_cbor, &cred->authdata, &cred->attcred,
&cred->authdata_ext) < 0) {
fido_log_debug("%s: cbor_decode_cred_authdata_multiple_cose failed",
__func__);
goto fail;
}
fido_log_debug("%s: cbor_decode_cred_authdata_multiple_cose returned attcred.type %d, cred.type %d",
__func__,
cred->attcred.type,
cred->type);
cred->type = cred->attcred.type;
} else {
/* Only single credential type */
if (cbor_decode_cred_authdata(val, cred->type,
&cred->authdata_cbor, &cred->authdata, &cred->attcred,
&cred->authdata_ext) < 0) {
fido_log_debug("%s: cbor_decode_cred_authdata",
__func__);
goto fail;
}
}
}

Expand Down Expand Up @@ -880,6 +899,8 @@ winhello_cred_free(struct winhello_cred *ctx)
free(e->pvExtension);
}
free(ctx->opt.Extensions.pExtensions);
if (ctx->cose.cCredentialParameters > 0)
free(ctx->cose.pCredentialParameters);
free(ctx);
}

Expand Down

0 comments on commit 43407bc

Please sign in to comment.