= Kync v1
:toc:
== General
Kync describes a **K**e**Y**e**NC**apsulation plugin-API for key encapsulation plugins.
Key encapsulation is the process of encrypting an already existing key with another different key. This allows you to
separate your application from the key storage so that the user can specify how the key is stored – e.g.
password-encrypted in a file or in the OS' keychain.
== Plugin API
To achive a simple and cross-platform compatible API, we use dynamic libraries that expose a C API/ABI.
=== General Rules
These general rules apply to all API functions and *MUST* be respected by any plugin that implement this API:
. Each function that may fail *MUST* return the error type
. No ownership is transferred
. An API-call *MUST NOT* take longer than 90 seconds before returning
. All functions *MUST* be threadsafe as long as their parameters are not shared
=== API Overview
These functions are defined by the API and *MUST* be implemented:
. `init`: Takes care of the library initialization and API version validation
. `capsule_format_uid`: Returns the capsule format UID for the implemented format
. `capsule_key_ids`: Gets all available capsule key IDs (for plugins that have their own key storage; e.g. GnuPG)
. `seal`: Seals some key bytes into a capsule
. `open`: Extracts the key bytes from a capsule
==== init
[source,cpp]
----
error_t init(uint8_t api_version, uint8_t logging_level);
----
This function initializes the library and returns an `error_t` to check if the initialization failed.
Parameters:
. `api_version`: The API version _expected by the caller_. The plugin *MUST* validate the expected version and return an
error if it does not implement it. The API version defined by this document is `1`.
. `logging_level`: The logging level the plugin should use (`0` means no logging). _Note: This only applies to StdErr_
==== capsule_format_uid
[source,cpp]
----
uint8_t[64] capsule_format_uid();
----
This function returns the plugin's capsule format UID. It *MUST NOT* fail.
The capsule format UID is a 64 byte UTF-8 format UID – this format-UID *MUST* map to one format only. To achieve this,
we append a random UUID; e.g. `AES-256-GCM-IETF.v1________.B593CCDE-B5C3-433A-ABBA-9087FBF13E60`. If you write a fully
compatible drop-in replacement for an already existing plugin, you *MAY* use the original format UID.
==== capsule_key_ids
[source,cpp]
----
error_t capsule_key_ids(slice_t* id_buffer);
----
This function writes all available capsule key IDs into `id_buffer`. This function is useful if the plugin offers it's
own key store that you can/must use (e.g. GnuPG).
The IDs are `uint8_t[256]`-arrays, which are just concatenated and written to `id_buffer`. If an ID is shorter than 256
bytes, it *MUST* be `'\0'`-padded. The ID must be an UTF-8 string.
Parameter `id_buffer`: The buffer into which the IDs are written.
==== seal
[source,cpp]
----
error_t seal(slice_t* dest, slice_t const* key_to_seal, slice_t const* capsule_key_id, slice_t const* user_secret);
----
This function seals the key bytes in `key_to_seal` and writes the resulting data to `dest`.
Parameters:
. `dest`: The payload destination
. `key_to_seal`: The slice containing the key bytes to seal
. `capsule_key_id`: The capsule key ID to use (see `capsule_key_ids`). This parameter *MAY* be `NULL` if it's not
necessary for the call.
. `user_secret`: A user-provided secret. This parameter may have multiple, plugin-dependent purposes; ranging from a
hardware-token-PIN to the capsule key itself. It *MAY* be `NULL` if it's not necessary for the call – however _if_ it
is `NULL`, a plugin *MUST NOT* perform any authentication attempts that could decrease a retry counter.
==== open
[source,cpp]
----
error_t open(slice_t* dest, slice_t const* payload, slice_t const* user_secret);
----
This function opens a key capsule and writes the resulting key bytes into `dest`.
Parameters:
. `dest`: The plaintext-key destination
. `payload`: The capsule payload
. `user_secret`: A user-provided secret. This parameter may have multiple, plugin-dependent purposes; ranging from a
hardware-token-PIN to the capsule key itself. It *MAY* be `NULL` if it's not necessary for the call – however _if_ it
is `NULL`, a plugin *MUST NOT* perform any authentication attempts that could decrease a retry counter.
==== slice_t
[source,cpp]
----
typedef struct {
uint8_t* data;
size_t capacity;
size_t len;
void* handle;
void (*reallocate)(slice_t* slice, size_t new_size);
} slice_t;
----
A slice is a struct that holds all components to represent a vector-type. The advantage of this pattern (in comparison
to a simple `ptr,len`-pattern) is, that it allows dynamic memory reallocation from within the plugin.
Fields:
. `data`: A pointer to the allocated memory region
. `len`: The amount of meaningful bytes at `data`
. `capacity`: The amount of allocated memory
. `handle`: A pointer to a handle (*MAY* handle)
. `reallocate`: A callback to reallocate data (*MAY* be `NULL`) – if this function fails, it *MUST*
https://en.wikipedia.org/wiki/C_process_control#abort[abort] the application.
Example:
[source,cpp]
----
void reallocate(slice_t* slice, size_t new_size) {
auto vector = reinterpret_cast<std::vector<uint8_t>*>(slice->handle);
vector->resize(new_size);
slice->data = vector->data();
slice->capacity = new_size;
slice->len = std::min(slice->len, slice->capacity);
}
int main(int argc, const char** argv) {
// Create a buffer and a slice over it
std::vector<uint8_t> buf;
slice_t slice {
.data = buf.data(), .capacity = buf.capacity(), .len = buf.size(),
.handle = &buf, .reallocate = reallocate
};
// Do something with slice
do_sth_with_slice(&slice);
// Take length from slice and do something with the buffer
buf.resize(slice.len);
do_sth_with_buf(&buf);
return 0;
}
----
=== Errors
Errors are defines as a constant error type-ID plus some additional type-dependent error information:
[source,cpp]
----
typedef struct {
uint8_t type_id[16]; // -> the error type as `\0`-padded string (e.g. "ENONE" or "EINIT")
uint8_t file[256]; // -> the file where the error occurred as `\0`-padded string (e.g. "main.c")
uint32_t line; // -> the line where the error occurred
uint8_t description[1024]; // -> an error description as `\0`-padded string
uint64_t info; // -> some type-dependent error info
} error_t;
----
==== ENONE
`ENONE` is the placeholder if no error occurred. `info` is usually unused.
==== EINIT
`EINIT` indicates a fatal error during library initialization. `info` is unused.
==== EPERM
`EPERM` indicates that an operation is not permitted (at least without providing authentication data).
`info` indicates if the action requires authentication (`info != 0`) or if the action will always fail (`info == 0`).
==== EACCES
`EACCES` indicates an authentication error. `info` indicates the amount of retries left; if there is no retry-limit,
`info` is `UINT64_MAX`.
==== ENOBUF
`ENOBUF` indicates insufficient buffer space – this happens if the allocated space is insufficient and the
`reallocate`-callback is `NULL`. `info` indicates the required size.
==== EIO
`EIO` indicates an I/O-related error. `info` is unused.
==== EILSEQ
`EILSEQ` indicates invalid capsule data. `info` is unused.
==== ENOKEY
`ENOKEY` indicates that there is no matching key available to decrypt the capsule. `info` is unused.
==== ECANCELED
`ECANCELED` indicates that the operation was canceled. `info` is unused.
==== ETIMEDOUT
`ETIMEDOUT` indicates that the operation timed out – either because it hit the 90s deadline or because something else
timed out (e.g. hardware token). `info` is unused.
==== EOTHER
`EOTHER` indicates that an unspecified fatal error occurred. `info` *MAY* be a plugin-specific error code and *MUST* be
ignored if it's meaning is unknown.