Engine building lesson 1: A minimum useless engine
In this lesson, we’re going to explore minimalism, in this case in the form of the most minimal engine possible (without obfuscating it).
The least boilerplate code for an engine looks like this:
#include <openssl/engine.h>
IMPLEMENT_DYNAMIC_BIND_FN(bind)
IMPLEMENT_DYNAMIC_CHECK_FN()
This example isn’t complete, it will not compile. However, it contains the absolute minimum required for those module to even be recognised as an OpenSSL engine.
When an engine is being loaded, the OpenSSL library will perform
certain checks to see that the engine is compatible with this OpenSSL
version, and finish up by calling the function whose name is given to
IMPLEMENT_DYNAMIC_BIND_FN
(bind
in our case).
So, to complete the engine a bit more, we need to add the function
bind
, which should do all the final steps to tie this engine’s
functionality with the calling OpenSSL.
#include <openssl/engine.h>
static int bind(ENGINE *e, const char *id)
{
return 1;
}
IMPLEMENT_DYNAMIC_BIND_FN(bind)
IMPLEMENT_DYNAMIC_CHECK_FN()
This engine will simply load. Period.
At this point, we can build it. Let’s pretend we call it
silly-engine.c
and we’re running on Linux with some version of
OpenSSL installed. Then, building is as simple as this:
$ gcc -fPIC -o silly-engine.o -c silly-engine.c
$ gcc -shared -o silly-engine.so -lcrypto silly-engine.o
Let’s try it out! Something to keep in mind is that if OpenSSL is
just given an engine name, such as silly-engine
, it will use
platform specific library naming conventions to find the actual
shareable name. So if given silly-engine
as an engine name on
Linux, it will try to find libsilly-engine.so
, and that’s not at all
what we’ve produced.
It is, however, also possible to give OpenSSL an absolute path for the engine, that’s what we’ll try out now.
$ openssl engine -t -c `pwd`/silly-engine.so
Segmentation fault
Uh-oh…
This happens to be a bug in OpenSSL, it should really report that the
engine hasn’t self identified with an identity string and a longer
name (can be used as a one line description).
[fix coming up]
So, let’s revisit the code one last time, and add some code in
bind()
#include <stdio.h>
#include <openssl/engine.h>
static const char *engine_id = "silly";
static const char *engine_name = "A silly engine for demonstration purposes";
static int bind(ENGINE *e, const char *id)
{
int ret = 0;
if (!ENGINE_set_id(e, engine_id)) {
fprintf(stderr, "ENGINE_set_id failed\n");
goto end;
}
if (!ENGINE_set_name(e, engine_name)) {
printf("ENGINE_set_name failed\n");
goto end;
}
ret = 1;
end:
return ret;
}
IMPLEMENT_DYNAMIC_BIND_FN(bind)
IMPLEMENT_DYNAMIC_CHECK_FN()
Rebuilding and running gives us some nice output
$ gcc -fPIC -o silly-engine.o -c silly-engine.c
$ gcc -shared -o silly-engine.so -lcrypto silly-engine.o
$ openssl engine -t -c `pwd`/silly-engine.so
(/home/levitte/tmp/silly-engine/silly-engine.so) A silly engine for demonstration purposes
Loaded: (silly) A silly engine for demonstration purposes
[ available ]
And voilà! A first engine, workable but entirely silly and useless.
If you’re interested, there’s a slightly more elaborated variant of this engine with configuration script and Makefile suitable for GNU auto* tools, libtool and automake for those who want to study that kind of setup closer.
Next lesson will be about adding a digest implementation.