Add a C API to SOP
SOP C API
If we want to translate SOP into a C API, how do we do it?
What version of C are we targeting? (C89 or later?)
data types
Some of these data types are potentially complex. We don't want anything complex exposed in the generic interface, so most of them should be opaque types.
How does the user convert from bytestreams to opaque types? (uint8*, size_t) tuples? file descriptors? how does the user convert back?
- we'll start with (uint8*, size_t) tuples ("buffers"). we think file descriptors could be tacked on later, but we won't prioritize
- for serialization, the library will allocate the buffer, which requires a library-specific de-allocation interface for the buffer. (people with their own memory-management schemes can always copy data out of the buffer)
some tentative decisions:
- USERID is a null-terminated C string
- MICALG is a custom, non-opaque char buffer.
- buffers: PASSWORD, DATA
- DATE is uint64, where 0 and (-1) are special: we need two special values: "now", and "no constraint"
- every other type is its own opaque data type
allocation and deallocation
If a data type is created, how does it get destroyed?
- for opaque data types, there will be a destructor, which the creator of the object is responsible for invoking.
function signatures
Most of the CLI arguments are optional, and many are repeatable. can we structure the C functions in a sensible, regular way for optional or repeatable arguments?
- for optional arguments, we are ok with having custom/null values for each of them
options:
- "builder pattern" -- common in some languages, but not ergonomic in C because of lack of methods
- fixed function definitions, with variadic arguments represented with linked lists
- fixed function definitions, with variadic arguments represented by list of pointers with a sentinel
Library context object?
Do we need a library context object? Will we ever? if we don't use one now, it might be hard to add later.
(for more info about library context objects, see libabc guidance )
- we think we should have one, it is an extension point for implementers
- initial use would allow changing deserialization defaults b/w ascii-armor and binary formats
- context object might also retain textual information about most recent failure
- context object gets passed to all functions except:
- destructors
- constructors (we think)
Error handling
Proposal: each function that can fail returns an integer that maps to the standard SOP error codes
examples:
struct sop_key_st;
typedef sop_key_st * sop_key;
struct sop_st; /* SOP library context object */
typedef sop_st * sop;
sop sop_context_init ();
void sop_context_destroy (sop ctx);
const char * sop_context_get_last_error (sop ctx);
sop_error_t sop_context_get_last_error_number (sop ctx);
void sop_context_armor_output (sop ctx, bool armor);
sop_error_t
sop_extract_cert (sop ctx, const sop_key key, sop_certs* out);
sop_key
sop_key_import (sop ctx, const uint8_t* data, size_t len);
void
sop_key_destroy (sop ctx, sop_key key);
void
sop_key_append_key (sop ctx, sop_key key, sop_key next);
uint8_t *
sop_key_export (sop ctx, const sop_key key, size_t *len);
void
sop_free_buffer (sop ctx, uint8_t *buffer); /* pass length? */
typedef char[16] sop_micalg;
typedef enum { SOP_SIGN_AS_BINARY, SOP_SIGN_AS_TEXT } sop_sign_as;
sop_error_t
sop_sign (sop ctx, sop_micalg * micalg, sop_sign_as sign_as, sop_key key);
sop_certs
sop_certs_import (sop ctx, const uint8_t* data, size_t len);
void
sop_certs_destroy (sop ctx, sop_certs certs);
void
sop_certs_append_certs (sop ctx, sop_certs certs, sop_certs next);
examples
sop_error_t err;
if ((err = ...) != SOP_SUCCESS) {
fprintf (stderr, "...%s\n", sop_context_get_last_error (sop))
}
proof of concept
TODO: produce a C implementation that creates the CLI from the C API
integration with documentation
it would make sense to include a new subsection in each command section that includes the C API for it. It would also be good to emit a .h file as an attachment to the RFC using the <sourcecode> xml element.