Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cred: support requesting multiple types #837

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions fuzz/export.gnu
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
fido_cbor_info_uv_modality;
fido_cbor_info_versions_len;
fido_cbor_info_versions_ptr;
fido_cred_append_type;
fido_cred_attstmt_len;
fido_cred_attstmt_ptr;
fido_cred_authdata_len;
Expand Down
3 changes: 2 additions & 1 deletion man/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,9 @@ list(APPEND MAN_ALIAS
fido_credman_metadata_new fido_credman_rp_name
fido_credman_metadata_new fido_credman_rp_new
fido_credman_metadata_new fido_credman_set_dev_rk
fido_cred_set_authdata fido_cred_set_attstmt
fido_cred_set_authdata fido_cred_append_type
fido_cred_set_authdata fido_cred_set_attobj
fido_cred_set_authdata fido_cred_set_attstmt
fido_cred_set_authdata fido_cred_set_authdata_raw
fido_cred_set_authdata fido_cred_set_blob
fido_cred_set_authdata fido_cred_set_clientdata
Expand Down
10 changes: 10 additions & 0 deletions man/fido_cred_new.3
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,16 @@ The
.Fn fido_cred_type
function returns the COSE algorithm of
.Fa cred .
If multiple types were requested through
.Fn fido_cred_append_type ,
the return value of
.Fn fido_cred_type
may change after a successful call to
.Fn fido_cred_set_attobj ,
.Fn fido_cred_set_authdata ,
.Fn fido_cred_set_authdata_raw ,
or
.Fn fido_dev_make_cred .
.Pp
The
.Fn fido_cred_flags
Expand Down
7 changes: 7 additions & 0 deletions man/fido_cred_set_authdata.3
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ typedef enum {
.Fn fido_cred_set_fmt "fido_cred_t *cred" "const char *ptr"
.Ft int
.Fn fido_cred_set_type "fido_cred_t *cred" "int cose_alg"
.Ft int
.Fn fido_cred_append_type "fido_cred_t *cred" "int cose_alg"
.Sh DESCRIPTION
The
.Nm
Expand Down Expand Up @@ -392,6 +394,11 @@ The type of a credential may only be set once.
Note that not all authenticators support COSE_RS256, COSE_ES384, or
COSE_EDDSA.
.Pp
Unlike
.Fn fido_cred_set_type ,
.Fn fido_cred_append_type
may be called repeatedly to request multiple different types.
.Pp
Use of the
.Nm
set of functions may happen in two distinct situations:
Expand Down
35 changes: 33 additions & 2 deletions regress/cred.c
Original file line number Diff line number Diff line change
Expand Up @@ -2847,7 +2847,7 @@ valid_tpm_rs256_cred(bool xfail)
assert(fido_cred_set_uv(c, FIDO_OPT_TRUE) == FIDO_OK);
assert(fido_cred_set_fmt(c, "tpm") == FIDO_OK);
assert(fido_cred_set_attstmt(c, attstmt_tpm_rs256, sizeof(attstmt_tpm_rs256)) == FIDO_OK);
// XXX: RHEL9 has deprecated SHA-1 for signing.
/* XXX: RHEL9 has deprecated SHA-1 for signing */
assert(fido_cred_verify(c) == (xfail ? FIDO_ERR_INVALID_SIG : FIDO_OK));
assert(fido_cred_prot(c) == 0);
assert(fido_cred_pubkey_len(c) == sizeof(pubkey_tpm_rs256));
Expand Down Expand Up @@ -2880,7 +2880,7 @@ valid_tpm_es256_cred(bool xfail)
assert(memcmp(fido_cred_x5c_list_ptr(c, 1), x509_1_tpm_es256, sizeof(x509_1_tpm_es256)) == 0);
assert(fido_cred_x5c_list_len(c, 2) == 0);
assert(fido_cred_x5c_list_ptr(c, 2) == NULL);
// XXX: RHEL9 has deprecated SHA-1 for signing.
/* XXX: RHEL9 has deprecated SHA-1 for signing */
assert(fido_cred_verify(c) == (xfail ? FIDO_ERR_INVALID_SIG : FIDO_OK));
assert(fido_cred_prot(c) == 0);
assert(fido_cred_pubkey_len(c) == sizeof(pubkey_tpm_es256));
Expand Down Expand Up @@ -3021,6 +3021,36 @@ entattest(void)
assert(fido_dev_close(dev) == FIDO_OK);
fido_dev_free(&dev);
}
static void
multiple_cose(void)
{
fido_cred_t *c;

c = alloc_cred();
assert(fido_cred_type(c) == COSE_UNSPEC);
assert(fido_cred_append_type(c, COSE_EDDSA) == FIDO_OK);
assert(fido_cred_type(c) == COSE_EDDSA); /* compat: only algorithm req. */
assert(fido_cred_append_type(c, COSE_ES256) == FIDO_OK);
assert(fido_cred_type(c) == COSE_EDDSA); /* compat: first algorithm req. */
assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK);
assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK);
assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK);
assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK);
assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK);
assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK);
assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK);
assert(fido_cred_set_fmt(c, "packed") == FIDO_OK);
assert(fido_cred_type(c) == COSE_ES256); /* actual algorithm used */
assert(fido_cred_verify(c) == FIDO_OK);
assert(fido_cred_prot(c) == 0);
assert(fido_cred_pubkey_len(c) == sizeof(pubkey));
assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0);
assert(fido_cred_id_len(c) == sizeof(id));
assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0);
assert(fido_cred_aaguid_len(c) == sizeof(aaguid));
assert(memcmp(fido_cred_aaguid_ptr(c), aaguid, sizeof(aaguid)) == 0);
free_cred(c);
}

int
main(void)
Expand Down Expand Up @@ -3058,6 +3088,7 @@ main(void)
attestation_object();
makecred();
entattest();
multiple_cose();

exit(0);
}
61 changes: 41 additions & 20 deletions src/cbor.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018-2022 Yubico AB. All rights reserved.
* Copyright (c) 2018-2024 Yubico AB. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
* SPDX-License-Identifier: BSD-2-Clause
Expand Down Expand Up @@ -468,18 +468,16 @@ cbor_encode_user_entity(const fido_user_t *user)
return (item);
}

cbor_item_t *
static cbor_item_t *
cbor_encode_pubkey_param(int cose_alg)
{
cbor_item_t *item = NULL;
cbor_item_t *body = NULL;
struct cbor_pair alg;
int ok = -1;

memset(&alg, 0, sizeof(alg));

if ((item = cbor_new_definite_array(1)) == NULL ||
(body = cbor_new_definite_map(2)) == NULL ||
if ((body = cbor_new_definite_map(2)) == NULL ||
cose_alg > -1 || cose_alg < INT16_MIN)
goto fail;

Expand All @@ -496,27 +494,50 @@ cbor_encode_pubkey_param(int cose_alg)
}

if (cbor_map_add(body, alg) == false ||
cbor_add_string(body, "type", "public-key") < 0 ||
cbor_array_push(item, body) == false)
cbor_add_string(body, "type", "public-key") < 0)
goto fail;

ok = 0;
fail:
if (ok < 0) {
if (item != NULL) {
cbor_decref(&item);
item = NULL;
if (body != NULL) {
cbor_decref(&body);
body = NULL;
}
}

if (body != NULL)
cbor_decref(&body);
if (alg.key != NULL)
cbor_decref(&alg.key);
if (alg.value != NULL)
cbor_decref(&alg.value);

return (body);
}

cbor_item_t *
cbor_encode_pubkey_param_array(const fido_int_array_t *algs)
{
cbor_item_t *item = NULL;
cbor_item_t *body = NULL;

if ((item = cbor_new_definite_array(algs->len)) == NULL)
goto fail;

for (size_t i = 0; i < algs->len; i++) {
if ((body = cbor_encode_pubkey_param(algs->ptr[i])) == NULL ||
cbor_array_push(item, body) == false)
goto fail;
cbor_decref(&body);
}

return (item);
fail:
if (body != NULL)
cbor_decref(&body);
if (item != NULL)
cbor_decref(&item);

return (NULL);
}

cbor_item_t *
Expand Down Expand Up @@ -1073,8 +1094,8 @@ cbor_decode_pubkey(const cbor_item_t *item, int *type, void *key)
}

static int
decode_attcred(const unsigned char **buf, size_t *len, int cose_alg,
fido_attcred_t *attcred)
decode_attcred(const unsigned char **buf, size_t *len,
const fido_int_array_t *algs, fido_attcred_t *attcred)
{
cbor_item_t *item = NULL;
struct cbor_load_result cbor;
Expand Down Expand Up @@ -1115,9 +1136,9 @@ decode_attcred(const unsigned char **buf, size_t *len, int cose_alg,
goto fail;
}

if (attcred->type != cose_alg) {
fido_log_debug("%s: cose_alg mismatch (%d != %d)", __func__,
attcred->type, cose_alg);
if (!fido_int_array_contains(algs, attcred->type)) {
fido_log_debug("%s: unexpected cose alg (%d)", __func__,
attcred->type);
goto fail;
}

Expand Down Expand Up @@ -1160,7 +1181,7 @@ 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,
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",
Expand Down Expand Up @@ -1346,7 +1367,7 @@ decode_assert_extensions(const unsigned char **buf, size_t *len,
}

int
cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg,
cbor_decode_cred_authdata(const cbor_item_t *item, const fido_int_array_t *algs,
fido_blob_t *authdata_cbor, fido_authdata_t *authdata,
fido_attcred_t *attcred, fido_cred_ext_t *authdata_ext)
{
Expand Down Expand Up @@ -1380,7 +1401,7 @@ cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg,

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

Expand Down
50 changes: 37 additions & 13 deletions src/cred.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ parse_makecred_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
fido_log_debug("%s: fido_blob_decode", __func__);
return (-1);
}
return (cbor_decode_cred_authdata(val, cred->type,
return (cbor_decode_cred_authdata(val, &cred->type,
&cred->authdata_cbor, &cred->authdata, &cred->attcred,
&cred->authdata_ext));
case 3: /* attestation statement */
Expand Down Expand Up @@ -64,17 +64,17 @@ fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin,
memset(&f, 0, sizeof(f));
memset(argv, 0, sizeof(argv));

if (cred->cdh.ptr == NULL || cred->type == 0) {
fido_log_debug("%s: cdh=%p, type=%d", __func__,
(void *)cred->cdh.ptr, cred->type);
if (cred->cdh.ptr == NULL || cred->type.len == 0) {
fido_log_debug("%s: cdh=%p, type.len=%zu", __func__,
(void *)cred->cdh.ptr, cred->type.len);
r = FIDO_ERR_INVALID_ARGUMENT;
goto fail;
}

if ((argv[0] = fido_blob_encode(&cred->cdh)) == NULL ||
(argv[1] = cbor_encode_rp_entity(&cred->rp)) == NULL ||
(argv[2] = cbor_encode_user_entity(&cred->user)) == NULL ||
(argv[3] = cbor_encode_pubkey_param(cred->type)) == NULL) {
(argv[3] = cbor_encode_pubkey_param_array(&cred->type)) == NULL) {
fido_log_debug("%s: cbor encode", __func__);
r = FIDO_ERR_INTERNAL;
goto fail;
Expand Down Expand Up @@ -588,13 +588,14 @@ fido_cred_reset_tx(fido_cred_t *cred)
free(cred->user.icon);
free(cred->user.name);
free(cred->user.display_name);
free(cred->type.ptr);
fido_cred_empty_exclude_list(cred);

memset(&cred->rp, 0, sizeof(cred->rp));
memset(&cred->user, 0, sizeof(cred->user));
memset(&cred->ext, 0, sizeof(cred->ext));
memset(&cred->type, 0, sizeof(cred->type));

cred->type = 0;
cred->rk = FIDO_OPT_OMIT;
cred->uv = FIDO_OPT_OMIT;
cred->ea.mode = 0;
Expand Down Expand Up @@ -643,7 +644,7 @@ fido_cred_set_authdata(fido_cred_t *cred, const unsigned char *ptr, size_t len)
goto fail;
}

if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
if (cbor_decode_cred_authdata(item, &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 @@ -684,7 +685,7 @@ fido_cred_set_authdata_raw(fido_cred_t *cred, const unsigned char *ptr,
goto fail;
}

if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
if (cbor_decode_cred_authdata(item, &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 @@ -1071,23 +1072,46 @@ fido_cred_set_fmt(fido_cred_t *cred, const char *fmt)
}

int
fido_cred_set_type(fido_cred_t *cred, int cose_alg)
fido_cred_append_type(fido_cred_t *cred, int cose_alg)
{
if (cred->type != 0)
return (FIDO_ERR_INVALID_ARGUMENT);
int *list_ptr;

if (cose_alg != COSE_ES256 && cose_alg != COSE_ES384 &&
cose_alg != COSE_RS256 && cose_alg != COSE_EDDSA)
return (FIDO_ERR_INVALID_ARGUMENT);

cred->type = cose_alg;
if (cred->type.len == SIZE_MAX)
return (FIDO_ERR_INVALID_ARGUMENT);

if ((list_ptr = recallocarray(cred->type.ptr, cred->type.len,
cred->type.len + 1, sizeof(*list_ptr))) == NULL)
return (FIDO_ERR_INTERNAL);

list_ptr[cred->type.len++] = cose_alg;
cred->type.ptr = list_ptr;

return (FIDO_OK);
}

int
fido_cred_set_type(fido_cred_t *cred, int cose_alg)
{
if (cred->type.len != 0)
return (FIDO_ERR_INVALID_ARGUMENT);

return (fido_cred_append_type(cred, cose_alg));
}

int
fido_cred_type(const fido_cred_t *cred)
{
return (cred->type);
if (cred->type.len == 0)
return (COSE_UNSPEC);

if (cred->attcred.type != COSE_UNSPEC)
return (cred->attcred.type);
LDVG marked this conversation as resolved.
Show resolved Hide resolved

return (cred->type.ptr[0]); /* compat: return (first) requested type */
}

uint8_t
Expand Down
9 changes: 3 additions & 6 deletions src/credman.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2022 Yubico AB. All rights reserved.
* Copyright (c) 2019-2024 Yubico AB. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
* SPDX-License-Identifier: BSD-2-Clause
Expand Down Expand Up @@ -281,11 +281,8 @@ credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg)
case 7:
return (cbor_decode_cred_id(val, &cred->attcred.id));
case 8:
if (cbor_decode_pubkey(val, &cred->attcred.type,
&cred->attcred.pubkey) < 0)
return (-1);
cred->type = cred->attcred.type; /* XXX */
return (0);
return (cbor_decode_pubkey(val, &cred->attcred.type,
&cred->attcred.pubkey));
case 10:
if (cbor_decode_uint64(val, &prot) < 0 || prot > INT_MAX ||
fido_cred_set_prot(cred, (int)prot) != FIDO_OK)
Expand Down
Loading
Loading