Module Name:    src
Committed By:   christos
Date:           Fri Sep 24 13:08:16 UTC 2021

Modified Files:
        src/external/bsd/pam-u2f/dist: util.c

Log Message:
Merge conflicts, avoid ssp issues


To generate a diff of this commit:
cvs rdiff -u -r1.3 -r1.4 src/external/bsd/pam-u2f/dist/util.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/external/bsd/pam-u2f/dist/util.c
diff -u src/external/bsd/pam-u2f/dist/util.c:1.3 src/external/bsd/pam-u2f/dist/util.c:1.4
--- src/external/bsd/pam-u2f/dist/util.c:1.3	Mon Mar  2 19:21:20 2020
+++ src/external/bsd/pam-u2f/dist/util.c	Fri Sep 24 09:08:16 2021
@@ -5,11 +5,13 @@
 #include <fido.h>
 #include <fido/es256.h>
 #include <fido/rs256.h>
+#include <fido/eddsa.h>
 
 #include <openssl/ec.h>
 #include <openssl/obj_mac.h>
 
-#include <stdbool.h>
+#include <inttypes.h>
+#include <limits.h>
 #include <stdlib.h>
 #include <fcntl.h>
 #include <sys/stat.h>
@@ -19,10 +21,40 @@
 #include <errno.h>
 #include <unistd.h>
 #include <string.h>
+#include <arpa/inet.h>
 
 #include "b64.h"
 #include "util.h"
 
+#define SSH_HEADER "-----BEGIN OPENSSH PRIVATE KEY-----\n"
+#define SSH_HEADER_LEN (sizeof(SSH_HEADER) - 1)
+#define SSH_TRAILER "-----END OPENSSH PRIVATE KEY-----\n"
+#define SSH_TRAILER_LEN (sizeof(SSH_TRAILER) - 1)
+#define SSH_AUTH_MAGIC "openssh-key-v1"
+#define SSH_AUTH_MAGIC_LEN (sizeof(SSH_AUTH_MAGIC)) // AUTH_MAGIC includes \0
+#define SSH_ES256 "[email protected]"
+#define SSH_ES256_LEN (sizeof(SSH_ES256) - 1)
+#define SSH_ES256_POINT_LEN 65
+#define SSH_P256_NAME "nistp256"
+#define SSH_P256_NAME_LEN (sizeof(SSH_P256_NAME) - 1)
+#define SSH_EDDSA "[email protected]"
+#define SSH_EDDSA_LEN (sizeof(SSH_EDDSA) - 1)
+#define SSH_EDDSA_POINT_LEN 32
+#define SSH_SK_USER_PRESENCE_REQD 0x01
+#define SSH_SK_USER_VERIFICATION_REQD 0x04
+#define SSH_SK_RESIDENT_KEY 0x20
+
+struct opts {
+  fido_opt_t up;
+  fido_opt_t uv;
+  fido_opt_t pin;
+};
+
+struct pk {
+  void *ptr;
+  int type;
+};
+
 static int hex_decode(const char *ascii_hex, unsigned char **blob,
                       size_t *blob_len) {
   *blob = NULL;
@@ -94,13 +126,15 @@ static char *normal_b64(const char *webs
   return (b64);
 }
 
-static es256_pk_t *translate_old_format_pubkey(const unsigned char *pk,
-                                               size_t pk_len) {
-  es256_pk_t *es256_pk = NULL;
+static int translate_old_format_pubkey(es256_pk_t *es256_pk,
+                                       const unsigned char *pk, size_t pk_len) {
   EC_KEY *ec = NULL;
   EC_POINT *q = NULL;
   const EC_GROUP *g = NULL;
-  int ok = 0;
+  int r = FIDO_ERR_INTERNAL;
+
+  if (es256_pk == NULL)
+    goto fail;
 
   if ((ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
       (g = EC_KEY_get0_group(ec)) == NULL)
@@ -111,226 +145,175 @@ static es256_pk_t *translate_old_format_
       !EC_KEY_set_public_key(ec, q))
     goto fail;
 
-  es256_pk = es256_pk_new();
-  if (es256_pk == NULL || es256_pk_from_EC_KEY(es256_pk, ec) < 0)
-    goto fail;
+  r = es256_pk_from_EC_KEY(es256_pk, ec);
 
-  ok = 1;
 fail:
   if (ec != NULL)
     EC_KEY_free(ec);
   if (q != NULL)
     EC_POINT_free(q);
-  if (!ok)
-    es256_pk_free(&es256_pk);
 
-  return (es256_pk);
+  return r;
 }
 
-int get_devices_from_authfile(const char *authfile, const char *username,
-                              unsigned max_devs, int verbose, FILE *debug_file,
-                              device_t *devices, unsigned *n_devs) {
-
-  char *buf = NULL;
-  char *s_user, *s_token;
-  int retval = 0;
-  int fd = -1;
-  struct stat st;
-  struct passwd *pw = NULL, pw_s;
-  char buffer[BUFSIZE];
-  int gpu_ret;
-  FILE *opwfile = NULL;
-  unsigned i;
-
-  /* Ensure we never return uninitialized count. */
-  *n_devs = 0;
-
-  fd = open(authfile, O_RDONLY | O_CLOEXEC | O_NOCTTY);
-  if (fd < 0) {
-    if (verbose)
-      D(debug_file, "Cannot open file: %s (%s)", authfile, strerror(errno));
-    goto err;
-  }
-
-  if (fstat(fd, &st) < 0) {
-    if (verbose)
-      D(debug_file, "Cannot stat file: %s (%s)", authfile, strerror(errno));
-    goto err;
-  }
-
-  if (!S_ISREG(st.st_mode)) {
-    if (verbose)
-      D(debug_file, "%s is not a regular file", authfile);
-    goto err;
-  }
-
-  if (st.st_size == 0) {
-    if (verbose)
-      D(debug_file, "File %s is empty", authfile);
-    goto err;
-  }
-
-  gpu_ret = getpwuid_r(st.st_uid, &pw_s, buffer, sizeof(buffer), &pw);
-  if (gpu_ret != 0 || pw == NULL) {
-    D(debug_file, "Unable to retrieve credentials for uid %u, (%s)", st.st_uid,
-      strerror(errno));
-    goto err;
-  }
+static int is_resident(const char *kh) { return strcmp(kh, "*") == 0; }
 
-  if (strcmp(pw->pw_name, username) != 0 && strcmp(pw->pw_name, "root") != 0) {
-    if (strcmp(username, "root") != 0) {
-      D(debug_file,
-        "The owner of the authentication file is neither %s nor root",
-        username);
-    } else {
-      D(debug_file, "The owner of the authentication file is not root");
-    }
-    goto err;
-  }
+static void reset_device(device_t *device) {
+  free(device->keyHandle);
+  free(device->publicKey);
+  free(device->coseType);
+  free(device->attributes);
+  memset(device, 0, sizeof(*device));
+}
 
-  opwfile = fdopen(fd, "r");
-  if (opwfile == NULL) {
-    if (verbose)
-      D(debug_file, "fdopen: %s", strerror(errno));
-    goto err;
-  } else {
-    fd = -1; /* fd belongs to opwfile */
-  }
+static int parse_native_format(const cfg_t *cfg, const char *username,
+                               char *buf, FILE *opwfile, device_t *devices,
+                               unsigned *n_devs) {
 
-  buf = malloc(sizeof(char) * (DEVSIZE * max_devs));
-  if (!buf) {
-    if (verbose)
-      D(debug_file, "Unable to allocate memory");
-    goto err;
-  }
+  char *s_user, *s_credential;
+  const char *s_token;
+  unsigned i;
 
-  retval = -2;
-  while (fgets(buf, (int) (DEVSIZE * (max_devs - 1)), opwfile)) {
+  while (fgets(buf, (int) (DEVSIZE * (cfg->max_devs - 1)), opwfile)) {
     char *saveptr = NULL;
     size_t len = strlen(buf);
     if (len > 0 && buf[len - 1] == '\n')
       buf[len - 1] = '\0';
 
-    if (verbose)
-      D(debug_file, "Authorization line: %s", buf);
+    if (cfg->debug)
+      D(cfg->debug_file, "Authorization line: %s", buf);
 
     s_user = strtok_r(buf, ":", &saveptr);
     if (s_user && strcmp(username, s_user) == 0) {
-      if (verbose)
-        D(debug_file, "Matched user: %s", s_user);
-
-      retval = -1; // We found at least one line for the user
+      if (cfg->debug)
+        D(cfg->debug_file, "Matched user: %s", s_user);
 
       // only keep last line for this user
       for (i = 0; i < *n_devs; i++) {
-        free(devices[i].keyHandle);
-        free(devices[i].publicKey);
-        free(devices[i].coseType);
-        free(devices[i].attributes);
-        devices[i].keyHandle = NULL;
-        devices[i].publicKey = NULL;
-        devices[i].coseType = NULL;
-        devices[i].attributes = NULL;
-        devices[i].old_format = 0;
+        reset_device(&devices[i]);
       }
       *n_devs = 0;
 
       i = 0;
-      while ((s_token = strtok_r(NULL, ",", &saveptr)) != NULL) {
-        if ((*n_devs)++ > max_devs - 1) {
-          *n_devs = max_devs;
-          if (verbose)
-            D(debug_file,
+      while ((s_credential = strtok_r(NULL, ":", &saveptr)) != NULL) {
+        // s_credential is the whole line now
+        char *credsaveptr = NULL;
+
+        if ((*n_devs)++ > cfg->max_devs - 1) {
+          *n_devs = cfg->max_devs;
+          if (cfg->debug) {
+            D(cfg->debug_file,
               "Found more than %d devices, ignoring the remaining ones",
-              max_devs);
+              cfg->max_devs);
+          }
           break;
         }
 
-        devices[i].keyHandle = NULL;
-        devices[i].publicKey = NULL;
-        devices[i].coseType = NULL;
-        devices[i].attributes = NULL;
-        devices[i].old_format = 0;
+        reset_device(&devices[i]);
+
+        s_token = strtok_r(s_credential, ",", &credsaveptr);
+
+        if (!s_token) {
+          if (cfg->debug) {
+            D(cfg->debug_file,
+              "Unable to retrieve keyHandle for device %d", i + 1);
+          }
+          return -1;
+        }
 
-        if (verbose)
-          D(debug_file, "KeyHandle for device number %d: %s", i + 1, s_token);
+        if (cfg->debug) {
+          D(cfg->debug_file, "KeyHandle for device number %d: %s", i + 1,
+            s_token);
+        }
 
         devices[i].keyHandle = strdup(s_token);
 
         if (!devices[i].keyHandle) {
-          if (verbose)
-            D(debug_file, "Unable to allocate memory for keyHandle number %d",
-              i);
-          goto err;
+          if (cfg->debug) {
+            D(cfg->debug_file,
+              "Unable to allocate memory for keyHandle number %d", i);
+          }
+          return -1;
         }
 
-        if (!strcmp(devices[i].keyHandle, "*") && verbose)
-          D(debug_file, "Credential is resident");
+        if (is_resident(devices[i].keyHandle) && cfg->debug) {
+          D(cfg->debug_file, "Credential is resident");
+        }
 
-        s_token = strtok_r(NULL, ",", &saveptr);
+        s_token = strtok_r(NULL, ",", &credsaveptr);
 
         if (!s_token) {
-          if (verbose)
-            D(debug_file, "Unable to retrieve publicKey number %d", i + 1);
-          goto err;
+          if (cfg->debug) {
+            D(cfg->debug_file, "Unable to retrieve publicKey number %d", i + 1);
+          }
+          return -1;
         }
 
-        if (verbose)
-          D(debug_file, "publicKey for device number %d: %s", i + 1, s_token);
+        if (cfg->debug) {
+          D(cfg->debug_file, "publicKey for device number %d: %s", i + 1,
+            s_token);
+        }
 
         devices[i].publicKey = strdup(s_token);
 
         if (!devices[i].publicKey) {
-          if (verbose)
-            D(debug_file, "Unable to allocate memory for publicKey number %d",
-              i);
-          goto err;
+          if (cfg->debug) {
+            D(cfg->debug_file,
+              "Unable to allocate memory for publicKey number %d", i);
+          }
+          return -1;
         }
 
-        s_token = strtok_r(NULL, ",", &saveptr);
-
-        devices[i].old_format = 0;
+        s_token = strtok_r(NULL, ",", &credsaveptr);
 
         if (!s_token) {
-          if (verbose) {
-            D(debug_file, "Unable to retrieve COSE type %d", i + 1);
-            D(debug_file, "Assuming ES256 (backwards compatibility)");
+          if (cfg->debug) {
+            D(cfg->debug_file, "Unable to retrieve COSE type %d", i + 1);
+            D(cfg->debug_file, "Assuming ES256 (backwards compatibility)");
           }
           devices[i].old_format = 1;
           devices[i].coseType = strdup("es256");
         } else {
-          if (verbose)
-            D(debug_file, "COSE type for device number %d: %s", i + 1, s_token);
+          if (cfg->debug) {
+            D(cfg->debug_file, "COSE type for device number %d: %s", i + 1,
+              s_token);
+          }
           devices[i].coseType = strdup(s_token);
         }
 
         if (!devices[i].coseType) {
-          if (verbose)
-            D(debug_file, "Unable to allocate memory for COSE type number %d",
-              i);
-          goto err;
+          if (cfg->debug) {
+            D(cfg->debug_file,
+              "Unable to allocate memory for COSE type number %d", i);
+          }
+          return -1;
         }
 
-        s_token = strtok_r(NULL, ":", &saveptr);
+        s_token = strtok_r(NULL, ",", &credsaveptr);
 
-        if (!s_token) {
-          if (verbose) {
-            D(debug_file, "Unable to retrieve attributes %d", i + 1);
-            D(debug_file, "Assuming 'p' (backwards compatibility)");
+        if (devices[i].old_format == 1) {
+          if (cfg->debug) {
+            D(cfg->debug_file, "Old format for device %d, no attributes",
+              i + 1);
+            D(cfg->debug_file, "Assuming 'presence' (backwards compatibility)");
           }
-          devices[i].attributes = strdup("p");
-        } else {
-          if (verbose)
-            D(debug_file, "Attributes for device number %d: %s", i + 1,
-              s_token);
-          devices[i].attributes = strdup(s_token);
+          s_token = "+presence";
+        } else if (!s_token) {
+          s_token = "";
+        }
+
+        if (cfg->debug) {
+          D(cfg->debug_file, "Attributes for device number %d: %s", i + 1,
+            s_token);
         }
+        devices[i].attributes = strdup(s_token);
 
         if (!devices[i].attributes) {
-          if (verbose)
-            D(debug_file, "Unable to allocate memory for attributes number %d",
-              i);
-          goto err;
+          if (cfg->debug) {
+            D(cfg->debug_file,
+              "Unable to allocate memory for attributes number %d", i);
+          }
+          return -1;
         }
 
         if (devices[i].old_format) {
@@ -338,10 +321,11 @@ int get_devices_from_authfile(const char
           devices[i].keyHandle = normal_b64(websafe_b64);
           free(websafe_b64);
           if (!devices[i].keyHandle) {
-            if (verbose)
-              D(debug_file, "Unable to allocate memory for keyHandle number %d",
-                i);
-            goto err;
+            if (cfg->debug) {
+              D(cfg->debug_file,
+                "Unable to allocate memory for keyHandle number %d", i);
+            }
+            return -1;
           }
         }
 
@@ -350,161 +334,1000 @@ int get_devices_from_authfile(const char
     }
   }
 
-  if (verbose)
-    D(debug_file, "Found %d device(s) for user %s", *n_devs, username);
+  return 1;
+}
 
-  retval = 1;
-  goto out;
+static int load_ssh_key(const cfg_t *cfg, char *buf, size_t buf_size,
+                        FILE *opwfile, size_t opwfile_size) {
+  char *cp = buf;
+  int ch;
 
-err:
-  for (i = 0; i < *n_devs; i++) {
-    free(devices[i].keyHandle);
-    free(devices[i].publicKey);
-    free(devices[i].coseType);
-    free(devices[i].attributes);
-    devices[i].keyHandle = NULL;
-    devices[i].publicKey = NULL;
-    devices[i].coseType = NULL;
-    devices[i].attributes = NULL;
+  if (opwfile_size > buf_size ||
+      opwfile_size < SSH_HEADER_LEN + SSH_TRAILER_LEN) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Malformed SSH key (length)");
+    }
+    return 0;
   }
 
-  *n_devs = 0;
+  // NOTE(adma): +1 for \0
+  if (fgets(buf, (int)(SSH_HEADER_LEN + 1), opwfile) == NULL ||
+      strlen(buf) != SSH_HEADER_LEN ||
+      strncmp(buf, SSH_HEADER, SSH_HEADER_LEN) != 0) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Malformed SSH key (header)");
+    }
+    return 0;
+  }
 
-out:
-  if (buf) {
-    free(buf);
-    buf = NULL;
+  while (opwfile_size > 0 && buf_size > 1) {
+    ch = fgetc(opwfile);
+    if (ch == EOF) {
+      if (cfg->debug) {
+        D(cfg->debug_file, "Unexpected authfile termination");
+      }
+      return 0;
+    }
+
+    opwfile_size--;
+    buf_size--;
+
+    if (ch != '\n' && ch != '\r') {
+      *cp = (char) ch;
+      if (ch == '-') {
+        // NOTE(adma): no +1 here since we already read one '-'
+        if (buf_size < SSH_TRAILER_LEN ||
+            fgets(cp + 1, (int)SSH_TRAILER_LEN, opwfile) == NULL ||
+            strlen(cp) != SSH_TRAILER_LEN ||
+            strncmp(cp, SSH_TRAILER, SSH_TRAILER_LEN) != 0) {
+          if (cfg->debug) {
+            D(cfg->debug_file, "Malformed SSH key (trailer)");
+          }
+          return 0;
+        }
+
+        *(cp) = '\0';
+        break;
+      } else {
+        cp++;
+      }
+    }
   }
 
-  if (opwfile)
-    fclose(opwfile);
+  if (cfg->debug) { // TODO(adma): too verbose? Delete?
+    D(cfg->debug_file, "Credential is \"%s\"", buf);
+  }
 
-  if (fd != -1)
-    close(fd);
+  return 1;
+}
 
-  return retval;
+static int ssh_get(const unsigned char **buf, size_t *size, unsigned char *dst,
+                   size_t len) {
+  if (*size < len)
+    return 0;
+  if (dst != NULL)
+    memcpy(dst, *buf, len);
+  *buf += len;
+  *size -= len;
+  return 1;
 }
 
-void free_devices(device_t *devices, const unsigned n_devs) {
-  unsigned i;
+static int ssh_get_u8(const unsigned char **buf, size_t *size, uint8_t *val) {
+  return ssh_get(buf, size, val, sizeof(*val));
+}
 
-  if (!devices)
-    return;
+static int ssh_get_u32(const unsigned char **buf, size_t *size, uint32_t *val) {
+  if (!ssh_get(buf, size, (unsigned char *) val, sizeof(*val)))
+    return 0;
+  if (val != NULL)
+    *val = ntohl(*val);
+  return 1;
+}
 
-  for (i = 0; i < n_devs; i++) {
-    free(devices[i].keyHandle);
-    devices[i].keyHandle = NULL;
+static int ssh_get_string_ref(const unsigned char **buf, size_t *size,
+                              const unsigned char **ref, size_t *lenp) {
+  uint32_t len;
+
+  if (!ssh_get_u32(buf, size, &len))
+    return 0;
+  if (!ssh_get(buf, size, NULL, len))
+    return 0;
+  if (ref != NULL)
+    *ref = *buf - len;
+  if (lenp != NULL)
+    *lenp = len;
+  return 1;
+}
+
+static int ssh_get_cstring(const unsigned char **buf, size_t *size, char **str,
+                           size_t *lenp) {
+  const unsigned char *ref;
+  size_t len;
+
+  if (!ssh_get_string_ref(buf, size, &ref, &len))
+    return 0;
+  if (str != NULL) {
+    if (len > SIZE_MAX - 1 || (*str = calloc(1, len + 1)) == NULL)
+      return 0;
+    memcpy(*str, ref, len);
+  }
+  if (lenp != NULL)
+    *lenp = len;
+  return 1;
+}
 
-    free(devices[i].publicKey);
-    devices[i].publicKey = NULL;
+static int ssh_log_cstring(const cfg_t *cfg, const unsigned char **buf,
+                           size_t *size, const char *name) {
+  char *str = NULL;
+  size_t len;
 
-    free(devices[i].coseType);
-    devices[i].coseType = NULL;
+  (void) name; // silence compiler warnings if PAM_DEBUG disabled
 
-    free(devices[i].attributes);
-    devices[i].attributes = NULL;
+  if (!ssh_get_cstring(buf, size, &str, &len)) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Malformed SSH key (%s)", name);
+    return 0;
   }
+  if (cfg->debug)
+    D(cfg->debug_file, "%s (%zu) \"%s\"", name, len, str);
 
-  free(devices);
-  devices = NULL;
+  free(str);
+  return 1;
 }
 
-static int get_authenticators(const cfg_t *cfg, const fido_dev_info_t *devlist,
-                              size_t devlist_len, fido_assert_t *assert,
-                              const void *kh, fido_dev_t **authlist) {
-  const fido_dev_info_t *di = NULL;
-  fido_dev_t *dev = NULL;
+static int ssh_get_attrs(const cfg_t *cfg, const unsigned char **buf,
+                         size_t *size, char **attrs) {
+  char tmp[32] = {0};
+  uint8_t flags;
   int r;
-  size_t i;
-  size_t j;
 
-  if (cfg->debug)
-    D(cfg->debug_file, "Working with %zu authenticator(s)", devlist_len);
+  // flags
+  if (!ssh_get_u8(buf, size, &flags)) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Malformed SSH key (flags)");
+    }
+    return 0;
+  }
+  if (cfg->debug) {
+    D(cfg->debug_file, "flags: %02x", flags);
+  }
 
-  for (i = 0, j = 0; i < devlist_len; i++) {
+  r = snprintf(tmp, sizeof(tmp), "%s%s",
+               flags & SSH_SK_USER_PRESENCE_REQD ? "+presence" : "",
+               flags & SSH_SK_USER_VERIFICATION_REQD ? "+verification" : "");
+  if (r < 0 || (size_t) r >= sizeof(tmp)) {
     if (cfg->debug)
-      D(cfg->debug_file, "Checking whether key exists in authenticator %zu", i);
+      D(cfg->debug_file, "Unable to prepare flags");
+    return 0;
+  }
 
-    di = fido_dev_info_ptr(devlist, i);
-    if (!di) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Unable to get device pointer");
-      continue;
+  if ((*attrs = strdup(tmp)) == NULL) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Unable to allocate attributes");
     }
+    return 0;
+  }
 
-    if (cfg->debug)
-      D(cfg->debug_file, "Authenticator path: %s", fido_dev_info_path(di));
+  return 1;
+}
 
-    dev = fido_dev_new();
-    if (!dev) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Unable to allocate device type");
-      continue;
+static int ssh_get_pubkey(const cfg_t *cfg, const unsigned char **buf,
+                          size_t *size, char **type_p, char **pubkey_p) {
+  char *ssh_type = NULL;
+  char *ssh_curve = NULL;
+  const unsigned char *blob;
+  size_t len;
+  int type;
+  size_t point_len;
+  int ok = 0;
+
+  *type_p = NULL;
+  *pubkey_p = NULL;
+
+  // key type
+  if (!ssh_get_cstring(buf, size, &ssh_type, &len)) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Malformed SSH key (keytype)");
+    }
+    goto err;
+  }
+
+  if (len == SSH_ES256_LEN && memcmp(ssh_type, SSH_ES256, SSH_ES256_LEN) == 0) {
+    type = COSE_ES256;
+    point_len = SSH_ES256_POINT_LEN;
+  } else if (len == SSH_EDDSA_LEN &&
+             memcmp(ssh_type, SSH_EDDSA, SSH_EDDSA_LEN) == 0) {
+    type = COSE_EDDSA;
+    point_len = SSH_EDDSA_POINT_LEN;
+  } else {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Unknown key type %s", ssh_type);
     }
+    goto err;
+  }
 
-    r = fido_dev_open(dev, fido_dev_info_path(di));
+  if (cfg->debug) {
+    D(cfg->debug_file, "keytype (%zu) \"%s\"", len, ssh_type);
+  }
+
+  if (type == COSE_ES256) {
+    // curve name
+    if (!ssh_get_cstring(buf, size, &ssh_curve, &len)) {
+      if (cfg->debug) {
+        D(cfg->debug_file, "Malformed SSH key (curvename)");
+      }
+      goto err;
+    }
+
+    if (len == SSH_P256_NAME_LEN &&
+        memcmp(ssh_curve, SSH_P256_NAME, SSH_P256_NAME_LEN) == 0) {
+      if (cfg->debug) {
+        D(cfg->debug_file, "curvename (%zu) \"%s\"", len, ssh_curve);
+      }
+    } else {
+      if (cfg->debug) {
+        D(cfg->debug_file, "Unknown curve %s", ssh_curve);
+      }
+      goto err;
+    }
+  }
+
+  // point
+  if (!ssh_get_string_ref(buf, size, &blob, &len)) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Malformed SSH key (point)");
+    }
+    goto err;
+  }
+
+  if (len != point_len) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Invalid point length, should be %zu, found %zu",
+        point_len, len);
+    }
+    goto err;
+  }
+
+  if (type == COSE_ES256) {
+    // Skip the initial '04'
+    if (len < 1) {
+      if (cfg->debug) {
+        D(cfg->debug_file, "Failed to skip initial '04'");
+      }
+      goto err;
+    }
+    blob++;
+    len--;
+  }
+
+  if (!b64_encode(blob, len, pubkey_p)) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Unable to allocate public key");
+    }
+    goto err;
+  }
+
+  if ((*type_p = strdup(cose_string(type))) == NULL) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Unable to allocate COSE type");
+    }
+    goto err;
+  }
+
+  ok = 1;
+err:
+  if (!ok) {
+    free(*type_p);
+    free(*pubkey_p);
+    *type_p = NULL;
+    *pubkey_p = NULL;
+  }
+  free(ssh_type);
+  free(ssh_curve);
+
+  return ok;
+}
+
+static int parse_ssh_format(const cfg_t *cfg, char *buf, size_t buf_size,
+                            FILE *opwfile, size_t opwfile_size,
+                            device_t *devices, unsigned *n_devs) {
+
+  const unsigned char *decoded;
+  unsigned char *decoded_initial = NULL;
+  size_t decoded_len;
+  const unsigned char *blob;
+  uint32_t check1, check2, tmp;
+  size_t len;
+
+  // The logic below is inspired by
+  // how ssh parses its own keys. See sshkey.c
+  reset_device(&devices[0]);
+
+  if (!load_ssh_key(cfg, buf, buf_size, opwfile, opwfile_size) ||
+      !b64_decode(buf, (void **) &decoded_initial, &decoded_len)) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Unable to decode credential");
+    }
+    goto out;
+  }
+
+  decoded = decoded_initial;
+
+  // magic
+  if (decoded_len < SSH_AUTH_MAGIC_LEN ||
+      memcmp(decoded, SSH_AUTH_MAGIC, SSH_AUTH_MAGIC_LEN) != 0) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Malformed SSH key (magic)");
+    }
+    goto out;
+  }
+
+  decoded += SSH_AUTH_MAGIC_LEN;
+  decoded_len -= SSH_AUTH_MAGIC_LEN;
+
+  if (!ssh_log_cstring(cfg, &decoded, &decoded_len, "ciphername") ||
+      !ssh_log_cstring(cfg, &decoded, &decoded_len, "kdfname") ||
+      !ssh_log_cstring(cfg, &decoded, &decoded_len, "kdfoptions"))
+    goto out;
+
+  if (!ssh_get_u32(&decoded, &decoded_len, &tmp)) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Malformed SSH key (nkeys)");
+    }
+    goto out;
+  }
+  if (cfg->debug) {
+    D(cfg->debug_file, "nkeys: %" PRIu32, tmp);
+  }
+  if (tmp != 1) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Multiple keys not supported");
+    }
+    goto out;
+  }
+
+  // public_key (skip)
+  if (!ssh_get_string_ref(&decoded, &decoded_len, NULL, NULL)) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Malformed SSH key (pubkey)");
+    }
+    goto out;
+  }
+
+  // private key (consume length)
+  if (!ssh_get_u32(&decoded, &decoded_len, &tmp) || decoded_len < tmp) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Malformed SSH key (pvtkey length)");
+    }
+    goto out;
+  }
+
+  // check1, check2
+  if (!ssh_get_u32(&decoded, &decoded_len, &check1) ||
+      !ssh_get_u32(&decoded, &decoded_len, &check2)) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Malformed SSH key (check1, check2)");
+    }
+    goto out;
+  }
+  if (cfg->debug) {
+    D(cfg->debug_file, "check1: %" PRIu32, check1);
+    D(cfg->debug_file, "check2: %" PRIu32, check2);
+  }
+  if (check1 != check2) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Mismatched check values");
+      goto out;
+    }
+  }
+
+  if (!ssh_get_pubkey(cfg, &decoded, &decoded_len, &devices[0].coseType,
+                      &devices[0].publicKey) ||
+      !ssh_log_cstring(cfg, &decoded, &decoded_len, "application") ||
+      !ssh_get_attrs(cfg, &decoded, &decoded_len, &devices[0].attributes))
+    goto out;
+
+  // keyhandle
+  if (!ssh_get_string_ref(&decoded, &decoded_len, &blob, &len) ||
+      !b64_encode(blob, len, &devices[0].keyHandle)) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Malformed SSH key (keyhandle)");
+    }
+    goto out;
+  }
+
+  if (cfg->debug) {
+    D(cfg->debug_file, "KeyHandle for device number 1: %s",
+      devices[0].keyHandle);
+    D(cfg->debug_file, "publicKey for device number 1: %s",
+      devices[0].publicKey);
+    D(cfg->debug_file, "COSE type for device number 1: %s",
+      devices[0].coseType);
+    D(cfg->debug_file, "Attributes for device number 1: %s",
+      devices[0].attributes);
+  }
+
+  // reserved (skip)
+  if (!ssh_get_string_ref(&decoded, &decoded_len, NULL, NULL)) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Malformed SSH key (reserved)");
+    }
+    goto out;
+  }
+
+  // comment
+  if (!ssh_log_cstring(cfg, &decoded, &decoded_len, "comment"))
+    goto out;
+
+  // padding
+  if (decoded_len >= 255) {
+    if (cfg->debug) {
+      D(cfg->debug_file, "Malformed SSH key (padding length)");
+    }
+    goto out;
+  }
+
+  for (int i = 1; (unsigned) i <= decoded_len; i++) {
+    if (decoded[i - 1] != i) {
+      if (cfg->debug) {
+        D(cfg->debug_file, "Malformed SSH key (padding)");
+      }
+      goto out;
+    }
+  }
+
+  free(decoded_initial);
+  decoded_initial = NULL;
+
+  *n_devs = 1;
+
+  return 1;
+
+out:
+  reset_device(&devices[0]);
+
+  if (decoded_initial) {
+    free(decoded_initial);
+    decoded_initial = NULL;
+  }
+
+  return -1;
+}
+
+int get_devices_from_authfile(const cfg_t *cfg, const char *username,
+                              device_t *devices, unsigned *n_devs) {
+
+  char *buf = NULL;
+  int retval = 0;
+  int fd = -1;
+  struct stat st;
+  struct passwd *pw = NULL, pw_s;
+  char buffer[BUFSIZE];
+  int gpu_ret;
+  FILE *opwfile = NULL;
+  size_t opwfile_size;
+  unsigned i;
+
+  /* Ensure we never return uninitialized count. */
+  *n_devs = 0;
+
+  fd = open(cfg->auth_file, O_RDONLY | O_CLOEXEC | O_NOCTTY);
+  if (fd < 0) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Cannot open file: %s (%s)", cfg->auth_file,
+        strerror(errno));
+    goto err;
+  }
+
+  if (fstat(fd, &st) < 0) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Cannot stat file: %s (%s)", cfg->auth_file,
+        strerror(errno));
+    goto err;
+  }
+
+  if (!S_ISREG(st.st_mode)) {
+    if (cfg->debug)
+      D(cfg->debug_file, "%s is not a regular file", cfg->auth_file);
+    goto err;
+  }
+
+  if (st.st_size == 0) {
+    if (cfg->debug)
+      D(cfg->debug_file, "File %s is empty", cfg->auth_file);
+    goto err;
+  }
+  opwfile_size = (size_t)st.st_size;
+
+  gpu_ret = getpwuid_r(st.st_uid, &pw_s, buffer, sizeof(buffer), &pw);
+  if (gpu_ret != 0 || pw == NULL) {
+    D(cfg->debug_file, "Unable to retrieve credentials for uid %u, (%s)",
+      st.st_uid, strerror(errno));
+    goto err;
+  }
+
+  if (strcmp(pw->pw_name, username) != 0 && strcmp(pw->pw_name, "root") != 0) {
+    if (strcmp(username, "root") != 0) {
+      D(cfg->debug_file,
+        "The owner of the authentication file is neither %s nor root",
+        username);
+    } else {
+      D(cfg->debug_file, "The owner of the authentication file is not root");
+    }
+    goto err;
+  }
+
+  opwfile = fdopen(fd, "r");
+  if (opwfile == NULL) {
+    if (cfg->debug)
+      D(cfg->debug_file, "fdopen: %s", strerror(errno));
+    goto err;
+  } else {
+    fd = -1; /* fd belongs to opwfile */
+  }
+
+  buf = calloc(1, (DEVSIZE * cfg->max_devs));
+  if (!buf) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Unable to allocate memory");
+    goto err;
+  }
+
+  if (cfg->sshformat == 0) {
+    retval = parse_native_format(cfg, username, buf, opwfile, devices, n_devs);
+  } else {
+    retval = parse_ssh_format(cfg, buf, DEVSIZE * cfg->max_devs, opwfile,
+                              opwfile_size, devices, n_devs);
+  }
+
+  if (retval != 1) {
+    // NOTE(adma): error message is logged by the previous function
+    goto err;
+  }
+
+  if (cfg->debug)
+    D(cfg->debug_file, "Found %d device(s) for user %s", *n_devs, username);
+
+  retval = 1;
+  goto out;
+
+err:
+  for (i = 0; i < *n_devs; i++) {
+    reset_device(&devices[i]);
+  }
+
+  *n_devs = 0;
+
+out:
+  if (buf) {
+    free(buf);
+    buf = NULL;
+  }
+
+  if (opwfile)
+    fclose(opwfile);
+
+  if (fd != -1)
+    close(fd);
+
+  return retval;
+}
+
+void free_devices(device_t *devices, const unsigned n_devs) {
+  unsigned i;
+
+  if (!devices)
+    return;
+
+  for (i = 0; i < n_devs; i++) {
+    reset_device(&devices[i]);
+  }
+
+  free(devices);
+  devices = NULL;
+}
+
+static int get_authenticators(const cfg_t *cfg, const fido_dev_info_t *devlist,
+                              size_t devlist_len, fido_assert_t *assert,
+                              const int rk, fido_dev_t **authlist) {
+  const fido_dev_info_t *di = NULL;
+  fido_dev_t *dev = NULL;
+  int r;
+  size_t i;
+  size_t j;
+
+  if (cfg->debug)
+    D(cfg->debug_file, "Working with %zu authenticator(s)", devlist_len);
+
+  for (i = 0, j = 0; i < devlist_len; i++) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Checking whether key exists in authenticator %zu", i);
+
+    di = fido_dev_info_ptr(devlist, i);
+    if (!di) {
+      if (cfg->debug)
+        D(cfg->debug_file, "Unable to get device pointer");
+      continue;
+    }
+
+    if (cfg->debug)
+      D(cfg->debug_file, "Authenticator path: %s", fido_dev_info_path(di));
+
+    dev = fido_dev_new();
+    if (!dev) {
+      if (cfg->debug)
+        D(cfg->debug_file, "Unable to allocate device type");
+      continue;
+    }
+
+    r = fido_dev_open(dev, fido_dev_info_path(di));
+    if (r != FIDO_OK) {
+      if (cfg->debug)
+        D(cfg->debug_file, "Failed to open authenticator: %s (%d)",
+          fido_strerr(r), r);
+      fido_dev_free(&dev);
+      continue;
+    }
+
+    if (rk || cfg->nodetect) {
+      /* resident credential or nodetect: try all authenticators */
+      authlist[j++] = dev;
+    } else {
+      r = fido_dev_get_assert(dev, assert, NULL);
+      if ((!fido_dev_is_fido2(dev) && r == FIDO_ERR_USER_PRESENCE_REQUIRED) ||
+          (fido_dev_is_fido2(dev) && r == FIDO_OK)) {
+        authlist[j++] = dev;
+        if (cfg->debug)
+          D(cfg->debug_file, "Found key in authenticator %zu", i);
+        return (1);
+      }
+      if (cfg->debug)
+        D(cfg->debug_file, "Key not found in authenticator %zu", i);
+
+      fido_dev_close(dev);
+      fido_dev_free(&dev);
+    }
+  }
+
+  if (j != 0)
+    return (1);
+  else {
+    if (cfg->debug)
+      D(cfg->debug_file, "Key not found");
+    return (0);
+  }
+}
+
+static void init_opts(struct opts *opts) {
+  opts->up = FIDO_OPT_FALSE;
+  opts->uv = FIDO_OPT_OMIT;
+  opts->pin = FIDO_OPT_FALSE;
+}
+
+static void parse_opts(const cfg_t *cfg, const char *attr, struct opts *opts) {
+  if (cfg->userpresence == 1 || strstr(attr, "+presence")) {
+    opts->up = FIDO_OPT_TRUE;
+  } else if (cfg->userpresence == 0) {
+    opts->up = FIDO_OPT_FALSE;
+  } else {
+    opts->up = FIDO_OPT_OMIT;
+  }
+
+  if (cfg->userverification == 1 || strstr(attr, "+verification")) {
+    opts->uv = FIDO_OPT_TRUE;
+  } else if (cfg->userverification == 0)
+    opts->uv = FIDO_OPT_FALSE;
+  else {
+    opts->uv = FIDO_OPT_OMIT;
+  }
+
+  if (cfg->pinverification == 1 || strstr(attr, "+pin")) {
+    opts->pin = FIDO_OPT_TRUE;
+  } else if (cfg->pinverification == 0) {
+    opts->pin = FIDO_OPT_FALSE;
+  } else {
+    opts->pin = FIDO_OPT_OMIT;
+  }
+}
+
+static int get_device_opts(fido_dev_t *dev, int *pin, int *uv) {
+  fido_cbor_info_t *info = NULL;
+  char *const *ptr;
+  const bool *val;
+  size_t len;
+
+  *pin = *uv = -1; /* unsupported */
+
+  if (fido_dev_is_fido2(dev)) {
+    if ((info = fido_cbor_info_new()) == NULL ||
+        fido_dev_get_cbor_info(dev, info) != FIDO_OK) {
+      fido_cbor_info_free(&info);
+      return 0;
+    }
+
+    ptr = fido_cbor_info_options_name_ptr(info);
+    val = fido_cbor_info_options_value_ptr(info);
+    len = fido_cbor_info_options_len(info);
+    for (size_t i = 0; i < len; i++) {
+      if (strcmp(ptr[i], "clientPin") == 0) {
+        *pin = val[i];
+      } else if (strcmp(ptr[i], "uv") == 0) {
+        *uv = val[i];
+      }
+    }
+  }
+
+  fido_cbor_info_free(&info);
+  return 1;
+}
+
+static int match_device_opts(fido_dev_t *dev, struct opts *opts) {
+  int pin, uv;
+
+  /* FIXME: fido_dev_{supports,has}_{pin,uv} (1.7.0) */
+  if (!get_device_opts(dev, &pin, &uv)) {
+    return -1;
+  }
+
+  if (opts->uv == FIDO_OPT_FALSE && uv < 0) {
+    opts->uv = FIDO_OPT_OMIT;
+  }
+
+  if ((opts->pin == FIDO_OPT_TRUE && pin != 1) ||
+      (opts->uv == FIDO_OPT_TRUE && uv != 1)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int set_opts(const cfg_t *cfg, const struct opts *opts,
+                    fido_assert_t *assert) {
+  if (fido_assert_set_up(assert, opts->up) != FIDO_OK) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Failed to set UP");
+    return 0;
+  }
+  if (fido_assert_set_uv(assert, opts->uv) != FIDO_OK) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Failed to set UV");
+    return 0;
+  }
+
+  return 1;
+}
+
+static int set_cdh(const cfg_t *cfg, fido_assert_t *assert) {
+  unsigned char cdh[32];
+  int r;
+
+  if (!random_bytes(cdh, sizeof(cdh))) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Failed to generate challenge");
+    return 0;
+  }
+
+  r = fido_assert_set_clientdata_hash(assert, cdh, sizeof(cdh));
+  if (r != FIDO_OK) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Unable to set challenge: %s (%d)", fido_strerr(r), r);
+    return 0;
+  }
+
+  return 1;
+}
+
+static fido_assert_t *prepare_assert(const cfg_t *cfg, const device_t *device,
+                                     const struct opts *opts) {
+  fido_assert_t *assert = NULL;
+  unsigned char *buf = NULL;
+  size_t buf_len;
+  int ok = 0;
+  int r;
+
+  if ((assert = fido_assert_new()) == NULL) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Unable to allocate assertion");
+    goto err;
+  }
+
+  if (device->old_format && strcmp(cfg->origin, cfg->appid) != 0)
+    r = fido_assert_set_rp(assert, cfg->appid);
+  else
+    r = fido_assert_set_rp(assert, cfg->origin);
+
+  if (r != FIDO_OK) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Unable to set origin: %s (%d)", fido_strerr(r), r);
+    goto err;
+  }
+
+  if (is_resident(device->keyHandle)) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Credential is resident");
+  } else {
+    if (cfg->debug)
+      D(cfg->debug_file, "Key handle: %s", device->keyHandle);
+    if (!b64_decode(device->keyHandle, (void **) &buf, &buf_len)) {
+      if (cfg->debug)
+        D(cfg->debug_file, "Failed to decode key handle");
+      goto err;
+    }
+
+    r = fido_assert_allow_cred(assert, buf, buf_len);
+    if (r != FIDO_OK) {
+      if (cfg->debug)
+        D(cfg->debug_file, "Unable to set keyHandle: %s (%d)", fido_strerr(r),
+          r);
+      goto err;
+    }
+  }
+
+  if (!set_opts(cfg, opts, assert)) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Failed to set assert options");
+    goto err;
+  }
+
+  if (!set_cdh(cfg, assert)) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Failed to set client data hash");
+    goto err;
+  }
+
+  ok = 1;
+
+err:
+  if (!ok)
+    fido_assert_free(&assert);
+
+  free(buf);
+
+  return assert;
+}
+
+static void reset_pk(struct pk *pk) {
+  if (pk->type == COSE_ES256) {
+    es256_pk_free((es256_pk_t **) &pk->ptr);
+  } else if (pk->type == COSE_RS256) {
+    rs256_pk_free((rs256_pk_t **) &pk->ptr);
+  } else if (pk->type == COSE_EDDSA) {
+    eddsa_pk_free((eddsa_pk_t **) &pk->ptr);
+  }
+  memset(pk, 0, sizeof(*pk));
+}
+
+int cose_type(const char *str, int *type) {
+  if (strcasecmp(str, "es256") == 0) {
+    *type = COSE_ES256;
+  } else if (strcasecmp(str, "rs256") == 0) {
+    *type = COSE_RS256;
+  } else if (strcasecmp(str, "eddsa") == 0) {
+    *type = COSE_EDDSA;
+  } else {
+    *type = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+const char *cose_string(int type) {
+  switch (type) {
+    case COSE_ES256:
+      return "es256";
+    case COSE_RS256:
+      return "rs256";
+    case COSE_EDDSA:
+      return "eddsa";
+    default:
+      return "unknown";
+  }
+}
+
+static int parse_pk(const cfg_t *cfg, int old, const char *type, const char *pk,
+                    struct pk *out) {
+  unsigned char *buf = NULL;
+  size_t buf_len;
+  int ok = 0;
+  int r;
+
+  reset_pk(out);
+
+  if (old) {
+    if (!hex_decode(pk, &buf, &buf_len)) {
+      if (cfg->debug)
+        D(cfg->debug_file, "Failed to decode public key");
+      goto err;
+    }
+  } else {
+    if (!b64_decode(pk, (void **) &buf, &buf_len)) {
+      if (cfg->debug)
+        D(cfg->debug_file, "Failed to decode public key");
+      goto err;
+    }
+  }
+
+  if (!cose_type(type, &out->type)) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Unknown COSE type '%s'", type);
+    goto err;
+  }
+
+  // For backwards compatibility, failure to pack the public key is not
+  // returned as an error.  Instead, it is handled by fido_verify_assert().
+  if (out->type == COSE_ES256) {
+    if ((out->ptr = es256_pk_new()) == NULL) {
+      if (cfg->debug)
+        D(cfg->debug_file, "Failed to allocate ES256 public key");
+      goto err;
+    }
+    if (old) {
+      r = translate_old_format_pubkey(out->ptr, buf, buf_len);
+    } else {
+      r = es256_pk_from_ptr(out->ptr, buf, buf_len);
+    }
+    if (r != FIDO_OK) {
+      if (cfg->debug)
+        D(cfg->debug_file, "Failed to convert ES256 public key");
+    }
+  } else if (out->type == COSE_RS256) {
+    if ((out->ptr = rs256_pk_new()) == NULL) {
+      if (cfg->debug)
+        D(cfg->debug_file, "Failed to allocate RS256 public key");
+      goto err;
+    }
+    r = rs256_pk_from_ptr(out->ptr, buf, buf_len);
     if (r != FIDO_OK) {
       if (cfg->debug)
-        D(cfg->debug_file, "Failed to open authenticator: %s (%d)",
-          fido_strerr(r), r);
-      fido_dev_free(&dev);
-      continue;
+        D(cfg->debug_file, "Failed to convert RS256 public key");
     }
-
-    if (kh == NULL || cfg->nodetect) {
-      /* resident credential or nodetect: try all authenticators */
-      authlist[j++] = dev;
-    } else {
-      r = fido_dev_get_assert(dev, assert, NULL);
-      if ((!fido_dev_is_fido2(dev) && r == FIDO_ERR_USER_PRESENCE_REQUIRED) ||
-          (fido_dev_is_fido2(dev) && r == FIDO_OK)) {
-        authlist[j++] = dev;
-        if (cfg->debug)
-          D(cfg->debug_file, "Found key in authenticator %zu", i);
-        return (1);
-      }
+  } else if (out->type == COSE_EDDSA) {
+    if ((out->ptr = eddsa_pk_new()) == NULL) {
       if (cfg->debug)
-        D(cfg->debug_file, "Key not found in authenticator %zu", i);
-
-      fido_dev_close(dev);
-      fido_dev_free(&dev);
+        D(cfg->debug_file, "Failed to allocate EDDSA public key");
+      goto err;
     }
-  }
-
-  if (kh == NULL && j != 0)
-    return (1);
-  else {
+    r = eddsa_pk_from_ptr(out->ptr, buf, buf_len);
+    if (r != FIDO_OK) {
+      if (cfg->debug)
+        D(cfg->debug_file, "Failed to convert EDDSA public key");
+    }
+  } else {
     if (cfg->debug)
-      D(cfg->debug_file, "Key not found");
-    return (0);
+      D(cfg->debug_file, "COSE type '%s' not handled", type);
+    goto err;
   }
+
+  ok = 1;
+err:
+  free(buf);
+
+  return ok;
 }
 
 int do_authentication(const cfg_t *cfg, const device_t *devices,
                       const unsigned n_devs, pam_handle_t *pamh) {
-  es256_pk_t *es256_pk = NULL;
-  rs256_pk_t *rs256_pk = NULL;
   fido_assert_t *assert = NULL;
   fido_dev_info_t *devlist = NULL;
   fido_dev_t **authlist = NULL;
   int cued = 0;
   int r;
   int retval = -2;
-  int cose_type;
-  size_t kh_len;
   size_t ndevs = 0;
   size_t ndevs_prev = 0;
-  size_t pk_len;
-  unsigned char challenge[32];
-  unsigned char *kh = NULL;
-  unsigned char *pk = NULL;
   unsigned i = 0;
-  fido_opt_t user_presence = FIDO_OPT_OMIT;
-  fido_opt_t user_verification = FIDO_OPT_OMIT;
-  fido_opt_t pin_verification = FIDO_OPT_OMIT;
+  struct opts opts;
+  struct pk pk;
   char *pin = NULL;
 
+  init_opts(&opts);
+#ifndef WITH_FUZZING
   fido_init(cfg->debug ? FIDO_DEBUG : 0);
+#else
+  fido_init(0);
+#endif
+  memset(&pk, 0, sizeof(pk));
 
   devlist = fido_dev_info_new(64);
   if (!devlist) {
@@ -524,21 +1347,7 @@ int do_authentication(const cfg_t *cfg, 
   ndevs_prev = ndevs;
 
   if (cfg->debug)
-    D(cfg->debug_file, "Device max index is %u", ndevs);
-
-  es256_pk = es256_pk_new();
-  if (!es256_pk) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Unable to allocate ES256 public key");
-    goto out;
-  }
-
-  rs256_pk = rs256_pk_new();
-  if (!rs256_pk) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Unable to allocate RS256 public key");
-    goto out;
-  }
+    D(cfg->debug_file, "Device max index is %zu", ndevs);
 
   authlist = calloc(64 + 1, sizeof(fido_dev_t *));
   if (!authlist) {
@@ -559,179 +1368,57 @@ int do_authentication(const cfg_t *cfg, 
       D(cfg->debug_file, "Attempting authentication with device number %d",
         i + 1);
 
-    assert = fido_assert_new();
-    if (!assert) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Unable to allocate assertion");
-      goto out;
-    }
-
-    r = fido_assert_set_rp(assert, cfg->origin);
-    if (r != FIDO_OK) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Unable to set origin: %s (%d)", fido_strerr(r), r);
-      goto out;
-    }
-
-    if (!strcmp(devices[i].keyHandle, "*")) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Credential is resident");
-    } else {
-      if (cfg->debug)
-        D(cfg->debug_file, "Key handle: %s", devices[i].keyHandle);
-      if (!b64_decode(devices[i].keyHandle, (void **) &kh, &kh_len)) {
-        if (cfg->debug)
-          D(cfg->debug_file, "Failed to decode key handle");
-        goto out;
-      }
-
-      r = fido_assert_allow_cred(assert, kh, kh_len);
-      if (r != FIDO_OK) {
-        if (cfg->debug)
-          D(cfg->debug_file, "Unable to set keyHandle: %s (%d)", fido_strerr(r),
-            r);
-        goto out;
-      }
-    }
-
-    if (devices[i].old_format) {
-      if (!hex_decode(devices[i].publicKey, &pk, &pk_len)) {
-        if (cfg->debug)
-          D(cfg->debug_file, "Failed to decode public key");
-        goto out;
-      }
-    } else {
-      if (!b64_decode(devices[i].publicKey, (void **) &pk, &pk_len)) {
-        if (cfg->debug)
-          D(cfg->debug_file, "Failed to decode public key");
-        goto out;
-      }
-    }
-
-    if (!strcmp(devices[i].coseType, "es256")) {
-      if (devices[i].old_format) {
-        es256_pk = translate_old_format_pubkey(pk, pk_len);
-        if (es256_pk == NULL) {
-          if (cfg->debug)
-            D(cfg->debug_file, "Failed to convert ES256 public key");
-        }
-      } else {
-        r = es256_pk_from_ptr(es256_pk, pk, pk_len);
-        if (r != FIDO_OK) {
-          if (cfg->debug)
-            D(cfg->debug_file, "Failed to convert ES256 public key");
-        }
-      }
-      cose_type = COSE_ES256;
-    } else if (!strcmp(devices[i].coseType, "rs256")) {
-      r = rs256_pk_from_ptr(rs256_pk, pk, pk_len);
-      if (r != FIDO_OK) {
-        if (cfg->debug)
-          D(cfg->debug_file, "Failed to convert RS256 public key");
-      }
-      cose_type = COSE_RS256;
-    } else {
-      if (cfg->debug)
-        D(cfg->debug_file, "Unknown COSE type '%s'", devices[i].coseType);
-      goto out;
-    }
-
-    if (cfg->userpresence == 1 || strstr(devices[i].attributes, "presence"))
-      user_presence = FIDO_OPT_TRUE;
-    else if (cfg->userpresence == 0)
-      user_presence = FIDO_OPT_FALSE;
-    else
-      user_presence = FIDO_OPT_OMIT;
-
-    if (cfg->userverification == 1 ||
-        strstr(devices[i].attributes, "verification"))
-      user_verification = FIDO_OPT_TRUE;
-    else if (cfg->userverification == 0)
-      user_verification = FIDO_OPT_FALSE;
-    else
-      user_verification = FIDO_OPT_OMIT;
-
-    if (cfg->pinverification == 1 || strstr(devices[i].attributes, "pin")) {
-      pin_verification = FIDO_OPT_TRUE;
-      user_verification = FIDO_OPT_TRUE;
-    } else if (cfg->pinverification == 0)
-      pin_verification = FIDO_OPT_FALSE;
-    else
-      pin_verification = FIDO_OPT_OMIT;
-
-    r = fido_assert_set_up(assert, FIDO_OPT_FALSE);
-    if (r != FIDO_OK) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to set UP");
-      goto out;
-    }
-
-    r = fido_assert_set_uv(assert, FIDO_OPT_OMIT);
-    if (r != FIDO_OK) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to set UV");
-      goto out;
-    }
-
-    if (!random_bytes(challenge, sizeof(challenge))) {
+    init_opts(&opts); /* used during authenticator discovery */
+    assert = prepare_assert(cfg, &devices[i], &opts);
+    if (assert == NULL) {
       if (cfg->debug)
-        D(cfg->debug_file, "Failed to generate challenge");
+        D(cfg->debug_file, "Failed to prepare assert");
       goto out;
     }
 
-    if (cfg->debug) {
-      char *b64_challenge;
-      if (!b64_encode(challenge, sizeof(challenge), &b64_challenge)) {
-        D(cfg->debug_file, "Failed to encode challenge");
-      } else {
-        D(cfg->debug_file, "Challenge: %s", b64_challenge);
-        free(b64_challenge);
-      }
-    }
-
-    r = fido_assert_set_clientdata_hash(assert, challenge, sizeof(challenge));
-    if (r != FIDO_OK) {
+    if (!parse_pk(cfg, devices[i].old_format, devices[i].coseType,
+                  devices[i].publicKey, &pk)) {
       if (cfg->debug)
-        D(cfg->debug_file, "Unable to set challenge: %s( %d)", fido_strerr(r),
-          r);
+        D(cfg->debug_file, "Failed to parse public key");
       goto out;
     }
 
-    if (get_authenticators(cfg, devlist, ndevs, assert, kh, authlist)) {
+    if (get_authenticators(cfg, devlist, ndevs, assert,
+                           is_resident(devices[i].keyHandle), authlist)) {
       for (size_t j = 0; authlist[j] != NULL; j++) {
-        r = fido_assert_set_up(assert, user_presence);
-        if (r != FIDO_OK) {
-          if (cfg->debug)
-            D(cfg->debug_file, "Failed to reset UP");
-          goto out;
-        }
+        /* options used during authentication */
+        parse_opts(cfg, devices[i].attributes, &opts);
 
-        r = fido_assert_set_uv(assert, user_verification);
-        if (r != FIDO_OK) {
-          if (cfg->debug)
-            D(cfg->debug_file, "Failed to reset UV");
-          goto out;
+        r = match_device_opts(authlist[j], &opts);
+        if (r != 1) {
+          if (cfg->debug) {
+            D(cfg->debug_file, "%s, skipping authenticator",
+              r < 0 ? "Failed to query supported options"
+                    : "Unsupported options");
+          }
+          continue;
         }
 
-        if (!random_bytes(challenge, sizeof(challenge))) {
+        if (!set_opts(cfg, &opts, assert)) {
           if (cfg->debug)
-            D(cfg->debug_file, "Failed to regenerate challenge");
+            D(cfg->debug_file, "Failed to set assert options");
           goto out;
         }
 
-        r =
-          fido_assert_set_clientdata_hash(assert, challenge, sizeof(challenge));
-        if (r != FIDO_OK) {
+        if (!set_cdh(cfg, assert)) {
           if (cfg->debug)
-            D(cfg->debug_file, "Unable to reset challenge: %s( %d)",
-              fido_strerr(r), r);
+            D(cfg->debug_file, "Failed to reset client data hash");
           goto out;
         }
 
-        if (pin_verification == FIDO_OPT_TRUE)
+        if (opts.pin == FIDO_OPT_TRUE) {
           pin = converse(pamh, PAM_PROMPT_ECHO_OFF, "Please enter the PIN: ");
-        if (user_presence == FIDO_OPT_TRUE ||
-            user_verification == FIDO_OPT_TRUE) {
+          if (pin == NULL) {
+            D(cfg->debug_file, "converse() returned NULL");
+            goto out;
+          }
+        }
+        if (opts.up == FIDO_OPT_TRUE || opts.uv == FIDO_OPT_TRUE) {
           if (cfg->manual == 0 && cfg->cue && !cued) {
             cued = 1;
             converse(pamh, PAM_TEXT_INFO,
@@ -745,10 +1432,14 @@ int do_authentication(const cfg_t *cfg, 
           pin = NULL;
         }
         if (r == FIDO_OK) {
-          r = fido_assert_verify(assert, 0, cose_type,
-                                 cose_type == COSE_ES256
-                                   ? (const void *) es256_pk
-                                   : (const void *) rs256_pk);
+          if (opts.pin == FIDO_OPT_TRUE || opts.uv == FIDO_OPT_TRUE) {
+            r = fido_assert_set_uv(assert, FIDO_OPT_TRUE);
+            if (r != FIDO_OK) {
+              D(cfg->debug_file, "Failed to set UV");
+              goto out;
+            }
+          }
+          r = fido_assert_verify(assert, 0, pk.type, pk.ptr);
           if (r == FIDO_OK) {
             retval = 1;
             goto out;
@@ -757,7 +1448,7 @@ int do_authentication(const cfg_t *cfg, 
       }
     } else {
       if (cfg->debug)
-        D(cfg->debug_file, "Device for this keyhandle is not present.");
+        D(cfg->debug_file, "Device for this keyhandle is not present");
     }
 
     i++;
@@ -788,12 +1479,6 @@ int do_authentication(const cfg_t *cfg, 
       i = 0;
     }
 
-    free(kh);
-    free(pk);
-
-    kh = NULL;
-    pk = NULL;
-
     for (size_t j = 0; authlist[j] != NULL; j++) {
       fido_dev_close(authlist[j]);
       fido_dev_free(&authlist[j]);
@@ -803,8 +1488,7 @@ int do_authentication(const cfg_t *cfg, 
   }
 
 out:
-  es256_pk_free(&es256_pk);
-  rs256_pk_free(&rs256_pk);
+  reset_pk(&pk);
   fido_assert_free(&assert);
   fido_dev_info_free(&devlist, ndevs);
 
@@ -816,174 +1500,125 @@ out:
     free(authlist);
   }
 
-  free(kh);
-  free(pk);
-
   return retval;
 }
 
 #define MAX_PROMPT_LEN (1024)
 
-int do_manual_authentication(const cfg_t *cfg, const device_t *devices,
-                             const unsigned n_devs, pam_handle_t *pamh) {
-  fido_assert_t *assert[n_devs];
-  es256_pk_t *es256_pk[n_devs];
-  rs256_pk_t *rs256_pk[n_devs];
-  unsigned char challenge[32];
-  unsigned char *kh = NULL;
-  unsigned char *pk = NULL;
-  unsigned char *authdata = NULL;
-  unsigned char *sig = NULL;
-  char *b64_challenge = NULL;
+static int manual_get_assert(const cfg_t *cfg, const char *prompt,
+                             pam_handle_t *pamh, fido_assert_t *assert) {
   char *b64_cdh = NULL;
   char *b64_rpid = NULL;
   char *b64_authdata = NULL;
   char *b64_sig = NULL;
-  char prompt[MAX_PROMPT_LEN];
-  char buf[MAX_PROMPT_LEN];
-  size_t kh_len;
-  size_t pk_len;
+  unsigned char *authdata = NULL;
+  unsigned char *sig = NULL;
   size_t authdata_len;
   size_t sig_len;
-  int cose_type[n_devs];
-  int retval = -2;
-  int n;
   int r;
-  unsigned i = 0;
-  bool user_presence = false;
-  bool user_verification = false;
-
-  memset(assert, 0, sizeof(assert));
-  memset(es256_pk, 0, sizeof(es256_pk));
-  memset(rs256_pk, 0, sizeof(rs256_pk));
-
-  fido_init(cfg->debug ? FIDO_DEBUG : 0);
-
-  for (i = 0; i < n_devs; ++i) {
-
-    assert[i] = fido_assert_new();
-    if (!assert[i]) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Unable to allocate assertion %u", i);
-      goto out;
-    }
-
-    r = fido_assert_set_rp(assert[i], cfg->origin);
-    if (r != FIDO_OK) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Unable to set origin: %s (%d)", fido_strerr(r), r);
-      goto out;
-    }
-
-    if (strstr(devices[i].attributes, "presence"))
-      user_presence = true;
-    if (strstr(devices[i].attributes, "verification"))
-      user_verification = true;
-
-    r = fido_assert_set_up(assert[i], user_presence);
-    if (r != FIDO_OK) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Unable to set UP: %s (%d)", fido_strerr(r), r);
-      goto out;
-    }
+  int ok = 0;
 
-    r = fido_assert_set_uv(assert[i], user_verification);
-    if (r != FIDO_OK) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Unable to set UV: %s (%d)", fido_strerr(r), r);
-      goto out;
-    }
+  b64_cdh = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
+  b64_rpid = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
+  b64_authdata = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
+  b64_sig = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
 
+  if (!b64_decode(b64_authdata, (void **) &authdata, &authdata_len)) {
     if (cfg->debug)
-      D(cfg->debug_file, "Attempting authentication with device number %d",
-        i + 1);
-
-    if (!strcmp(devices[i].keyHandle, "*")) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Credential is resident");
-    } else {
-      if (!b64_decode(devices[i].keyHandle, (void **) &kh, &kh_len)) {
-        if (cfg->debug)
-          D(cfg->debug_file, "Failed to decode key handle");
-        goto out;
-      }
+      D(cfg->debug_file, "Failed to decode authenticator data");
+    goto err;
+  }
 
-      r = fido_assert_allow_cred(assert[i], kh, kh_len);
-      if (r != FIDO_OK) {
-        if (cfg->debug)
-          D(cfg->debug_file, "Unable to set keyHandle: %s (%d)", fido_strerr(r),
-            r);
-        goto out;
-      }
+  if (!b64_decode(b64_sig, (void **) &sig, &sig_len)) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Failed to decode signature");
+    goto err;
+  }
 
-      free(kh);
-      kh = NULL;
-    }
+  r = fido_assert_set_count(assert, 1);
+  if (r != FIDO_OK) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Failed to set signature count of assertion");
+    goto err;
+  }
 
-    if (devices[i].old_format) {
-      if (!hex_decode(devices[i].publicKey, &pk, &pk_len)) {
-        if (cfg->debug)
-          D(cfg->debug_file, "Failed to decode public key");
-        goto out;
-      }
-    } else {
-      if (!b64_decode(devices[i].publicKey, (void **) &pk, &pk_len)) {
-        if (cfg->debug)
-          D(cfg->debug_file, "Failed to decode public key");
-        goto out;
-      }
-    }
+  r = fido_assert_set_authdata(assert, 0, authdata, authdata_len);
+  if (r != FIDO_OK) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Failed to set authdata of assertion");
+    goto err;
+  }
 
-    if (!strcmp(devices[i].coseType, "es256")) {
-      es256_pk[i] = es256_pk_new();
-      if (!es256_pk[i]) {
-        if (cfg->debug)
-          D(cfg->debug_file, "Unable to allocate key %u", i);
-        goto out;
-      }
+  r = fido_assert_set_sig(assert, 0, sig, sig_len);
+  if (r != FIDO_OK) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Failed to set signature of assertion");
+    goto err;
+  }
 
-      if (es256_pk_from_ptr(es256_pk[i], pk, pk_len) != FIDO_OK) {
-        if (cfg->debug)
-          D(cfg->debug_file, "Failed to convert public key");
-        goto out;
-      }
+  ok = 1;
+err:
+  free(b64_cdh);
+  free(b64_rpid);
+  free(b64_authdata);
+  free(b64_sig);
+  free(authdata);
+  free(sig);
 
-      cose_type[i] = COSE_ES256;
-    } else {
-      rs256_pk[i] = rs256_pk_new();
-      if (!rs256_pk[i]) {
-        if (cfg->debug)
-          D(cfg->debug_file, "Unable to allocate key %u", i);
-        goto out;
-      }
+  return ok;
+}
 
-      if (rs256_pk_from_ptr(rs256_pk[i], pk, pk_len) != FIDO_OK) {
-        if (cfg->debug)
-          D(cfg->debug_file, "Failed to convert public key");
-        goto out;
-      }
+int do_manual_authentication(const cfg_t *cfg, const device_t *devices,
+                             const unsigned n_devs, pam_handle_t *pamh) {
+  fido_assert_t **assert = NULL;
+  struct pk *pk = NULL;
+  char *b64_challenge = NULL;
+  char prompt[MAX_PROMPT_LEN];
+  char buf[MAX_PROMPT_LEN];
+  int retval = -2;
+  int n;
+  int r;
+  unsigned i = 0;
+  struct opts opts;
 
-      cose_type[i] = COSE_RS256;
-    }
+  init_opts(&opts);
+  assert = calloc(n_devs, sizeof(*assert));
+  if (assert == NULL)
+	goto out;
+  pk = calloc(n_devs, sizeof(*pk));
+  if (pk == NULL)
+	goto out;
 
-    free(pk);
-    pk = NULL;
+#ifndef WITH_FUZZING
+  fido_init(cfg->debug ? FIDO_DEBUG : 0);
+#else
+  fido_init(0);
+#endif
 
-    if (!random_bytes(challenge, sizeof(challenge))) {
+  for (i = 0; i < n_devs; ++i) {
+    /* options used during authentication */
+    parse_opts(cfg, devices[i].attributes, &opts);
+    assert[i] = prepare_assert(cfg, &devices[i], &opts);
+    if (assert[i] == NULL) {
       if (cfg->debug)
-        D(cfg->debug_file, "Failed to generate challenge");
+        D(cfg->debug_file, "Failed to prepare assert");
       goto out;
     }
 
-    r =
-      fido_assert_set_clientdata_hash(assert[i], challenge, sizeof(challenge));
-    if (r != FIDO_OK) {
+    if (cfg->debug)
+      D(cfg->debug_file, "Attempting authentication with device number %d",
+        i + 1);
+
+    if (!parse_pk(cfg, devices[i].old_format, devices[i].coseType,
+                  devices[i].publicKey, &pk[i])) {
       if (cfg->debug)
-        D(cfg->debug_file, "Failed to set challenge");
+        D(cfg->debug_file, "Unable to parse public key %u", i);
       goto out;
     }
 
-    if (!b64_encode(challenge, sizeof(challenge), &b64_challenge)) {
+    if (!b64_encode(fido_assert_clientdata_hash_ptr(assert[i]),
+                    fido_assert_clientdata_hash_len(assert[i]),
+                    &b64_challenge)) {
       if (cfg->debug)
         D(cfg->debug_file, "Failed to encode challenge");
       goto out;
@@ -1029,65 +1664,13 @@ int do_manual_authentication(const cfg_t
       goto out;
     }
 
-    b64_cdh = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
-    b64_rpid = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
-    b64_authdata = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
-    b64_sig = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
-
-    if (!b64_decode(b64_authdata, (void **) &authdata, &authdata_len)) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to decode authenticator data");
-      goto out;
-    }
-
-    if (!b64_decode(b64_sig, (void **) &sig, &sig_len)) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to decode signature");
-      goto out;
-    }
-
-    free(b64_cdh);
-    free(b64_rpid);
-    free(b64_authdata);
-    free(b64_sig);
-
-    b64_cdh = NULL;
-    b64_rpid = NULL;
-    b64_authdata = NULL;
-    b64_sig = NULL;
-
-    r = fido_assert_set_count(assert[i], 1);
-    if (r != FIDO_OK) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to set signature count of assertion %u", i);
-      goto out;
-    }
-
-    r = fido_assert_set_authdata(assert[i], 0, authdata, authdata_len);
-    if (r != FIDO_OK) {
+    if (!manual_get_assert(cfg, prompt, pamh, assert[i])) {
       if (cfg->debug)
-        D(cfg->debug_file, "Failed to set authdata of assertion %u", i);
+        D(cfg->debug_file, "Failed to get assert %u", i);
       goto out;
     }
 
-    r = fido_assert_set_sig(assert[i], 0, sig, sig_len);
-    if (r != FIDO_OK) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to set signature of assertion %u", i);
-      goto out;
-    }
-
-    free(authdata);
-    free(sig);
-
-    authdata = NULL;
-    sig = NULL;
-
-    if (cose_type[i] == COSE_ES256)
-      r = fido_assert_verify(assert[i], 0, COSE_ES256, es256_pk[i]);
-    else
-      r = fido_assert_verify(assert[i], 0, COSE_RS256, rs256_pk[i]);
-
+    r = fido_assert_verify(assert[i], 0, pk[i].type, pk[i].ptr);
     if (r == FIDO_OK) {
       retval = 1;
       break;
@@ -1097,19 +1680,11 @@ int do_manual_authentication(const cfg_t
 out:
   for (i = 0; i < n_devs; i++) {
     fido_assert_free(&assert[i]);
-    es256_pk_free(&es256_pk[i]);
-    rs256_pk_free(&rs256_pk[i]);
+    reset_pk(&pk[i]);
   }
-
-  free(kh);
+  free(assert);
   free(pk);
   free(b64_challenge);
-  free(b64_cdh);
-  free(b64_rpid);
-  free(b64_authdata);
-  free(b64_sig);
-  free(authdata);
-  free(sig);
 
   return retval;
 }
@@ -1131,7 +1706,7 @@ static int _converse(pam_handle_t *pamh,
 
 char *converse(pam_handle_t *pamh, int echocode, const char *prompt) {
   const struct pam_message msg = {.msg_style = echocode,
-                                  .msg = (char *)(uintptr_t)prompt};
+                                  .msg = (char *) (uintptr_t) prompt};
   const struct pam_message *msgs = &msg;
   struct pam_response *resp = NULL;
   int retval = _converse(pamh, 1, &msgs, &resp);
@@ -1163,7 +1738,12 @@ void _debug(FILE *debug_file, const char
             const char *fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
-#ifdef LOG_DEBUG
+
+#if defined(WITH_FUZZING)
+  (void) debug_file;
+  snprintf(NULL, 0, DEBUG_STR, file, line, func);
+  vsnprintf(NULL, 0, fmt, ap);
+#elif defined(LOG_DEBUG)
   if (debug_file == (FILE *) -1) {
     syslog(LOG_AUTHPRIV | LOG_DEBUG, DEBUG_STR, file, line, func);
     vsyslog(LOG_AUTHPRIV | LOG_DEBUG, fmt, ap);

Reply via email to