Skip to content

Getting Started

David Sugar edited this page Oct 14, 2023 · 8 revisions

Welcome, this guide should help you get started with keylib. If you have any questions or encounter bugs, please open an issue.

Keylib is a library designed to assist you in implementing PassKeys, which are platform authenticators compatible with FIDO2. This library handles the task of generating new credentials and assertions when required, and your responsibility is to furnish it with the necessary callbacks. Keylib currently offers two interfaces, one for Zig and another one for C.

1) Callbacks

The first step when using this library is to implement the required callbacks (C, Zig).

C

The first thing we must do is include the keylib.h, header file into our code. This file includes enum definitions, the callback signatures, the definition of the Datastruct which we will need when implementing the read callback, and functions for interacting with the library.

#include "keylib/keylib.h" // Data, Denied, DoesNotExist

Next we implement the uv (user verification) and up (user presence) callbacks. The uv callback is used to authenticate a user, e.g. by asking for a password. What authentication method you want to use for your application is up to you. The up callback is used to verify the (physical) presence of a user. You can for example do this by displaying a button the user can click on. Both functions return Accepted on success, Denied if rejected (e.g. user entered wrong password or clicked cancel), and Timeout if a timeout has occurred.

// Make a user presence check, e.g. display a button and ask the user if she wants to confirm the action.
// The arguments info, user, and rp are null terminated strings that provide additional context that
// you can display.
//
// This function should return Accepted, Denied, or Timeout
int my_up(const char* info, const char* user, const char* rp) {
    printf("up\n");
    return Denied;
}

// 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() {
    printf("uv\n");                                                                                  
    return Denied;
}

Next we implement the select_cred callback. This callback is used if more than one credential is bound to the same relying party (the service the credential was created for). The callback will provide a null terminated array of user strings. Those strings should be displayed on the screen and the user must then select a user. The function returns either a user index (starting from 0) or a negative error value.

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

The authenticator also needs some way to read, write and delete data. The read callback takes a id string (the unique credential id), a rp string (the relying party id/ base url), and a pointer to a string array.

  • If id is not null:
    • Create a string array with two elements, where the first element contains the requested data and the second element is a null terminator. Assign the array to out. Then return SUCCESS.
  • If id is null and rp is not null:
    • Create a null terminated string array that contains all data associated with the given rp. Assign the array to out. Then return SUCCESS.
  • Else:
    • Create a null terminated string array that contains all stored data. Assign the array to out. Then return SUCCESS.

Return DoesNotExist if no data could be found.

// Read data from permanent storage.
int my_read(const char* id, const char* rp, char*** out) {
    printf("read");
    return DoesNotExist;
}

NOTE: It is important that the array itself and all (data) strings are null terminated!

The following is an example how the creation of a array with one element could look like:

char** x = malloc(sizeof(char*) * 2);
x[0] = data; // think of data as a hex encodex, null terminated string
x[1] = NULL;
*out = x;
return Error_SUCCESS;

All memory assigned to out is owned by the authenticator, i.e. it's NOT you responsibility to free it.


The write callback is used to persist the given data. It is both used to create new entries and to update existing ones. You can assume that the credential id is unique, i.e. if the id already exists update (overwrite) the existing entry, otherwise create a new one. Make sure you associate the data with the given relying party id (rp) so you can find it later.

// Persist the given data and make sure that it can be found using its id and associated rp (relying party).
int my_write(const char* id, const char* rp, const char* data) {
    printf("write");
    return -1;
}

The delete callback is quite simple, find and delete the data associated with the given id.

// Delete the data associated with the given id.
int my_delete(const char* id, const char* rp) {
    printf("delete\n");
    return -1;
}
Clone this wiki locally