[swift-server-dev] Next Server Security API Group Meeting

Helge Heß me at helgehess.eu
Mon Jan 30 03:59:43 CST 2017


> On 30 Jan 2017, at 08:45, Jean-Daniel via swift-server-dev <swift-server-dev at swift.org> wrote:
>> Le 29 janv. 2017 à 15:33, Helge Heß via swift-server-dev <swift-server-dev at swift.org> a écrit :
>> Hi,
>> a major thing the proposal is missing is a strong requirement for non blocking I/O (for the server side effort). That should be builtin from the start.
>> This seems possible with OpenSSL, but AFAIK not with CommonCrypto. Which I don’t think is a big deal, because NIO could probably be emulated using threads on macOS. The latter is a dev platform anyways and it should be fine if it doesn’t scale that well …
> What do you mean by « NIO is not possible with CommonCrypto ». CommonCryto has no IO API AFAIK. This is just a low level cryptographic library that provide a couple of digest and encryption algorithms. On macOS, the recommended low-level way to do SSL is using the SecurityFramework.

Obviously that's what I meant, sorry for not being precise ;->

> And here again, the API is not responsible of the IO handling. So there is not constraint about what you use for IO.

I’m not entirely sure what you mean by that. Obviously a TLS library is absolutely responsible for Input/Output - in fact that is its whole purpose :-) Reading and writing data as well as connecting and accepting sockets (doing the TLS handshakes).
(Yes, I know, you refer to the ‘raw’ I/O to the kernel, but that is not necessarily the Posix API, it could also be libuv or libdispatch being hooked up)

AFAIK all libraries have the facility to stack another I/O library below them by the means of providing lower level read/write/connect/accept C function callbacks. (OpenSSL in a very flexible way using their BIOs).

However, I thought - and I may be very wrong here, which would be excellent - that SecurityFramework only supported blocking read and write callbacks and cannot be ‘paused’ until new data arrives.

Let me illustrate what I mean. Lets say we are doing the TLS handshake after accepting a socket. This involves the TLS library reading data from the TLS client. The TLS library may want to read a 4K block of data. So the usual blocking API goes like that (pseudo code):

  let rc = do_tls_handshake(tls_socket)
    Internal read 4K
      1) Calls read() -> returns say 512 bytes
      2) Calls read() -> returns say 123 bytes
      n) Calls read() -> returns say 321 bytes
    Validate, return, done

Now the ‘requirement’ for a server TLS library used on a production system (IMHO) is that a do_tls_handshake function (and any other I/O it triggers) can deal with any read() returning EWOULDBLOCK. It would need to record where it was and be able to resume doing the handshake when more data comes in.

There are many models on how the API for the NIO would look like (in Node/Noze it would just record the closures), but the basic flow is something like this:

  let rc = do_tls_handshake(tls_socket)
    Internal read 4K
      1) Calls read() -> returns say 512 bytes
      2) Calls read() -> returns EWOULDBLOCK
         do_tls_handshake returns EWOULDBLOCK

  if rc == EWOULDBLOCK {
    Wait-for-data-on-socket (kqueue …).onData {
      let rc = do_tls_handshake(tls_socket) // continue handshake
        Internal read 4K
          2) Calls read() -> returns 123
          3) Calls read() -> returns 321
          4) Calls read() -> returns EWOULDBLOCK
          do_tls_handshake returns EWOULDBLOCK

If SecurityFramework can deal with that, all is cool :-)

Regardless the API to the Swift TLS library should be so, that its I/O operations can run in a non-blocking fashion. Just imagine you want to write something like nginx in Swift.


More information about the swift-server-dev mailing list