r4gus/keylib

Wrong algorithm of public key

Closed this issue · 8 comments

I created a simple authenticator using the C bindings and it seems to work as expected. However im getting an error when trying to register on https://passkey.org/ : Wrong algorithm of public key!

The credentials are written to my authenticator but the website throws the error.
Now im wondering what I can do to circumvent this? Do I need to change the settings and is there even a straightforward way to do this using the C bindings?

I haven't updated the C bindings for a while so maybe this is the problem. If you provide your code, I can look into this.

Still very prototype like so dont mind debug statements etc. Any help would be greatly appreciated

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <time.h>


#include "vault.cpp"

extern "C" {
  #include "keylib/keylib.h"
  #include "keylib/uhid.h"
}



Vault vault = Vault();


// Make a user presence check
int my_up(const char *info, const char *user, const char *rp)
{
  printf("Getting user presence\n");
  //char c = getchar();
  sleep(1);
  return UpResult_Accepted;
}

// This callback should implement some form of user verification, e.g. when called, ask a user for a password.
//
// This function should return Accepted, Denied, or Timeout
int my_uv(const char *info, const char *user, const char *rp)
{
  printf("Getting User verification\n");
  //char c = getchar();
  sleep(1);
  return UpResult_Accepted;
}

// Let the user select one of multiple credentials associated with a relying party.
//
// You should either return the users index or -1.
int my_selected_cred(const char *rpId, char **users)
{
  printf("Getting selected cred\n");
  return 0;
}

// Read data from vault.
// if id or rp is given return respectiv data. else return everything
// memory assigned to out is owned by authenticator (aka no delete needed)
int my_read(const char *id, const char *rp, char ***out)
{
  std::cout << "Reading from vault" << std::endl;


  if(id != NULL)
  {

    VaultEntry* entry = vault.getEntry(id);
    if(entry == nullptr)
    {
      return Error_DoesNotExist;
    }

    char* data = new char[strlen(entry->data) + 1];

    strncpy(data, entry->data, strlen(entry->data));
    data[strlen(entry->data)] = '\0';


    char** read = new char*[2];
    read[0] = data;
    read[1] = NULL;

    *out = read;


    return Error_SUCCESS;
  }
  else if(rp != NULL)
  {
    std::vector<VaultEntry*> entries = vault.getRpEntries(rp);

    char** read = new char*[entries.size() + 1];
    for(int i = 0; i < entries.size(); i++)
    {
      read[i] = entries[i]->data;
    }
    read[entries.size()] = nullptr;

    *out = read;
    return Error_SUCCESS;
  }
  else
  {
    if (vault.entries.size() == 0)
    {
      return Error_DoesNotExist;
    }

    char** read = new char*[vault.entries.size() + 1];

    for(int i = 0; i < vault.entries.size(); i++)
    {
      read[i] = vault.entries[i]->data;
    }
    read[vault.entries.size()] = nullptr;

    *out = read;
    return Error_SUCCESS;
  }

  return Error_DoesNotExist;
}

// Persist the given data id is unique (sending same id twice will overwrite the data)
int my_write(const char *id, const char *rp, const char *data)
{

  //deleting old entry with same id
  if(vault.getEntry(id) != nullptr)
  {
    vault.deleteEntry(id);
  }

  vault.addEntry(id, rp, data);
  
  return Error_SUCCESS;
}

// Delete the data associated with the given id.
int my_delete(const char *id)
{
  printf("delete");

  vault.deleteEntry(id);
  return 0;
}

int main()
{

  printf("Init auth struct\n");

  Callbacks c = {
      my_up, my_uv, my_selected_cred, my_read, my_write, my_delete
  };

  void *auth = auth_init(c);

  if (auth == NULL)
  {
    printf("Auth struct not initialized\n");
    return -1;
  }

  printf("Auth struct initialized\n");

  printf("Init uhid\n");

  // expects incoming usb packets
  void *ctaphid_handler = ctaphid_init();
  int fd = uhid_open();

  if (fd < 0 || ctaphid_handler == NULL)
  {
    printf("UHID not initialized\n");
    return -1;
  }

  printf("UHID initialized\n");

  while (1)
  {
    char buffer[64];
    int packet_length = uhid_read_packet(fd, &buffer[0]); // read a packet (if available)

    if (packet_length)
    {
      // The handler will either return NULL or a pointer to
      // a ctaphid packet iterator.
      void *iter = ctaphid_handle(ctaphid_handler, &buffer[0], packet_length, auth);

      // Every call to next will return a 64 byte packet ready
      // to be sent to the host.
      if (iter)
      {
        char out[64];

        while (ctaphid_iterator_next(iter, &out[0]))
        {
          uhid_write_packet(fd, &out[0], 64);
        }

        // Don't forget to free the iterator
        ctaphid_iterator_deinit(iter);
      }
    }
  }

  uhid_close(fd);
  ctaphid_deinit(ctaphid_handler);

  return 0;
}

Thanks for the code. One question: do you link a static/ shared library... I guess so ??? I ask because I commented out building the c library because I made some breaking changes:

// TODO: doesn't make much sense right now to provide C bindings

If you could provide me with more information about what you did, I can probably better help you. And btw, thanks for the interest in the library.

Yeah my build.zig file looks like this

`
const std = @import("std");

pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

const keylib_dep = b.dependency("keylib", .{
    .target = target,
    .optimize = optimize,
});

const exe = b.addExecutable(.{
    .name = "virtual_authenticator",
    .root_source_file = .{ .path = "src/main.cpp" },
    .target = target,
    .optimize = optimize,
});
exe.linkLibrary(keylib_dep.artifact("keylib"));
exe.linkLibrary(keylib_dep.artifact("uhid"));
exe.linkSystemLibrary("c++");
//exe.linkLibC();
b.installArtifact(exe);

const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
    run_cmd.addArgs(args);
}

const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);

}`

As for what I did is just basically following the wiki and storing the credentials in memory for now.
Im pretty happy with the results as it works with webauthn.io and with github but I would definitely like to expand on that project a bit further.
I would also be open to help to bring the C bindings up to date (though my Zig knowledge is not all to deep)

I'm happy to hear that it works for some sites but it should actually work for all that support es256. So I'm regularly using passkey.org to test my passkey service for Linux and never encountered that error.

Do you mind attaching your whole project as a tarball or zip to this discussion? I want to look into this over the weekend, and it makes it easier (and faster) to reproduce the issue with your code.

Update: So I've tested it with my passkey service on Linux (https://github.com/r4gus/keypass) and I can register a credential with passkey.org. So maybe there is something wrong with the standard init function (configuration).

Sure heres my project:
virtual_authenticator.zip

@Twistedp

Fixed with latest commit.

The problem was that github synced the wiki pages when I moved from codeberg back to github (which I wasn't aware of). The build.zig.zon example pointed to an old version on codeberg (this is why you were able to use the bindings in the first place; I commented them out on github).

I reintroduced the C bindings but they are far form perfect/complete.

You project now works with passkey.org (sorry for burning your username for the next 24 hours on passkey.org).

Screenshot from 2024-04-06 13-56-38

Here is your updated project: va_new.tar.gz

If you're interested in contributing to the project, you could use your project to update the wiki. We could also add it to the examples folder but it would be best to have everything in one file (main.cpp).