upstream: Extends the SK API to accept a set of key/value options
for all operations. These are intended to future-proof the API a little by making it easier to specify additional fields for without having to change the API version for each. At present, only two options are defined: one to explicitly specify the device for an operation (rather than accepting the middleware's autoselection) and another to specify the FIDO2 username that may be used when generating a resident key. These new options may be invoked at key generation time via ssh-keygen -O This also implements a suggestion from Markus to avoid "int" in favour of uint32_t for the algorithm argument in the API, to make implementation of ssh-sk-client/helper a little easier. feedback, fixes and ok markus@ OpenBSD-Commit-ID: 973ce11704609022ab36abbdeb6bc23c8001eabc
This commit is contained in:
parent
2ab335712d
commit
c312ca077c
47
PROTOCOL.u2f
47
PROTOCOL.u2f
|
@ -233,7 +233,7 @@ support for the common case of USB HID security keys internally.
|
|||
|
||||
The middleware library need only expose a handful of functions:
|
||||
|
||||
#define SSH_SK_VERSION_MAJOR 0x00030000 /* API version */
|
||||
#define SSH_SK_VERSION_MAJOR 0x00040000 /* API version */
|
||||
#define SSH_SK_VERSION_MAJOR_MASK 0xffff0000
|
||||
|
||||
/* Flags */
|
||||
|
@ -245,6 +245,11 @@ The middleware library need only expose a handful of functions:
|
|||
#define SSH_SK_ECDSA 0x00
|
||||
#define SSH_SK_ED25519 0x01
|
||||
|
||||
/* Error codes */
|
||||
#define SSH_SK_ERR_GENERAL -1
|
||||
#define SSH_SK_ERR_UNSUPPORTED -2
|
||||
#define SSH_SK_ERR_PIN_REQUIRED -3
|
||||
|
||||
struct sk_enroll_response {
|
||||
uint8_t *public_key;
|
||||
size_t public_key_len;
|
||||
|
@ -266,35 +271,63 @@ The middleware library need only expose a handful of functions:
|
|||
};
|
||||
|
||||
struct sk_resident_key {
|
||||
uint8_t alg;
|
||||
uint32_t alg;
|
||||
size_t slot;
|
||||
char *application;
|
||||
struct sk_enroll_response key;
|
||||
};
|
||||
|
||||
struct sk_option {
|
||||
char *name;
|
||||
char *value;
|
||||
uint8_t important;
|
||||
};
|
||||
|
||||
/* Return the version of the middleware API */
|
||||
uint32_t sk_api_version(void);
|
||||
|
||||
/* Enroll a U2F key (private key generation) */
|
||||
int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
|
||||
int sk_enroll(uint32_t alg,
|
||||
const uint8_t *challenge, size_t challenge_len,
|
||||
const char *application, uint8_t flags, const char *pin,
|
||||
struct sk_option **options,
|
||||
struct sk_enroll_response **enroll_response);
|
||||
|
||||
/* Sign a challenge */
|
||||
int sk_sign(int alg, const uint8_t *message, size_t message_len,
|
||||
int sk_sign(uint32_t alg, const uint8_t *message, size_t message_len,
|
||||
const char *application,
|
||||
const uint8_t *key_handle, size_t key_handle_len,
|
||||
uint8_t flags, const char *pin,
|
||||
uint8_t flags, const char *pin, struct sk_option **options,
|
||||
struct sk_sign_response **sign_response);
|
||||
|
||||
/* Enumerate all resident keys */
|
||||
int sk_load_resident_keys(const char *pin,
|
||||
int sk_load_resident_keys(const char *pin, struct sk_option **options,
|
||||
struct sk_resident_key ***rks, size_t *nrks);
|
||||
|
||||
The SSH_SK_VERSION_MAJOR should be incremented for each incompatible
|
||||
API change.
|
||||
|
||||
In OpenSSH, these will be invoked by using a similar mechanism to
|
||||
The options may be used to pass miscellaneous options to the middleware
|
||||
as a NULL-terminated array of pointers to struct sk_option. The middleware
|
||||
may ignore unsupported or unknown options unless the "important" flag is
|
||||
set, in which case it should return failure if an unsupported option is
|
||||
requested.
|
||||
|
||||
At present the following options names are supported:
|
||||
|
||||
"device"
|
||||
|
||||
Specifies a specific FIDO device on which to perform the
|
||||
operation. The value in this field is interpreted by the
|
||||
middleware but it would be typical to specify a path to
|
||||
a /dev node for the device in question.
|
||||
|
||||
"user"
|
||||
|
||||
Specifies the FIDO2 username used when enrolling a key,
|
||||
overriding OpenSSH's default of using an all-zero username.
|
||||
|
||||
In OpenSSH, the middleware will be invoked by using a similar mechanism to
|
||||
ssh-pkcs11-helper to provide address-space containment of the
|
||||
middleware from ssh-agent.
|
||||
|
||||
|
|
23
sk-api.h
23
sk-api.h
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: sk-api.h,v 1.6 2019/12/30 09:24:45 djm Exp $ */
|
||||
/* $OpenBSD: sk-api.h,v 1.7 2020/01/06 02:00:46 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2019 Google LLC
|
||||
*
|
||||
|
@ -58,30 +58,37 @@ struct sk_sign_response {
|
|||
};
|
||||
|
||||
struct sk_resident_key {
|
||||
uint8_t alg;
|
||||
uint32_t alg;
|
||||
size_t slot;
|
||||
char *application;
|
||||
struct sk_enroll_response key;
|
||||
};
|
||||
|
||||
#define SSH_SK_VERSION_MAJOR 0x00030000 /* current API version */
|
||||
struct sk_option {
|
||||
char *name;
|
||||
char *value;
|
||||
uint8_t required;
|
||||
};
|
||||
|
||||
#define SSH_SK_VERSION_MAJOR 0x00040000 /* current API version */
|
||||
#define SSH_SK_VERSION_MAJOR_MASK 0xffff0000
|
||||
|
||||
/* Return the version of the middleware API */
|
||||
uint32_t sk_api_version(void);
|
||||
|
||||
/* Enroll a U2F key (private key generation) */
|
||||
int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
|
||||
int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
|
||||
const char *application, uint8_t flags, const char *pin,
|
||||
struct sk_enroll_response **enroll_response);
|
||||
struct sk_option **options, struct sk_enroll_response **enroll_response);
|
||||
|
||||
/* Sign a challenge */
|
||||
int sk_sign(int alg, const uint8_t *message, size_t message_len,
|
||||
int sk_sign(uint32_t alg, const uint8_t *message, size_t message_len,
|
||||
const char *application, const uint8_t *key_handle, size_t key_handle_len,
|
||||
uint8_t flags, const char *pin, struct sk_sign_response **sign_response);
|
||||
uint8_t flags, const char *pin, struct sk_option **options,
|
||||
struct sk_sign_response **sign_response);
|
||||
|
||||
/* Enumerate all resident keys */
|
||||
int sk_load_resident_keys(const char *pin,
|
||||
int sk_load_resident_keys(const char *pin, struct sk_option **options,
|
||||
struct sk_resident_key ***rks, size_t *nrks);
|
||||
|
||||
#endif /* _SK_API_H */
|
||||
|
|
194
sk-usbhid.c
194
sk-usbhid.c
|
@ -54,7 +54,7 @@
|
|||
} while (0)
|
||||
#endif
|
||||
|
||||
#define SK_VERSION_MAJOR 0x00030000 /* current API version */
|
||||
#define SK_VERSION_MAJOR 0x00040000 /* current API version */
|
||||
|
||||
/* Flags */
|
||||
#define SK_USER_PRESENCE_REQD 0x01
|
||||
|
@ -91,12 +91,18 @@ struct sk_sign_response {
|
|||
};
|
||||
|
||||
struct sk_resident_key {
|
||||
uint8_t alg;
|
||||
uint32_t alg;
|
||||
size_t slot;
|
||||
char *application;
|
||||
struct sk_enroll_response key;
|
||||
};
|
||||
|
||||
struct sk_option {
|
||||
char *name;
|
||||
char *value;
|
||||
uint8_t required;
|
||||
};
|
||||
|
||||
/* If building as part of OpenSSH, then rename exported functions */
|
||||
#if !defined(SK_STANDALONE)
|
||||
#define sk_api_version ssh_sk_api_version
|
||||
|
@ -109,17 +115,18 @@ struct sk_resident_key {
|
|||
uint32_t sk_api_version(void);
|
||||
|
||||
/* Enroll a U2F key (private key generation) */
|
||||
int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
|
||||
int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
|
||||
const char *application, uint8_t flags, const char *pin,
|
||||
struct sk_enroll_response **enroll_response);
|
||||
struct sk_option **options, struct sk_enroll_response **enroll_response);
|
||||
|
||||
/* Sign a challenge */
|
||||
int sk_sign(int alg, const uint8_t *message, size_t message_len,
|
||||
int sk_sign(uint32_t alg, const uint8_t *message, size_t message_len,
|
||||
const char *application, const uint8_t *key_handle, size_t key_handle_len,
|
||||
uint8_t flags, const char *pin, struct sk_sign_response **sign_response);
|
||||
uint8_t flags, const char *pin, struct sk_option **options,
|
||||
struct sk_sign_response **sign_response);
|
||||
|
||||
/* Load resident keys */
|
||||
int sk_load_resident_keys(const char *pin,
|
||||
int sk_load_resident_keys(const char *pin, struct sk_option **options,
|
||||
struct sk_resident_key ***rks, size_t *nrks);
|
||||
|
||||
static void skdebug(const char *func, const char *fmt, ...)
|
||||
|
@ -235,15 +242,27 @@ try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len,
|
|||
|
||||
/* Iterate over configured devices looking for a specific key handle */
|
||||
static fido_dev_t *
|
||||
find_device(const uint8_t *message, size_t message_len, const char *application,
|
||||
const uint8_t *key_handle, size_t key_handle_len)
|
||||
find_device(const char *path, const uint8_t *message, size_t message_len,
|
||||
const char *application, const uint8_t *key_handle, size_t key_handle_len)
|
||||
{
|
||||
fido_dev_info_t *devlist = NULL;
|
||||
fido_dev_t *dev = NULL;
|
||||
size_t devlist_len = 0, i;
|
||||
const char *path;
|
||||
int r;
|
||||
|
||||
if (path != NULL) {
|
||||
if ((dev = fido_dev_new()) == NULL) {
|
||||
skdebug(__func__, "fido_dev_new failed");
|
||||
return NULL;
|
||||
}
|
||||
if ((r = fido_dev_open(dev, path)) != FIDO_OK) {
|
||||
skdebug(__func__, "fido_dev_open failed");
|
||||
fido_dev_free(&dev);
|
||||
return NULL;
|
||||
}
|
||||
return dev;
|
||||
}
|
||||
|
||||
if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
|
||||
skdebug(__func__, "fido_dev_info_new failed");
|
||||
goto out;
|
||||
|
@ -402,7 +421,7 @@ pack_public_key_ed25519(const fido_cred_t *cred,
|
|||
}
|
||||
|
||||
static int
|
||||
pack_public_key(int alg, const fido_cred_t *cred,
|
||||
pack_public_key(uint32_t alg, const fido_cred_t *cred,
|
||||
struct sk_enroll_response *response)
|
||||
{
|
||||
switch(alg) {
|
||||
|
@ -431,10 +450,45 @@ fidoerr_to_skerr(int fidoerr)
|
|||
}
|
||||
}
|
||||
|
||||
static int
|
||||
check_enroll_options(struct sk_option **options, char **devicep,
|
||||
uint8_t *user_id, size_t user_id_len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (options == NULL)
|
||||
return 0;
|
||||
for (i = 0; options[i] != NULL; i++) {
|
||||
if (strcmp(options[i]->name, "device") == 0) {
|
||||
if ((*devicep = strdup(options[i]->value)) == NULL) {
|
||||
skdebug(__func__, "strdup device failed");
|
||||
return -1;
|
||||
}
|
||||
skdebug(__func__, "requested device %s", *devicep);
|
||||
} if (strcmp(options[i]->name, "user") == 0) {
|
||||
if (strlcpy(user_id, options[i]->value, user_id_len) >=
|
||||
user_id_len) {
|
||||
skdebug(__func__, "user too long");
|
||||
return -1;
|
||||
}
|
||||
skdebug(__func__, "requested user %s",
|
||||
(char *)user_id);
|
||||
} else {
|
||||
skdebug(__func__, "requested unsupported option %s",
|
||||
options[i]->name);
|
||||
if (options[i]->required) {
|
||||
skdebug(__func__, "unknown required option");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
|
||||
sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
|
||||
const char *application, uint8_t flags, const char *pin,
|
||||
struct sk_enroll_response **enroll_response)
|
||||
struct sk_option **options, struct sk_enroll_response **enroll_response)
|
||||
{
|
||||
fido_cred_t *cred = NULL;
|
||||
fido_dev_t *dev = NULL;
|
||||
|
@ -454,6 +508,11 @@ sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
|
|||
skdebug(__func__, "enroll_response == NULL");
|
||||
goto out;
|
||||
}
|
||||
memset(user_id, 0, sizeof(user_id));
|
||||
if (check_enroll_options(options, &device,
|
||||
user_id, sizeof(user_id)) != 0)
|
||||
goto out; /* error already logged */
|
||||
|
||||
*enroll_response = NULL;
|
||||
switch(alg) {
|
||||
#ifdef WITH_OPENSSL
|
||||
|
@ -468,7 +527,7 @@ sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
|
|||
skdebug(__func__, "unsupported key type %d", alg);
|
||||
goto out;
|
||||
}
|
||||
if ((device = pick_first_device()) == NULL) {
|
||||
if (device == NULL && (device = pick_first_device()) == NULL) {
|
||||
skdebug(__func__, "pick_first_device failed");
|
||||
goto out;
|
||||
}
|
||||
|
@ -477,7 +536,6 @@ sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
|
|||
skdebug(__func__, "fido_cred_new failed");
|
||||
goto out;
|
||||
}
|
||||
memset(user_id, 0, sizeof(user_id));
|
||||
if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) {
|
||||
skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
|
||||
goto out;
|
||||
|
@ -654,7 +712,8 @@ pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
|
|||
}
|
||||
|
||||
static int
|
||||
pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response)
|
||||
pack_sig(uint32_t alg, fido_assert_t *assert,
|
||||
struct sk_sign_response *response)
|
||||
{
|
||||
switch(alg) {
|
||||
#ifdef WITH_OPENSSL
|
||||
|
@ -668,13 +727,42 @@ pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response)
|
|||
}
|
||||
}
|
||||
|
||||
/* Checks sk_options for sk_sign() and sk_load_resident_keys() */
|
||||
static int
|
||||
check_sign_load_resident_options(struct sk_option **options, char **devicep)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (options == NULL)
|
||||
return 0;
|
||||
for (i = 0; options[i] != NULL; i++) {
|
||||
if (strcmp(options[i]->name, "device") == 0) {
|
||||
if ((*devicep = strdup(options[i]->value)) == NULL) {
|
||||
skdebug(__func__, "strdup device failed");
|
||||
return -1;
|
||||
}
|
||||
skdebug(__func__, "requested device %s", *devicep);
|
||||
} else {
|
||||
skdebug(__func__, "requested unsupported option %s",
|
||||
options[i]->name);
|
||||
if (options[i]->required) {
|
||||
skdebug(__func__, "unknown required option");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sk_sign(int alg, const uint8_t *message, size_t message_len,
|
||||
sk_sign(uint32_t alg, const uint8_t *message, size_t message_len,
|
||||
const char *application,
|
||||
const uint8_t *key_handle, size_t key_handle_len,
|
||||
uint8_t flags, const char *pin, struct sk_sign_response **sign_response)
|
||||
uint8_t flags, const char *pin, struct sk_option **options,
|
||||
struct sk_sign_response **sign_response)
|
||||
{
|
||||
fido_assert_t *assert = NULL;
|
||||
char *device = NULL;
|
||||
fido_dev_t *dev = NULL;
|
||||
struct sk_sign_response *response = NULL;
|
||||
int ret = SSH_SK_ERR_GENERAL;
|
||||
|
@ -689,8 +777,10 @@ sk_sign(int alg, const uint8_t *message, size_t message_len,
|
|||
goto out;
|
||||
}
|
||||
*sign_response = NULL;
|
||||
if ((dev = find_device(message, message_len, application, key_handle,
|
||||
key_handle_len)) == NULL) {
|
||||
if (check_sign_load_resident_options(options, &device) != 0)
|
||||
goto out; /* error already logged */
|
||||
if ((dev = find_device(device, message, message_len,
|
||||
application, key_handle, key_handle_len)) == NULL) {
|
||||
skdebug(__func__, "couldn't find device for key handle");
|
||||
goto out;
|
||||
}
|
||||
|
@ -737,6 +827,7 @@ sk_sign(int alg, const uint8_t *message, size_t message_len,
|
|||
response = NULL;
|
||||
ret = 0;
|
||||
out:
|
||||
free(device);
|
||||
if (response != NULL) {
|
||||
free(response->sig_r);
|
||||
free(response->sig_s);
|
||||
|
@ -789,6 +880,7 @@ read_rks(const char *devpath, const char *pin,
|
|||
}
|
||||
skdebug(__func__, "get metadata for %s failed: %s",
|
||||
devpath, fido_strerr(r));
|
||||
ret = fidoerr_to_skerr(r);
|
||||
goto out;
|
||||
}
|
||||
skdebug(__func__, "existing %llu, remaining %llu",
|
||||
|
@ -904,7 +996,7 @@ read_rks(const char *devpath, const char *pin,
|
|||
}
|
||||
|
||||
int
|
||||
sk_load_resident_keys(const char *pin,
|
||||
sk_load_resident_keys(const char *pin, struct sk_option **options,
|
||||
struct sk_resident_key ***rksp, size_t *nrksp)
|
||||
{
|
||||
int ret = SSH_SK_ERR_GENERAL, r = -1;
|
||||
|
@ -912,39 +1004,57 @@ sk_load_resident_keys(const char *pin,
|
|||
size_t i, ndev = 0, nrks = 0;
|
||||
const fido_dev_info_t *di;
|
||||
struct sk_resident_key **rks = NULL;
|
||||
char *device = NULL;
|
||||
*rksp = NULL;
|
||||
*nrksp = 0;
|
||||
|
||||
if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
|
||||
skdebug(__func__, "fido_dev_info_new failed");
|
||||
goto out;
|
||||
}
|
||||
if ((r = fido_dev_info_manifest(devlist,
|
||||
MAX_FIDO_DEVICES, &ndev)) != FIDO_OK) {
|
||||
skdebug(__func__, "fido_dev_info_manifest failed: %s",
|
||||
fido_strerr(r));
|
||||
goto out;
|
||||
}
|
||||
for (i = 0; i < ndev; i++) {
|
||||
if ((di = fido_dev_info_ptr(devlist, i)) == NULL) {
|
||||
skdebug(__func__, "no dev info at %zu", i);
|
||||
continue;
|
||||
}
|
||||
skdebug(__func__, "trying %s", fido_dev_info_path(di));
|
||||
if ((r = read_rks(fido_dev_info_path(di), pin,
|
||||
&rks, &nrks)) != 0) {
|
||||
if (check_sign_load_resident_options(options, &device) != 0)
|
||||
goto out; /* error already logged */
|
||||
if (device != NULL) {
|
||||
skdebug(__func__, "trying %s", device);
|
||||
if ((r = read_rks(device, pin, &rks, &nrks)) != 0) {
|
||||
skdebug(__func__, "read_rks failed for %s",
|
||||
fido_dev_info_path(di));
|
||||
continue;
|
||||
ret = r;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
/* Try all devices */
|
||||
if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
|
||||
skdebug(__func__, "fido_dev_info_new failed");
|
||||
goto out;
|
||||
}
|
||||
if ((r = fido_dev_info_manifest(devlist,
|
||||
MAX_FIDO_DEVICES, &ndev)) != FIDO_OK) {
|
||||
skdebug(__func__, "fido_dev_info_manifest failed: %s",
|
||||
fido_strerr(r));
|
||||
goto out;
|
||||
}
|
||||
for (i = 0; i < ndev; i++) {
|
||||
if ((di = fido_dev_info_ptr(devlist, i)) == NULL) {
|
||||
skdebug(__func__, "no dev info at %zu", i);
|
||||
continue;
|
||||
}
|
||||
skdebug(__func__, "trying %s", fido_dev_info_path(di));
|
||||
if ((r = read_rks(fido_dev_info_path(di), pin,
|
||||
&rks, &nrks)) != 0) {
|
||||
skdebug(__func__, "read_rks failed for %s",
|
||||
fido_dev_info_path(di));
|
||||
/* remember last error */
|
||||
ret = r;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* success */
|
||||
ret = 0;
|
||||
/* success, unless we have no keys but a specific error */
|
||||
if (nrks > 0 || ret == SSH_SK_ERR_GENERAL)
|
||||
ret = 0;
|
||||
*rksp = rks;
|
||||
*nrksp = nrks;
|
||||
rks = NULL;
|
||||
nrks = 0;
|
||||
out:
|
||||
free(device);
|
||||
for (i = 0; i < nrks; i++) {
|
||||
free(rks[i]->application);
|
||||
freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: ssh-add.c,v 1.148 2019/12/30 09:22:49 djm Exp $ */
|
||||
/* $OpenBSD: ssh-add.c,v 1.149 2020/01/06 02:00:46 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
|
@ -549,7 +549,8 @@ load_resident_keys(int agent_fd, const char *skprovider, int qflag)
|
|||
char *fp;
|
||||
|
||||
pass = read_passphrase("Enter PIN for security key: ", RP_ALLOW_STDIN);
|
||||
if ((r = sshsk_load_resident(skprovider, pass, &keys, &nkeys)) != 0) {
|
||||
if ((r = sshsk_load_resident(skprovider, NULL, pass,
|
||||
&keys, &nkeys)) != 0) {
|
||||
error("Unable to load resident keys: %s", ssh_err(r));
|
||||
return r;
|
||||
}
|
||||
|
|
21
ssh-keygen.1
21
ssh-keygen.1
|
@ -1,4 +1,4 @@
|
|||
.\" $OpenBSD: ssh-keygen.1,v 1.188 2020/01/03 07:33:33 jmc Exp $
|
||||
.\" $OpenBSD: ssh-keygen.1,v 1.189 2020/01/06 02:00:46 djm Exp $
|
||||
.\"
|
||||
.\" Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
|
@ -35,7 +35,7 @@
|
|||
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd $Mdocdate: January 3 2020 $
|
||||
.Dd $Mdocdate: January 6 2020 $
|
||||
.Dt SSH-KEYGEN 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -462,8 +462,18 @@ section may be specified.
|
|||
.Pp
|
||||
When generating a key that will be hosted on a FIDO authenticator, this
|
||||
flag may be used to specify key-specific options.
|
||||
Two FIDO authenticator options are supported at present:
|
||||
The FIDO authenticator options are supported at present are:
|
||||
.Pp
|
||||
.Cm application
|
||||
overrides the default FIDO application/origin string of
|
||||
.Dq ssh: .
|
||||
This option may be useful when generating host or domain-specific resident
|
||||
keys.
|
||||
.Cm device
|
||||
explicitly specify a device to generate the key on, rather than accepting
|
||||
the authenticator middleware's automatic selection.
|
||||
.Xr fido 4
|
||||
device to use, rather than letting the token middleware select one.
|
||||
.Cm no-touch-required
|
||||
indicates that the generated private key should not require touch
|
||||
events (user presence) when making signatures.
|
||||
|
@ -478,6 +488,11 @@ Resident keys may be supported on FIDO2 tokens and typically require that
|
|||
a PIN be set on the token prior to generation.
|
||||
Resident keys may be loaded off the token using
|
||||
.Xr ssh-add 1 .
|
||||
.Cm user
|
||||
allows specification of a username to be associated with a resident key,
|
||||
overriding the empty default username.
|
||||
Specifying a username may be useful when generating multiple resident keys
|
||||
for the same application name.
|
||||
.Pp
|
||||
The
|
||||
.Fl O
|
||||
|
|
39
ssh-keygen.c
39
ssh-keygen.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: ssh-keygen.c,v 1.381 2020/01/02 22:40:09 djm Exp $ */
|
||||
/* $OpenBSD: ssh-keygen.c,v 1.382 2020/01/06 02:00:46 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
|
@ -2915,7 +2915,7 @@ skip_ssh_url_preamble(const char *s)
|
|||
}
|
||||
|
||||
static int
|
||||
do_download_sk(const char *skprovider)
|
||||
do_download_sk(const char *skprovider, const char *device)
|
||||
{
|
||||
struct sshkey **keys;
|
||||
size_t nkeys, i;
|
||||
|
@ -2927,7 +2927,8 @@ do_download_sk(const char *skprovider)
|
|||
fatal("Cannot download keys without provider");
|
||||
|
||||
pin = read_passphrase("Enter PIN for security key: ", RP_ALLOW_STDIN);
|
||||
if ((r = sshsk_load_resident(skprovider, pin, &keys, &nkeys)) != 0) {
|
||||
if ((r = sshsk_load_resident(skprovider, device, pin,
|
||||
&keys, &nkeys)) != 0) {
|
||||
freezero(pin, strlen(pin));
|
||||
error("Unable to load resident keys: %s", ssh_err(r));
|
||||
return -1;
|
||||
|
@ -3067,6 +3068,7 @@ main(int argc, char **argv)
|
|||
int do_gen_candidates = 0, do_screen_candidates = 0, download_sk = 0;
|
||||
unsigned long long cert_serial = 0;
|
||||
char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL;
|
||||
char *sk_application = NULL, *sk_device = NULL, *sk_user = NULL;
|
||||
size_t i, nopts = 0;
|
||||
u_int32_t bits = 0;
|
||||
uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD;
|
||||
|
@ -3396,8 +3398,17 @@ main(int argc, char **argv)
|
|||
}
|
||||
if (pkcs11provider != NULL)
|
||||
do_download(pw);
|
||||
if (download_sk)
|
||||
return do_download_sk(sk_provider);
|
||||
if (download_sk) {
|
||||
for (i = 0; i < nopts; i++) {
|
||||
if (strncasecmp(opts[i], "device=", 7) == 0) {
|
||||
sk_device = xstrdup(opts[i] + 7);
|
||||
} else {
|
||||
fatal("Option \"%s\" is unsupported for "
|
||||
"FIDO authenticator download", opts[i]);
|
||||
}
|
||||
}
|
||||
return do_download_sk(sk_provider, sk_device);
|
||||
}
|
||||
if (print_fingerprint || print_bubblebabble)
|
||||
do_fingerprint(pw);
|
||||
if (change_passphrase)
|
||||
|
@ -3484,6 +3495,13 @@ main(int argc, char **argv)
|
|||
sk_flags &= ~SSH_SK_USER_PRESENCE_REQD;
|
||||
} else if (strcasecmp(opts[i], "resident") == 0) {
|
||||
sk_flags |= SSH_SK_RESIDENT_KEY;
|
||||
} else if (strncasecmp(opts[i], "device=", 7) == 0) {
|
||||
sk_device = xstrdup(opts[i] + 7);
|
||||
} else if (strncasecmp(opts[i], "user=", 5) == 0) {
|
||||
sk_user = xstrdup(opts[i] + 5);
|
||||
} else if (strncasecmp(opts[i],
|
||||
"application=", 12) == 0) {
|
||||
sk_application = xstrdup(opts[i] + 12);
|
||||
} else {
|
||||
fatal("Option \"%s\" is unsupported for "
|
||||
"FIDO authenticator enrollment", opts[i]);
|
||||
|
@ -3495,14 +3513,11 @@ main(int argc, char **argv)
|
|||
}
|
||||
passphrase = NULL;
|
||||
for (i = 0 ; i < 3; i++) {
|
||||
if (!quiet) {
|
||||
printf("You may need to touch your security "
|
||||
"key to authorize key generation.\n");
|
||||
}
|
||||
fflush(stdout);
|
||||
r = sshsk_enroll(type, sk_provider,
|
||||
cert_key_id == NULL ? "ssh:" : cert_key_id,
|
||||
sk_flags, passphrase, NULL, &private, NULL);
|
||||
r = sshsk_enroll(type, sk_provider, sk_device,
|
||||
sk_application == NULL ? "ssh:" : sk_application,
|
||||
sk_user, sk_flags, passphrase, NULL,
|
||||
&private, NULL);
|
||||
if (r == 0)
|
||||
break;
|
||||
if (r != SSH_ERR_KEY_WRONG_PASSPHRASE)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: ssh-sk-client.c,v 1.3 2019/12/30 09:23:28 djm Exp $ */
|
||||
/* $OpenBSD: ssh-sk-client.c,v 1.4 2020/01/06 02:00:46 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2019 Google LLC
|
||||
*
|
||||
|
@ -282,8 +282,9 @@ sshsk_sign(const char *provider, struct sshkey *key,
|
|||
}
|
||||
|
||||
int
|
||||
sshsk_enroll(int type, const char *provider_path, const char *application,
|
||||
uint8_t flags, const char *pin, struct sshbuf *challenge_buf,
|
||||
sshsk_enroll(int type, const char *provider_path, const char *device,
|
||||
const char *application, const char *userid, uint8_t flags,
|
||||
const char *pin, struct sshbuf *challenge_buf,
|
||||
struct sshkey **keyp, struct sshbuf *attest)
|
||||
{
|
||||
int oerrno, r = SSH_ERR_INTERNAL_ERROR;
|
||||
|
@ -311,7 +312,9 @@ sshsk_enroll(int type, const char *provider_path, const char *application,
|
|||
if ((r = sshbuf_put_u32(req, SSH_SK_HELPER_ENROLL)) != 0 ||
|
||||
(r = sshbuf_put_u32(req, (u_int)type)) != 0 ||
|
||||
(r = sshbuf_put_cstring(req, provider_path)) != 0 ||
|
||||
(r = sshbuf_put_cstring(req, device)) != 0 ||
|
||||
(r = sshbuf_put_cstring(req, application)) != 0 ||
|
||||
(r = sshbuf_put_cstring(req, userid)) != 0 ||
|
||||
(r = sshbuf_put_u8(req, flags)) != 0 ||
|
||||
(r = sshbuf_put_cstring(req, pin)) != 0 ||
|
||||
(r = sshbuf_put_stringb(req, challenge_buf)) != 0) {
|
||||
|
@ -358,8 +361,8 @@ sshsk_enroll(int type, const char *provider_path, const char *application,
|
|||
}
|
||||
|
||||
int
|
||||
sshsk_load_resident(const char *provider_path, const char *pin,
|
||||
struct sshkey ***keysp, size_t *nkeysp)
|
||||
sshsk_load_resident(const char *provider_path, const char *device,
|
||||
const char *pin, struct sshkey ***keysp, size_t *nkeysp)
|
||||
{
|
||||
int oerrno, r = SSH_ERR_INTERNAL_ERROR;
|
||||
struct sshbuf *kbuf = NULL, *req = NULL, *resp = NULL;
|
||||
|
@ -378,6 +381,7 @@ sshsk_load_resident(const char *provider_path, const char *pin,
|
|||
|
||||
if ((r = sshbuf_put_u32(req, SSH_SK_HELPER_LOAD_RESIDENT)) != 0 ||
|
||||
(r = sshbuf_put_cstring(req, provider_path)) != 0 ||
|
||||
(r = sshbuf_put_cstring(req, device)) != 0 ||
|
||||
(r = sshbuf_put_cstring(req, pin)) != 0) {
|
||||
error("%s: compose: %s", __func__, ssh_err(r));
|
||||
goto out;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: ssh-sk-helper.c,v 1.6 2019/12/30 09:23:28 djm Exp $ */
|
||||
/* $OpenBSD: ssh-sk-helper.c,v 1.7 2020/01/06 02:00:46 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2019 Google LLC
|
||||
*
|
||||
|
@ -77,6 +77,17 @@ reply_error(int r, char *fmt, ...)
|
|||
return resp;
|
||||
}
|
||||
|
||||
/* If the specified string is zero length, then free it and replace with NULL */
|
||||
static void
|
||||
null_empty(char **s)
|
||||
{
|
||||
if (s == NULL || *s == NULL || **s != '\0')
|
||||
return;
|
||||
|
||||
free(*s);
|
||||
*s = NULL;
|
||||
}
|
||||
|
||||
static struct sshbuf *
|
||||
process_sign(struct sshbuf *req)
|
||||
{
|
||||
|
@ -108,10 +119,7 @@ process_sign(struct sshbuf *req)
|
|||
"msg len %zu, compat 0x%lx", __progname, sshkey_type(key),
|
||||
provider, msglen, (u_long)compat);
|
||||
|
||||
if (*pin == 0) {
|
||||
free(pin);
|
||||
pin = NULL;
|
||||
}
|
||||
null_empty(&pin);
|
||||
|
||||
if ((r = sshsk_sign(provider, key, &sig, &siglen,
|
||||
message, msglen, compat, pin)) != 0) {
|
||||
|
@ -138,7 +146,7 @@ process_enroll(struct sshbuf *req)
|
|||
{
|
||||
int r;
|
||||
u_int type;
|
||||
char *provider, *application, *pin;
|
||||
char *provider, *application, *pin, *device, *userid;
|
||||
uint8_t flags;
|
||||
struct sshbuf *challenge, *attest, *kbuf, *resp;
|
||||
struct sshkey *key;
|
||||
|
@ -149,7 +157,9 @@ process_enroll(struct sshbuf *req)
|
|||
|
||||
if ((r = sshbuf_get_u32(req, &type)) != 0 ||
|
||||
(r = sshbuf_get_cstring(req, &provider, NULL)) != 0 ||
|
||||
(r = sshbuf_get_cstring(req, &device, NULL)) != 0 ||
|
||||
(r = sshbuf_get_cstring(req, &application, NULL)) != 0 ||
|
||||
(r = sshbuf_get_cstring(req, &userid, NULL)) != 0 ||
|
||||
(r = sshbuf_get_u8(req, &flags)) != 0 ||
|
||||
(r = sshbuf_get_cstring(req, &pin, NULL)) != 0 ||
|
||||
(r = sshbuf_froms(req, &challenge)) != 0)
|
||||
|
@ -163,13 +173,12 @@ process_enroll(struct sshbuf *req)
|
|||
sshbuf_free(challenge);
|
||||
challenge = NULL;
|
||||
}
|
||||
if (*pin == 0) {
|
||||
free(pin);
|
||||
pin = NULL;
|
||||
}
|
||||
null_empty(&device);
|
||||
null_empty(&userid);
|
||||
null_empty(&pin);
|
||||
|
||||
if ((r = sshsk_enroll((int)type, provider, application, flags, pin,
|
||||
challenge, &key, attest)) != 0) {
|
||||
if ((r = sshsk_enroll((int)type, provider, device, application, userid,
|
||||
flags, pin, challenge, &key, attest)) != 0) {
|
||||
resp = reply_error(r, "Enrollment failed: %s", ssh_err(r));
|
||||
goto out;
|
||||
}
|
||||
|
@ -200,7 +209,7 @@ static struct sshbuf *
|
|||
process_load_resident(struct sshbuf *req)
|
||||
{
|
||||
int r;
|
||||
char *provider, *pin;
|
||||
char *provider, *pin, *device;
|
||||
struct sshbuf *kbuf, *resp;
|
||||
struct sshkey **keys = NULL;
|
||||
size_t nkeys = 0, i;
|
||||
|
@ -209,17 +218,17 @@ process_load_resident(struct sshbuf *req)
|
|||
fatal("%s: sshbuf_new failed", __progname);
|
||||
|
||||
if ((r = sshbuf_get_cstring(req, &provider, NULL)) != 0 ||
|
||||
(r = sshbuf_get_cstring(req, &device, NULL)) != 0 ||
|
||||
(r = sshbuf_get_cstring(req, &pin, NULL)) != 0)
|
||||
fatal("%s: buffer error: %s", __progname, ssh_err(r));
|
||||
if (sshbuf_len(req) != 0)
|
||||
fatal("%s: trailing data in request", __progname);
|
||||
|
||||
if (*pin == 0) {
|
||||
free(pin);
|
||||
pin = NULL;
|
||||
}
|
||||
null_empty(&device);
|
||||
null_empty(&pin);
|
||||
|
||||
if ((r = sshsk_load_resident(provider, pin, &keys, &nkeys)) != 0) {
|
||||
if ((r = sshsk_load_resident(provider, device, pin,
|
||||
&keys, &nkeys)) != 0) {
|
||||
resp = reply_error(r, " sshsk_load_resident failed: %s",
|
||||
ssh_err(r));
|
||||
goto out;
|
||||
|
|
121
ssh-sk.c
121
ssh-sk.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: ssh-sk.c,v 1.23 2019/12/30 09:24:45 djm Exp $ */
|
||||
/* $OpenBSD: ssh-sk.c,v 1.24 2020/01/06 02:00:47 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2019 Google LLC
|
||||
*
|
||||
|
@ -53,29 +53,32 @@ struct sshsk_provider {
|
|||
/* Enroll a U2F key (private key generation) */
|
||||
int (*sk_enroll)(int alg, const uint8_t *challenge,
|
||||
size_t challenge_len, const char *application, uint8_t flags,
|
||||
const char *pin, struct sk_enroll_response **enroll_response);
|
||||
const char *pin, struct sk_option **opts,
|
||||
struct sk_enroll_response **enroll_response);
|
||||
|
||||
/* Sign a challenge */
|
||||
int (*sk_sign)(int alg, const uint8_t *message, size_t message_len,
|
||||
const char *application,
|
||||
const uint8_t *key_handle, size_t key_handle_len,
|
||||
uint8_t flags, const char *pin,
|
||||
uint8_t flags, const char *pin, struct sk_option **opts,
|
||||
struct sk_sign_response **sign_response);
|
||||
|
||||
/* Enumerate resident keys */
|
||||
int (*sk_load_resident_keys)(const char *pin,
|
||||
int (*sk_load_resident_keys)(const char *pin, struct sk_option **opts,
|
||||
struct sk_resident_key ***rks, size_t *nrks);
|
||||
};
|
||||
|
||||
/* Built-in version */
|
||||
int ssh_sk_enroll(int alg, const uint8_t *challenge,
|
||||
size_t challenge_len, const char *application, uint8_t flags,
|
||||
const char *pin, struct sk_enroll_response **enroll_response);
|
||||
const char *pin, struct sk_option **opts,
|
||||
struct sk_enroll_response **enroll_response);
|
||||
int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len,
|
||||
const char *application,
|
||||
const uint8_t *key_handle, size_t key_handle_len,
|
||||
uint8_t flags, const char *pin, struct sk_sign_response **sign_response);
|
||||
int ssh_sk_load_resident_keys(const char *pin,
|
||||
uint8_t flags, const char *pin, struct sk_option **opts,
|
||||
struct sk_sign_response **sign_response);
|
||||
int ssh_sk_load_resident_keys(const char *pin, struct sk_option **opts,
|
||||
struct sk_resident_key ***rks, size_t *nrks);
|
||||
|
||||
static void
|
||||
|
@ -339,9 +342,80 @@ skerr_to_ssherr(int skerr)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sshsk_free_options(struct sk_option **opts)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (opts == NULL)
|
||||
return;
|
||||
for (i = 0; opts[i] != NULL; i++) {
|
||||
free(opts[i]->name);
|
||||
free(opts[i]->value);
|
||||
free(opts[i]);
|
||||
}
|
||||
free(opts);
|
||||
}
|
||||
|
||||
static int
|
||||
sshsk_add_option(struct sk_option ***optsp, size_t *noptsp,
|
||||
const char *name, const char *value, uint8_t required)
|
||||
{
|
||||
struct sk_option **opts = *optsp;
|
||||
size_t nopts = *noptsp;
|
||||
|
||||
if ((opts = recallocarray(opts, nopts, nopts + 2, /* extra for NULL */
|
||||
sizeof(*opts))) == NULL) {
|
||||
error("%s: array alloc failed", __func__);
|
||||
return SSH_ERR_ALLOC_FAIL;
|
||||
}
|
||||
*optsp = opts;
|
||||
*noptsp = nopts + 1;
|
||||
if ((opts[nopts] = calloc(1, sizeof(**opts))) == NULL) {
|
||||
error("%s: alloc failed", __func__);
|
||||
return SSH_ERR_ALLOC_FAIL;
|
||||
}
|
||||
if ((opts[nopts]->name = strdup(name)) == NULL ||
|
||||
(opts[nopts]->value = strdup(value)) == NULL) {
|
||||
error("%s: alloc failed", __func__);
|
||||
return SSH_ERR_ALLOC_FAIL;
|
||||
}
|
||||
opts[nopts]->required = required;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
make_options(const char *device, const char *user_id,
|
||||
struct sk_option ***optsp)
|
||||
{
|
||||
struct sk_option **opts = NULL;
|
||||
size_t nopts = 0;
|
||||
int r, ret = SSH_ERR_INTERNAL_ERROR;
|
||||
|
||||
if (device != NULL &&
|
||||
(r = sshsk_add_option(&opts, &nopts, "device", device, 0)) != 0) {
|
||||
ret = r;
|
||||
goto out;
|
||||
}
|
||||
if (user_id != NULL &&
|
||||
(r = sshsk_add_option(&opts, &nopts, "user", user_id, 0)) != 0) {
|
||||
ret = r;
|
||||
goto out;
|
||||
}
|
||||
/* success */
|
||||
*optsp = opts;
|
||||
opts = NULL;
|
||||
nopts = 0;
|
||||
ret = 0;
|
||||
out:
|
||||
sshsk_free_options(opts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
sshsk_enroll(int type, const char *provider_path, const char *application,
|
||||
uint8_t flags, const char *pin, struct sshbuf *challenge_buf,
|
||||
sshsk_enroll(int type, const char *provider_path, const char *device,
|
||||
const char *application, const char *userid, uint8_t flags,
|
||||
const char *pin, struct sshbuf *challenge_buf,
|
||||
struct sshkey **keyp, struct sshbuf *attest)
|
||||
{
|
||||
struct sshsk_provider *skp = NULL;
|
||||
|
@ -350,17 +424,23 @@ sshsk_enroll(int type, const char *provider_path, const char *application,
|
|||
const u_char *challenge;
|
||||
size_t challenge_len;
|
||||
struct sk_enroll_response *resp = NULL;
|
||||
struct sk_option **opts = NULL;
|
||||
int r = SSH_ERR_INTERNAL_ERROR;
|
||||
int alg;
|
||||
|
||||
debug("%s: provider \"%s\", application \"%s\", flags 0x%02x, "
|
||||
"challenge len %zu%s", __func__, provider_path, application,
|
||||
flags, challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf),
|
||||
debug("%s: provider \"%s\", device \"%s\", application \"%s\", "
|
||||
"userid \"%s\", flags 0x%02x, challenge len %zu%s", __func__,
|
||||
provider_path, device, application, userid, flags,
|
||||
challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf),
|
||||
(pin != NULL && *pin != '\0') ? " with-pin" : "");
|
||||
|
||||
*keyp = NULL;
|
||||
if (attest)
|
||||
sshbuf_reset(attest);
|
||||
|
||||
if ((r = make_options(device, userid, &opts)) != 0)
|
||||
goto out;
|
||||
|
||||
switch (type) {
|
||||
#ifdef WITH_OPENSSL
|
||||
case KEY_ECDSA_SK:
|
||||
|
@ -407,7 +487,7 @@ sshsk_enroll(int type, const char *provider_path, const char *application,
|
|||
/* XXX validate flags? */
|
||||
/* enroll key */
|
||||
if ((r = skp->sk_enroll(alg, challenge, challenge_len, application,
|
||||
flags, pin, &resp)) != 0) {
|
||||
flags, pin, opts, &resp)) != 0) {
|
||||
error("Security key provider \"%s\" returned failure %d",
|
||||
provider_path, r);
|
||||
r = skerr_to_ssherr(r);
|
||||
|
@ -437,6 +517,7 @@ sshsk_enroll(int type, const char *provider_path, const char *application,
|
|||
key = NULL; /* transferred */
|
||||
r = 0;
|
||||
out:
|
||||
sshsk_free_options(opts);
|
||||
sshsk_free(skp);
|
||||
sshkey_free(key);
|
||||
sshsk_free_enroll_response(resp);
|
||||
|
@ -528,6 +609,7 @@ sshsk_sign(const char *provider_path, struct sshkey *key,
|
|||
struct sk_sign_response *resp = NULL;
|
||||
struct sshbuf *inner_sig = NULL, *sig = NULL;
|
||||
uint8_t message[32];
|
||||
struct sk_option **opts = NULL;
|
||||
|
||||
debug("%s: provider \"%s\", key %s, flags 0x%02x%s", __func__,
|
||||
provider_path, sshkey_type(key), key->sk_flags,
|
||||
|
@ -571,7 +653,7 @@ sshsk_sign(const char *provider_path, struct sshkey *key,
|
|||
if ((r = skp->sk_sign(alg, message, sizeof(message),
|
||||
key->sk_application,
|
||||
sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle),
|
||||
key->sk_flags, pin, &resp)) != 0) {
|
||||
key->sk_flags, pin, opts, &resp)) != 0) {
|
||||
debug("%s: sk_sign failed with code %d", __func__, r);
|
||||
r = skerr_to_ssherr(r);
|
||||
goto out;
|
||||
|
@ -617,6 +699,7 @@ sshsk_sign(const char *provider_path, struct sshkey *key,
|
|||
/* success */
|
||||
r = 0;
|
||||
out:
|
||||
sshsk_free_options(opts);
|
||||
explicit_bzero(message, sizeof(message));
|
||||
sshsk_free(skp);
|
||||
sshsk_free_sign_response(resp);
|
||||
|
@ -645,8 +728,8 @@ sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks)
|
|||
}
|
||||
|
||||
int
|
||||
sshsk_load_resident(const char *provider_path, const char *pin,
|
||||
struct sshkey ***keysp, size_t *nkeysp)
|
||||
sshsk_load_resident(const char *provider_path, const char *device,
|
||||
const char *pin, struct sshkey ***keysp, size_t *nkeysp)
|
||||
{
|
||||
struct sshsk_provider *skp = NULL;
|
||||
int r = SSH_ERR_INTERNAL_ERROR;
|
||||
|
@ -654,6 +737,7 @@ sshsk_load_resident(const char *provider_path, const char *pin,
|
|||
size_t i, nrks = 0, nkeys = 0;
|
||||
struct sshkey *key = NULL, **keys = NULL, **tmp;
|
||||
uint8_t flags;
|
||||
struct sk_option **opts = NULL;
|
||||
|
||||
debug("%s: provider \"%s\"%s", __func__, provider_path,
|
||||
(pin != NULL && *pin != '\0') ? ", have-pin": "");
|
||||
|
@ -663,11 +747,13 @@ sshsk_load_resident(const char *provider_path, const char *pin,
|
|||
*keysp = NULL;
|
||||
*nkeysp = 0;
|
||||
|
||||
if ((r = make_options(device, NULL, &opts)) != 0)
|
||||
goto out;
|
||||
if ((skp = sshsk_open(provider_path)) == NULL) {
|
||||
r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
|
||||
goto out;
|
||||
}
|
||||
if ((r = skp->sk_load_resident_keys(pin, &rks, &nrks)) != 0) {
|
||||
if ((r = skp->sk_load_resident_keys(pin, opts, &rks, &nrks)) != 0) {
|
||||
error("Security key provider \"%s\" returned failure %d",
|
||||
provider_path, r);
|
||||
r = skerr_to_ssherr(r);
|
||||
|
@ -710,6 +796,7 @@ sshsk_load_resident(const char *provider_path, const char *pin,
|
|||
nkeys = 0;
|
||||
r = 0;
|
||||
out:
|
||||
sshsk_free_options(opts);
|
||||
sshsk_free(skp);
|
||||
sshsk_free_sk_resident_keys(rks, nrks);
|
||||
sshkey_free(key);
|
||||
|
|
14
ssh-sk.h
14
ssh-sk.h
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: ssh-sk.h,v 1.8 2019/12/30 09:23:28 djm Exp $ */
|
||||
/* $OpenBSD: ssh-sk.h,v 1.9 2020/01/06 02:00:47 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2019 Google LLC
|
||||
*
|
||||
|
@ -20,9 +20,10 @@
|
|||
|
||||
struct sshbuf;
|
||||
struct sshkey;
|
||||
struct sk_option;
|
||||
|
||||
/* Version of protocol expected from ssh-sk-helper */
|
||||
#define SSH_SK_HELPER_VERSION 3
|
||||
#define SSH_SK_HELPER_VERSION 4
|
||||
|
||||
/* ssh-sk-helper messages */
|
||||
#define SSH_SK_HELPER_ERROR 0 /* Only valid H->C */
|
||||
|
@ -40,8 +41,9 @@ struct sshkey;
|
|||
* If successful and the attest_data buffer is not NULL then attestation
|
||||
* information is placed there.
|
||||
*/
|
||||
int sshsk_enroll(int type, const char *provider_path, const char *application,
|
||||
uint8_t flags, const char *pin, struct sshbuf *challenge_buf,
|
||||
int sshsk_enroll(int type, const char *provider_path, const char *device,
|
||||
const char *application, const char *userid, uint8_t flags,
|
||||
const char *pin, struct sshbuf *challenge_buf,
|
||||
struct sshkey **keyp, struct sshbuf *attest);
|
||||
|
||||
/*
|
||||
|
@ -60,8 +62,8 @@ int sshsk_sign(const char *provider_path, struct sshkey *key,
|
|||
*
|
||||
* Returns 0 on success or a ssherr.h error code on failure.
|
||||
*/
|
||||
int sshsk_load_resident(const char *provider_path, const char *pin,
|
||||
struct sshkey ***keysp, size_t *nkeysp);
|
||||
int sshsk_load_resident(const char *provider_path, const char *device,
|
||||
const char *pin, struct sshkey ***keysp, size_t *nkeysp);
|
||||
|
||||
#endif /* _SSH_SK_H */
|
||||
|
||||
|
|
Loading…
Reference in New Issue