Hey there,

On Tue, 14 Nov 2000, Dr S N Henson wrote:

> > adding support for non-blocking public key operations to SSL handshake
> > requires a lot of changes. first, one must add new states to SSL
> > statemachine for public key operations. then we need a non-blocking
> > interface for public key operations.

There's a bunch of reasons why all of this applies to situations that just
don't involve SSL - anything which tries to do a public key operation
would be just as good a candidate for an async model as SSL is (eg. SSH
implementations built on top of OpenSSL, eg. OpenSSH as it happens :-).

> > right now BIO's are used for all non-blocking data sources (sockets, SSL).
> > i think that public key operations should have a BIO interface too.
> > then we can add third sub-BIO to SSL statemachine: a public key BIO.

hmm ... I find that concept rather disturbing, and I hope it doesn't make
me wake in the middle of the night shaking with fear :-) Seriously though,
all that is required (and I do *not* intend to make this sound trivial) is
some concept of crypto functions being optionally async - ie. the
functions can return with the key operation in an incomplete state. Note,
moving from the word "operations" to "functions" is the point of all this.
The RSA, DSA, DH structures already have their "method"s abstracted so
that the mechanism by which the operations take place is detached from the
key data. The raw key data itself can even reside outside the confines of
the RSA/DSA/DH structure if the method it is using is such that it
understands this. (Ie. hardware key-management - "keys in the box").

The question is how elegantly this could be worked into the present API
without undue disruption ... I'm pessemistic that it could be done
*perfectly* without some fundamental changes, but allow me to think out
loud for a moment. BTW: I'll think out loud only using the RSA case!
<grin>

Brainstormed idea
-----------------

This idea is admittedly bad enough and kludgey enough that I wouldn't want
to code or commit it into OpenSSL, but it should at least illustrate that
it's possible to do this - (a) without completely rewriting the library,
and (b) in such a way as to retain backward-compatibility with old apps
that are (or choose to be) unaware of async support;

We define a new function pointer in RSA_METHOD - something equivalent to
"async_ctrl()" ... anyway, it should be possible for an RSA_METHOD to
indicate to the calling application if it supports an async interface or
not (if the function pointer is NULL that's kind of an implicit - "no, in
case you're interested, I do *not* support anything interesting like
async").

We design the async support by means of an RSA_ctrl() call to change the
future interpretation of each of the RSA_***() key operations that
correspond to RSA_METHOD members; RSA_private_encrypt(),
RSA_public_decrypt(), etc. NB: There's currently no RSA_ctrl() function in
OpenSSL, but that's probably just 'cause noone needed one yet :-)

So, to query if an RSA key can be operated in an async manner, you call
something like;

if(RSA_get_capabilities(rsa_value_ptr, RSA_CAPS_ASYNC))
    /* set some app-specific state to enable use of the async stuff */

RSA_get_capabilities would spot the RSA_CAPS_ASYNC flag and immediately
call the RSA_METHOD's "async_ctrl()" function in the correct form to
determine whether or not async support is present (or just note whether
the function pointer is NULL or not!).

Then to specify async operations from now on, you do;

RSA_ctrl(rsa, RSA_CTRL_ASYNC_ENABLE, ...);
op_id = RSA_private_encrypt(rsa, ....);

Where the RSA_CTRL_ASYNC_START command would fail if the async_ctrl
pointer was NULL, otherwise would call the async_ctrl() function with the
RSA* pointer so it can set the necessary per-key state in a way the
RSA_METHOD itself can later understand and use.

And now the return value "op_id" from RSA_private_encrypt, rather than
indicating result size (or error) as it normally does, in fact would be an
id representing the non-blocking operation that has begun. Then later;

switch((res = RSA_async_query(rsa, op_id))) {
    case RSA_ASYNC_ERROR:
        RSA_async_get_error(rsa, op_id);
        /* ... whatever ... */
    case RSA_ASYNC_INPROGRESS:
        /* Go back to waiting or doing something else */
    default:
        break;
}
/* OK. "res" equals the return value RSA_private_encrypt() would have
 * returned under normal non-async circumstances. Now grab the data. */
RSA_async_get_output(rsa, op_id, ....);

This would be entirely backward compatible with the existing API, would
work as it currently does unless the RSA_METHOD supported async *and* the
caller asked for it, and would allow the "async" mechansim of the
operations to come directly from the RSA_METHOD. Bear in mind that in the
hardware case, a lot of hardware API's *already* support non-blocking
implicitly - it's more difficult sometimes to make them look like a
blocking method because you have to start an operation and then *block*
until the result comes back. So, async is a good idea to try and support -
but insisting that the transport for the "async-ness" is a BIO is *not*
good, especially if it's up in the SSL stuff somewhere rather than in the
RSA_METHOD-specific bits ... if in fact there *IS* a BIO-driven interface
for piping key operations off somewhere, and we want it to be present at
the SSL scope - all that's needed is an RSA_METHOD (and DSA_METHOD,
DH_METHOD, etc) specific to that, and then BIO* pointer could be passed in
using a ctrl() function or whatever from the SSL layer itself (in fact,
they could all go into an ENGINE and then ENGINE_ctrl() could be used to
do it).

In fact, all of this could be done at the RSA/DSA/DH level and tested
without changing the SSL state machine - and only then when it was deemed
acceptable, would it be the time to find the entry points where the SSL
state machine calls the public-key operations and have them support a new
"state" where the public key operation is in-progress ... at that point,
the state machine will escape out - and any further read/write attempts
(on either side) would involve the state-machine querying the pending
operation before choosing whether to advance or not.

> Actually there's already something like this for the X509_LOOKUP stuff
> in the SSL state machine which simply attaches a "special" reason to the
> SSL BIOs or SSL structures. Though AFAIK absolutely nothing uses it.
> 
> What this would involve in practice is some callback for public key
> operations which is passed the relevant SSL structure then it could
> attach some relevant ex_data when it was called. Then whenever the
> public key operation was completed it would retry. The default callback
> would always block as at present.

I think the use of a response callback would be the tail wagging the dog.
If the async public key operation is happening over a socket or some other
"select()"able type of interface, then it would already be possible to
retry the SSL state machine at the point you suspect the public key op is
done (just as we do currently with non-blocking socket IO on the encrypted
side of an SSL). If the public key operation itself wants to be able to
callback into the SSL state machine once it itself receives some
case-specific notification of completion, then the case-specific ex_data
for that key and method could have a pointer to the SSL as you suggested -
but I think having the interface favour simply the start/query approach
would be preferable in most cases ... unless you allow interruption a-la
signals, you'll still have just "leave a note" somewhere in the SSL anyway
hoping it will get picked up - and a fast non-blocking async_query()
operation facilitates that anyway. After all the idea here is to allow an
SSL to just "schedule" its public key ops, and then have the controlling
thread/process get on with other things. Periodic retries of the SSL state
machine would simply leave the SSL as-is, as long as the **_async_query()
response says to, until the operation was complete. If the application
knows where those async operations are happening (eg. over a socket) then
it may be able to more intelligently know when best to retry the SSL. But
having the RSA/DSA/DH_METHOD work in some callback-oriented "event" model
by design would make things worse IMHO.

> This is actually rather difficult (or impossible) to do cross platform.
> You can use select for certain things only and you can have fun with the
> Windows variants as well. How to wait for a public key event to finish
> is likely to be very hardware/software specific and possibly combining
> that with various fd related events is likely to make things harder
> still.

Precisely why I think the async mechansism should be left to the public
key methods, and simply place an optional async interface in front of it
so that calling applications (and I'd include the ssl/ directory's
state-machine logic in this) can choose whether or not to detect it and
use it.

> Currently we take the easy way out and don't even try any of this and
> leave the application to decide what to do.

:-) That's a very good place to start :-)

Cheers,
Geoff




______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
Development Mailing List                       [EMAIL PROTECTED]
Automated List Manager                           [EMAIL PROTECTED]

Reply via email to