9. The Provider ABI as an Engineering Contract

A provider implementation is a binary extension whose correctness depends on agreement between independently compiled components. The public headers describe this agreement with types, identifiers, structures, and callback signatures. Reading the interface as a contract is more productive than treating it as a collection of symbols that merely need to compile. Every callback has inputs, outputs, ownership implications, and assumptions about when it is invoked.

9.1 From exported symbol to operation

The first boundary is the module entry point. The loader must find the exported name and call it using the expected signature. The entry point then returns a provider context and a provider dispatch table. A later query requests an operation class and obtains an algorithm table. An algorithm entry identifies a further dispatch table for that operation. These are separate transitions. A defect in an early transition can prevent a later callback from being reached at all.

Exported initialization symbolProvider context and provider dispatchOperation query and algorithm advertisementDigest callbacks and per-operation state
Figure 2. Four reviewable transitions in the teaching module. Each transition can fail independently.

The practical implication is that diagnostic output should correspond to the transition under investigation. A missing entry point is a loader/interface issue. An unsupported operation is an advertisement issue. A property mismatch is a selection issue. A wrong digest length is an execution or metadata issue. Treating all of them as “OpenSSL cannot find my algorithm” obscures the relevant evidence and encourages accidental fixes.

9.2 Dispatch-table structure

The source uses static arrays terminated by a zero identifier and a null function pointer. Static lifetime is important because the core may retain the returned description beyond the query call. Returning a pointer to an automatic local array would produce a lifetime defect even if the array appeared correct during initialization. In the prototype, no dynamic algorithm advertisement is needed, so static data also reduces the number of allocation and synchronization decisions.

The casts to a generic function-pointer type can look unusual to a reader new to this API. They are part of the dispatch representation; the operation-specific function type determines how the pointer is called. Review should therefore check the identifier and the actual implementation signature together. A correctly spelled callback name is not enough if it is paired with the wrong function identifier. The public core-dispatch declarations provide the reference for that comparison. [9]

9.3 Supported and unsupported operations

The provider advertises only a digest operation. It should not return its digest table when asked for a cipher, signature, or other operation class. The callback harness makes this distinction observable by requesting a cipher and expecting a null result. It then requests a digest and checks the algorithm name and property string. These checks test the provider’s own advertisement logic without relying on how the command-line tool formats an algorithm listing.

An unsupported operation is not necessarily an error in a small provider. Providers may have a limited scope. The engineering defect would be to advertise support that the module cannot fulfill, or to let unrelated callbacks be interpreted as the requested operation. The study therefore values narrow, accurate advertisement over a broad list of untested capabilities.

9.4 Algorithm names and identity

The name EDU-SHA256 communicates a teaching-specific operation identity while retaining a recognizable relationship to SHA-256. It prevents the example from silently competing with the standard name in an unqualified application request. That design choice makes experiments easier to interpret, although a real replacement provider may intentionally advertise standard names. Such a replacement would need a more careful property and deployment policy because a familiar algorithm name would no longer identify one implementation by itself.

The property definition and the provider handle are also distinct concepts. The prototype makes them consistent, but the paper does not assume that a property string is cryptographically bound to the module’s identity. The programmatic client checks the fetched method’s provider object. That observation is useful for diagnosing selection within the OpenSSL process. It is not remote attestation and does not establish the integrity of the module file.

9.5 Parameter descriptors as schema

A gettable-parameter callback acts like a small schema: it describes the names and types the implementation understands. The corresponding get-parameters callback performs the exchange. Correctness involves both the value and its representation. A digest size of 32 stored using an incompatible type is not a valid response merely because a debugger can find the number 32 somewhere in memory. Typed setters help keep the implementation aligned with the requested representation. [10]

The extension tests this principle by asking for the size with a UTF-8-string parameter rather than an integer size parameter. The prototype’s setter rejects the incompatible request. It also receives an unknown parameter name and leaves the caller’s storage unchanged. That latter test verifies the behavior of this implementation for an unrecognized request; it should not be generalized into a rule that every arbitrary malformed parameter list must be accepted.

9.6 Binary compatibility and deployment

Compilation against public headers is an important first step, but it is not a complete compatibility claim. The host loader, the module’s dependencies, and the runtime OpenSSL library still have to agree. The build script records an ordinary Linux shared-object workflow and links against the installed library. It does not attempt to construct a portable binary for unrelated distributions or architectures.

A deployable provider should specify its supported OpenSSL versions, target platforms, build settings, and dependency assumptions. It should also test installation layouts in which the module and its dependencies are found through the intended paths. This paper avoids guessing about compatibility that has not been exercised. The strongest binary claim supported here is that the module built and ran on the recorded OpenSSL 3.5.5 host.