Originally posted by @drbild over a year ago to the original repository.
Though this is significantly out of date, I think the principles are still relevant, and wanted to retain the discussion.
Crypto interface design
This is a brain-dump for now of my thoughts about the crypto interface design. We can refine it into a better description & discussion over time.
Implications of Design Goals for XTT Implementation
No dynamic memory allocation required.
Support arbitrary implementations of the crypto primitives.
Allow asynchronous crypto primitives for non-blocking use of HSMs.
Be as performant (nearly) as a monolithic crypto lib.
These four goals alone pose some interesting incompatibilities. Consider the following possible implementations.
Shared header w/ arbitrary linked implementation
No dynamic memory allocation implies that all struct definitions must be visible to the calling code. The shared header thus implies that all implementations must share the same struct definitions. Of course, the different possible implementations for linking will use different underlying representations for primitives like keys. Thus, the representations must be translated at the interface boundary for every call. And that is not performant.
The translation could be done (at the cost of some cycles) were the data types always representing the same concept, e.g., different representations of an ed25519 key. The interface would define a common serialization format to translate to and from. But consider an implementation backed by an HSM. The private key would not actually be a key, but instead some opaque identifier for the key inside the HSM. Finding a clean, common representation seems difficult.
An alternative is for the interface to take pointers to opaque structs defined by the implementation. But this requires dynamic memory allocation, as those opaque structs would have to be allocated by the linked implementation code, where the full definition is known.
Shared header for functions, implementation-specific header for data types
This approach would solve the static allocation issue. The XTT library would have to be compiled against a specific implementation defining the structs. (The shared header defining the methods would just have forward declarations.)
I'm leaning towards this approach currently. My general feeling is that the crypto implementation should be statically linked to the XTT library anyway (not like openSSL with a shared lib for the crypto). So this keep everything clean.
Polymorphism - the Compile/Runtime, Global/Local matrix
The "arbitrary implementation" design goal is still ill-defined. Should the implementation be chosen at compile time or be configurable at run-time? Should it be common to the whole program or changeable for each XTT "session"?
The two approaches in the prior section are in the (compile-time, global) quadrant of the design space.
Supporting runtime selection or local per-session selection of primitives seems to require either dynamic memory allocation or a shared/translatable representation of the data structures, which are non starters.
Proposed Approach
XTT provides a shared header that forward declares the structs and methods used to access crypto primitives. The user is responsible for compiling XTT against an implementation that defines those structs and methods.
We provide two (or more) reference implementations that delegate to different libraries (say mbedTLS and wolfSSL).
These two implementations are in the (compile-time, global) quadrant above. They are applied globally to all XTT sessions within the program.
Any user is free to provide their own implementation instead if they don't like our chosen point in the design space. Such an implementation could
mix and match from various backends
support linking-based selection of the backend by implementing a translation layer (like the first approach listed above)
support runtime polymorphism at the cost of doing dynamic memory allocation from the library.
The point is that our interface doesn't preclude users from doing something more.
The one concession made to support this flexibility is the method for function dispatch. Rather than access the methods forward declared in the shared header directly by name, they are accessed via a vtable in the XTT context objects.
Implementation
I'll do a mock-up of this approach from the existing crypto lib to see how it looks and feels.