Draft proposal for TLS Service APIs (please review)


(Gelareh Taban) #1

Hello all,

I would like to share the following API proposal draft for Server security.
Once enough feedback has been received, I will initiate a PR for an
official review period.

A more readable version of the proposal can be found at:
https://github.com/gtaban/blogs/blob/master/TLSService_API_Proposal.md

Regards,
Gelareh

···

------------------------------------------

Proposal: SS-001 (TLS Service APIs)
Authors: Gelareh Taban, Bill Abt
Review Manager: TBD
Status: Awaiting review

1 - Introduction

This proposal presents the design of TLS service, which is a collection of
Swift APIs that provide SSL/TLS functionality with a consistent API surface
for both Apple and Linux platforms.

For the purposes of brevity, we use the term TLS to refer to all SSL and
TLS related functionality in the remainder of the document.

2 - Motivation

Currently there is no standard set of Swift TLS APIs on Apple and Linux
platforms which has resulted in projects implementing their own Swift
security functionality or using their security library of choice (be it
OpenSSL, LibreSSL, etc.). This has resulted in fragmentation of the space
as well as incompatibility of project dependencies if more than one
security package is needed by different modules.

This motivates the necessity for defining a standard set of protocols that
define the behavior of the TLS service and how the application and the
server and networking layers beneath it interact with the TLS service.

3 - Proposed solution

We propose a new model that is easily extensible and supports a plug-n-play
architecture which allows an application to choose the underlying security
framework of choice.

https://github.com/gtaban/blogs/blob/master/TLSServiceArchitecture.png

3.1 - Assumptions

At the time of writing this proposal, the Swift Server API work group has
not yet defined any standards for the transport management or application
layers. For our design, we have assumed a network stack that consists of:

- System transport layer
- Transport management
- HTTP request management
- (Optional) Web server
- Application

Our security stack is composed of:
- Underlying security framework (SecureTransport library in
Security.Framework on Apple and OpenSSL or alternatives on Linux)
- TLS service

In our model diagram (
https://github.com/gtaban/blogs/blob/master/TLSServiceArchitecture.png) we
omit both the system level transport layer as well as the underlying
security library and show the relationship between the TLS service protocol
and the various application layers.

3.2 - Design description

TLS service protocol defines a set of methods that is implemented by TLS
service and it interfaces with the transport management layer (for example,
socket management). These methods are implemented by the TLS service which
in turn uses its choice an of underlying security library. As an example,
the TLS service uses SecurityTransport library on Apple platform and
OpenSSL or alternatively LibreSSL on Linux.

If an application requires TLS for its use case, it creates a TLS service
object and configures it based on its requirements. Note that the
configuration of the TLS service is outside the scope of the current
document.

The application then passes the TLS service object to its lower level
frameworks that deal with networking and communication. Each lower level
framework maintains an optional instance variable of type TLS service
protocol. If the optional variable exists, it is further passed down until
it gets to the lowest level that deals with the Swift transport layer APIs
(in the diagram above, this is the HTTP Management layer). When this layer
creates the connection using the transport layer APIs, it assigns the TLS
service object to the transport layer delegate. The Swift socket layer is
then responsible for calling the TLS service protocol methods that handle
the TLS functionality at the appropriate times.

Note that in the diagram above, we abstract away the transport mechanism
using transport management protocol. The definition for this protocol is
out of the scope of this proposal and will be discussed in a separate,
future proposal.

3.3. - Benefits

There are several benefits provided by this model:

1- It allows the application to choose the underlying security library of
choice for their TLS by passing in the appropriate TLS service object.

  As long as the TLS service object conforms to the specification of
the TLS service protocol, it can implement its methods using any underlying
security library. The application can then create the TLS service object of
its choice and simply pass the object to the appropriate methods that
invoke transport-level functionality.

  This model is specially important for projects that involve multiple
dependencies that use conflicting security libraries (e.g., LibreSSL and
OpenSSL which share the same API surface and building a project with both
of these libraries results in link errors). Now the application can choose
its own TLS service object and pass it to all frameworks that require TLS.

2- Application do not have dependency on underlying security framework if
TLS is not enabled.

  By mandating Transport/HTTP Management layers to be dependent only on
the TLS service protocol, they are agnostic of the underlying security
framework and hence do not need to have them in the project if they are not
used.

3- It allows users to use the same TLS library for both client and server
applications.

  This is especially important for many server side applications which
need to behave as both clients and servers depending on external services
they connect to. Also by decoupling the TLS service client and server
objects, debugging an application with multiple types of connections will
be simplified.

4- The underlying Transport/HTTP Management layers are agnostic of the
underlying security functionality and simply hand off the TLS service
object to the Transport layer delegate.

5- The underlying Transport layer is responsible for supporting blocking
and non-blocking connections as long as the underlying security library
supports both types of connection.

3.4 - A note on creating a Swifty TLS service

There are a number of cases in low-level systems programming where similar
functionality is differentiated by a flag or type, such as SOCK_STREAM vs.
SOCK_DGRAM or SSLv23_server_method vs. SSLv23_client_method.

The latter is an example of how OpenSSL differentiates between the server
side or the client side of a TLS connection. In TLS, the client is the
initiator of a TLS session; the server is the entity that accepts the
requests for TLS sessions made by clients. Depending on the connection type
(server or client), different actions and policies are supported such as
cipher suite selection during a TLS handshake, request for handshake
renegotiations and trust validation policies for server name and
certificates.

Whilst using a flag or a type is convenient in languages such as C, it's
not very Swift-like and does not adhere to the single responsibility
principle [1] of object oriented paradigm. We recommend that different
interfaces should be created that decouple the client and server concepts
as far as the higher application layers are concerned. This is generalized
approach that can be adopted by the other layers of the application stack.
For example, the Transport layer can also decouple the interface of client
and server connections.

4 - Detailed design

4.1 - TLS service protocol

The TLS service protocol describes the methods that the transport layer
calls to handle transport-level events for the TLS service object.

- onClientCreate

This will be called when a client I/O connection is created and appropriate
TLS connection needs to be configured, including context creation, the
handshake and connection verification.

This is a client only method.

  ///
  /// Setup the contexts and process the TLSService configurations
(certificates, etc)
  ///

  func onClientCreate() throws

- onServerCreate

This will be called when a server I/O connection is created and appropriate
TLS connection needs to be setup, including context creation, the handshake
and connection verification.

This is a server only method.

  ///
  /// Setup the contexts and process the TLSService configurations
(certificates, etc)
  ///

  func onServerCreate() throws

- onDestroy

This will be called when an I/O instance connection is closed and any
remaining TLS context needs to be destroyed.

This is both a client and server method.

  ///
  /// Destroy any remaining contexts
  ///
  func onDestroy()

- onAccept

This will be called once an I/O instance connection has been accepted, to
setup the TLS connection, do the handshake and connection verification.

This is both a client and server method.

  ///
  /// Processing on acceptance from a listening connection
  ///
  ///
  /// - Parameter IORef: The connected I/O instance
  ///
  func onAccept(IORef: TransportManagementDelegate) throws

- onConnect

This will be called once a socket connection has been made, to setup the
TLS connection, do the handshake and connection verification.

This is both a client and server method.

  ///
  /// Processing on connection to a listening connection
   ///
  /// - Parameter connectionRef: The connected I/O instance
  ///
  func onConnect(IORef: TransportManagementDelegate) throws

- onSend

This will be called when data is to be written to an I/O instance. The
input data buffer is written to the TLS connection associated with that I/O
instance.

This is both a client and server method.

  ///
  /// Low level writer
  ///
  /// - Parameters:
  /// - buffer: Buffer pointer
  /// - bufSize: Size of the buffer
  ///
  /// - Returns the number of bytes written. Zero indicates TLS
shutdown, less than zero indicates error.
  ///
  func onSend(buffer: UnsafeRawPointer, bufSize: Int) throws -> Int

- onReceive

This will be called when data is to be read from an I/O instance. Encrypted
data is read from TLS connection associated with that I/O instance and
decrypted and written to the buffer passed in.

This is both a client and server method.

  ///
  /// Low level reader
  ///
  /// - Parameters:
  /// - buffer: Buffer pointer
  /// - bufSize: Size of the buffer
  ///
  /// - Returns the number of bytes read. Zero indicates TLS shutdown,
less than zero indicates error.
  ///
  func onReceive(buffer: UnsafeMutableRawPointer, bufSize: Int) throws
-> Int

5 - Non-goals

This proposal:

1- DOES NOT describe the TLS service configuration, which includes
information on certificate types, formats and chains, cipher suites, etc.
We expect this to be specified in a future proposal.

2- DOES NOT describe the TLS service trust policies, which define trust and
validation policies of the incoming connection. We expect this to be
specified in a future proposal.

3- DOES NOT describe the interface between the TLS service and the
transport layer and any dependencies. We expect this to be specified in a
future proposal.

6 - Source compatibility

The proposed change is designed to be as non-breaking as possible, however
it does place several assumptions on the rest of the transport/application
stack.

- The application layer must import and instantiate a TLS service object
which implements the TLS service protocol if it wants to enable TLS
service.

- Every framework layer above the transport management layer but below the
application layer (which includes HTTP and server frameworks) has an
optional object that implements the TLS service protocol. If the TLS object
exists, it is passed down to each layer below.

- The HTTP layer which sets up the transport management layer assigns the
TLS object to the transport management delegate if the object exists.

- The transport management layer which sets up the I/O communication
implements the transport management protocol.

- The transport management layer which sets up the I/O communication and
deals with the low level C system I/O, calls the appropriate TLS service
protocol methods whenever I/O data needs to be secured.

- The long term goal for the location of the TLS service protocol is within
the Foundation framework. In the short term, the protocol can live within
the transport management framework.

7 - References

1 -
https://web.archive.org/web/20150202200348/http://www.objectmentor.com/resources/articles/srp.pdf


#2

Hi Gelareh,

I haven’t attended a single security API meeting yet but just curious if it is possible to make the api support vector write or write from swift Collection protocol? Like the followings:
func tlsWrite(buffer: Collection<UInt8>) throws
func tlsWritev(vectors: Collection<iovec>)

If it is possible it will be very helpful since
1) it is very swifty and user friendly
2) vectorIO could significantly reduce the overhead to merge fragments of bytes into a single buffer.

Michael.

···

On Mar 20, 2017, at 1:04 PM, Gelareh Taban via swift-server-dev <swift-server-dev@swift.org <mailto:swift-server-dev@swift.org>> wrote:

Hello all,

I would like to share the following API proposal draft for Server security. Once enough feedback has been received, I will initiate a PR for an official review period.

A more readable version of the proposal can be found at: https://github.com/gtaban/blogs/blob/master/TLSService_API_Proposal.md

Regards,
Gelareh

------------------------------------------

Proposal: SS-001 (TLS Service APIs)
Authors: Gelareh Taban, Bill Abt
Review Manager: TBD
Status: Awaiting review

1 - Introduction

This proposal presents the design of TLS service, which is a collection of Swift APIs that provide SSL/TLS functionality with a consistent API surface for both Apple and Linux platforms.

For the purposes of brevity, we use the term TLS to refer to all SSL and TLS related functionality in the remainder of the document.

2 - Motivation

Currently there is no standard set of Swift TLS APIs on Apple and Linux platforms which has resulted in projects implementing their own Swift security functionality or using their security library of choice (be it OpenSSL, LibreSSL, etc.). This has resulted in fragmentation of the space as well as incompatibility of project dependencies if more than one security package is needed by different modules.

This motivates the necessity for defining a standard set of protocols that define the behavior of the TLS service and how the application and the server and networking layers beneath it interact with the TLS service.

3 - Proposed solution

We propose a new model that is easily extensible and supports a plug-n-play architecture which allows an application to choose the underlying security framework of choice.

https://github.com/gtaban/blogs/blob/master/TLSServiceArchitecture.png

3.1 - Assumptions

At the time of writing this proposal, the Swift Server API work group has not yet defined any standards for the transport management or application layers. For our design, we have assumed a network stack that consists of:

- System transport layer
- Transport management
- HTTP request management
- (Optional) Web server
- Application

Our security stack is composed of:
- Underlying security framework (SecureTransport library in Security.Framework on Apple and OpenSSL or alternatives on Linux)
- TLS service

In our model diagram (https://github.com/gtaban/blogs/blob/master/TLSServiceArchitecture.png) we omit both the system level transport layer as well as the underlying security library and show the relationship between the TLS service protocol and the various application layers.

3.2 - Design description

TLS service protocol defines a set of methods that is implemented by TLS service and it interfaces with the transport management layer (for example, socket management). These methods are implemented by the TLS service which in turn uses its choice an of underlying security library. As an example, the TLS service uses SecurityTransport library on Apple platform and OpenSSL or alternatively LibreSSL on Linux.

If an application requires TLS for its use case, it creates a TLS service object and configures it based on its requirements. Note that the configuration of the TLS service is outside the scope of the current document.

The application then passes the TLS service object to its lower level frameworks that deal with networking and communication. Each lower level framework maintains an optional instance variable of type TLS service protocol. If the optional variable exists, it is further passed down until it gets to the lowest level that deals with the Swift transport layer APIs (in the diagram above, this is the HTTP Management layer). When this layer creates the connection using the transport layer APIs, it assigns the TLS service object to the transport layer delegate. The Swift socket layer is then responsible for calling the TLS service protocol methods that handle the TLS functionality at the appropriate times.

Note that in the diagram above, we abstract away the transport mechanism using transport management protocol. The definition for this protocol is out of the scope of this proposal and will be discussed in a separate, future proposal.

3.3. - Benefits

There are several benefits provided by this model:

1- It allows the application to choose the underlying security library of choice for their TLS by passing in the appropriate TLS service object.

As long as the TLS service object conforms to the specification of the TLS service protocol, it can implement its methods using any underlying security library. The application can then create the TLS service object of its choice and simply pass the object to the appropriate methods that invoke transport-level functionality.

This model is specially important for projects that involve multiple dependencies that use conflicting security libraries (e.g., LibreSSL and OpenSSL which share the same API surface and building a project with both of these libraries results in link errors). Now the application can choose its own TLS service object and pass it to all frameworks that require TLS.

2- Application do not have dependency on underlying security framework if TLS is not enabled.

By mandating Transport/HTTP Management layers to be dependent only on the TLS service protocol, they are agnostic of the underlying security framework and hence do not need to have them in the project if they are not used.

3- It allows users to use the same TLS library for both client and server applications.

This is especially important for many server side applications which need to behave as both clients and servers depending on external services they connect to. Also by decoupling the TLS service client and server objects, debugging an application with multiple types of connections will be simplified.

4- The underlying Transport/HTTP Management layers are agnostic of the underlying security functionality and simply hand off the TLS service object to the Transport layer delegate.

5- The underlying Transport layer is responsible for supporting blocking and non-blocking connections as long as the underlying security library supports both types of connection.

3.4 - A note on creating a Swifty TLS service

There are a number of cases in low-level systems programming where similar functionality is differentiated by a flag or type, such as SOCK_STREAM vs. SOCK_DGRAM or SSLv23_server_method vs. SSLv23_client_method.

The latter is an example of how OpenSSL differentiates between the server side or the client side of a TLS connection. In TLS, the client is the initiator of a TLS session; the server is the entity that accepts the requests for TLS sessions made by clients. Depending on the connection type (server or client), different actions and policies are supported such as cipher suite selection during a TLS handshake, request for handshake renegotiations and trust validation policies for server name and certificates.

Whilst using a flag or a type is convenient in languages such as C, it's not very Swift-like and does not adhere to the single responsibility principle [1] of object oriented paradigm. We recommend that different interfaces should be created that decouple the client and server concepts as far as the higher application layers are concerned. This is generalized approach that can be adopted by the other layers of the application stack. For example, the Transport layer can also decouple the interface of client and server connections.

4 - Detailed design

4.1 - TLS service protocol

The TLS service protocol describes the methods that the transport layer calls to handle transport-level events for the TLS service object.

- onClientCreate

This will be called when a client I/O connection is created and appropriate TLS connection needs to be configured, including context creation, the handshake and connection verification.

This is a client only method.

///
/// Setup the contexts and process the TLSService configurations (certificates, etc)
///

func onClientCreate() throws

- onServerCreate

This will be called when a server I/O connection is created and appropriate TLS connection needs to be setup, including context creation, the handshake and connection verification.

This is a server only method.

///
/// Setup the contexts and process the TLSService configurations (certificates, etc)
///

func onServerCreate() throws

- onDestroy

This will be called when an I/O instance connection is closed and any remaining TLS context needs to be destroyed.

This is both a client and server method.

///
/// Destroy any remaining contexts
///
func onDestroy()

- onAccept

This will be called once an I/O instance connection has been accepted, to setup the TLS connection, do the handshake and connection verification.

This is both a client and server method.

///
/// Processing on acceptance from a listening connection
///
///
/// - Parameter IORef: The connected I/O instance
///
func onAccept(IORef: TransportManagementDelegate) throws

- onConnect

This will be called once a socket connection has been made, to setup the TLS connection, do the handshake and connection verification.

This is both a client and server method.

///
/// Processing on connection to a listening connection
///
/// - Parameter connectionRef: The connected I/O instance
///
func onConnect(IORef: TransportManagementDelegate) throws

- onSend

This will be called when data is to be written to an I/O instance. The input data buffer is written to the TLS connection associated with that I/O instance.

This is both a client and server method.

///
/// Low level writer
///
/// - Parameters:
/// - buffer: Buffer pointer
/// - bufSize: Size of the buffer
///
/// - Returns the number of bytes written. Zero indicates TLS shutdown, less than zero indicates error.
///
func onSend(buffer: UnsafeRawPointer, bufSize: Int) throws -> Int

- onReceive

This will be called when data is to be read from an I/O instance. Encrypted data is read from TLS connection associated with that I/O instance and decrypted and written to the buffer passed in.

This is both a client and server method.

///
/// Low level reader
///
/// - Parameters:
/// - buffer: Buffer pointer
/// - bufSize: Size of the buffer
///
/// - Returns the number of bytes read. Zero indicates TLS shutdown, less than zero indicates error.
///
func onReceive(buffer: UnsafeMutableRawPointer, bufSize: Int) throws -> Int

5 - Non-goals

This proposal:

1- DOES NOT describe the TLS service configuration, which includes information on certificate types, formats and chains, cipher suites, etc. We expect this to be specified in a future proposal.

2- DOES NOT describe the TLS service trust policies, which define trust and validation policies of the incoming connection. We expect this to be specified in a future proposal.

3- DOES NOT describe the interface between the TLS service and the transport layer and any dependencies. We expect this to be specified in a future proposal.

6 - Source compatibility

The proposed change is designed to be as non-breaking as possible, however it does place several assumptions on the rest of the transport/application stack.

- The application layer must import and instantiate a TLS service object which implements the TLS service protocol if it wants to enable TLS service.

- Every framework layer above the transport management layer but below the application layer (which includes HTTP and server frameworks) has an optional object that implements the TLS service protocol. If the TLS object exists, it is passed down to each layer below.

- The HTTP layer which sets up the transport management layer assigns the TLS object to the transport management delegate if the object exists.

- The transport management layer which sets up the I/O communication implements the transport management protocol.

- The transport management layer which sets up the I/O communication and deals with the low level C system I/O, calls the appropriate TLS service protocol methods whenever I/O data needs to be secured.

- The long term goal for the location of the TLS service protocol is within the Foundation framework. In the short term, the protocol can live within the transport management framework.

7 - References

1 - https://web.archive.org/web/20150202200348/http://www.objectmentor.com/resources/articles/srp.pdf

_______________________________________________
swift-server-dev mailing list
swift-server-dev@swift.org <mailto:swift-server-dev@swift.org>
https://lists.swift.org/mailman/listinfo/swift-server-dev


(Brent Royal-Gordon) #3

4.1 - TLS service protocol

The TLS service protocol describes the methods that the transport layer calls to handle transport-level events for the TLS service object.

I have a bunch of questions about the design you're presenting, and I think many of them ultimately stem from not understanding some of the high-level aspects of the proposal. For instance:

* What types conform to this protocol? From the diagram, it looks like there's a type for each "engine"—a SecureTransportService, an OpenSSLService, etc.—and each instance represents a particular configuration of that engine. So you create a WhateverTLSService, configure it, and then hand it off (through several layers) to the transport management layer, which calls methods on it to handle various events. The transport management layer then uses that one TLSService to handle many connections. Is that correct?

* What is the lifecycle of a connection? Does the TLSService create them itself, does the transport management layer create them and hand them off, or does the transport management layer retain control over them from beginning to end?

* You discuss the higher layers creating a TLSService object and caching it in a property, then ultimately handing it down to the transport management layer, which then attaches it to socket objects. But presumably you can have many socket objects, possibly simultaneously. Are they all served by a single TLSService instance, or by many? If they share a TLSService, how does the TLSService know which socket is talking to it at a given moment? If they have separate ones, how does the transport management layer acquire a new one when it needs it?

You mention that this proposal is very small in scope, and it's fine to describe some of these details in general ways. For instance, you don't need to describe the interface to a Swift socket or the transport layer in detail. But currently, the description of these surrounding systems is *so* vague that I'm struggling to assess this design.

Perhaps some of these details have been described in other documents or meetings; if so, they really need to be presented in this document, too.

- onClientCreate

Why do these methods all have "on" prefixes? I'm not totally sure I understand the intended usage here, but I see two possibilities:

* These are imperative commands. `onAccept` says that the TLS engine should accept a connection, `onSend` means it should send some data, etc. In that case, these should not have any prefix—they should just be `accept`, `send`, etc.

* These are essentially delegate methods notifying the TLS engine of an event. `onAccept` says that the system has accepted a connection and the TLS engine should do what it needs to do with it, `onSend` means the system is about to send data and it needs the TLS engine to modify it, etc. If so, Swift APIs more often use words like `should`, `will`, or `did` than `on`, particularly since they're more precise about the timing of the delegate method compared to the actual event.

In either case, I don't think "on" is the best naming for these. It needlessly bucks platform conventions.

This will be called when a client I/O connection is created and appropriate TLS connection needs to be configured, including context creation, the handshake and connection verification.

This is a client only method.

///
/// Setup the contexts and process the TLSService configurations (certificates, etc)
///

func onClientCreate() throws

- onServerCreate

This will be called when a server I/O connection is created and appropriate TLS connection needs to be setup, including context creation, the handshake and connection verification.

This is a server only method.

///
/// Setup the contexts and process the TLSService configurations (certificates, etc)
///

func onServerCreate() throws

What are these methods supposed to do, exactly?

* Do they put `self` into either a client state or a server state? If so, what happens if you call both, or neither, or call one twice? Would it be better to do this as part of initialization, or to have them make a client TLS object or server TLS object, or to require whatever code hands the TLSService to the TransportManager to pre-configure it as either client or server?

* Do they create a new instance that's either a client or a server? If so, how do they return it?

* Do they configure something recently created as either client or server? If so, how do they access whatever they need to configure?

Basically, what state are these supposed to operate upon?

- onDestroy

This will be called when an I/O instance connection is closed and any remaining TLS context needs to be destroyed.

This is both a client and server method.

///
/// Destroy any remaining contexts
///
func onDestroy()

Is this called at the end of each connection, or is it called once when the transport management layer is totally finished with the TLSService, or are these the same thing?

If per-connection, how does it know which connection?

If during destruction, should we just class-constrain and use `deinit` for this purpose?

- onAccept

This will be called once an I/O instance connection has been accepted, to setup the TLS connection, do the handshake and connection verification.

This is both a client and server method.

///
/// Processing on acceptance from a listening connection
///
///
/// - Parameter IORef: The connected I/O instance
///
func onAccept(IORef: TransportManagementDelegate) throws

I take it the parameter is some sort of abstraction wrapping e.g. a socket. So why is it called a `TransportManagementDelegate`? Shouldn't its name include words like `Connection` or `Socket` or `IOHandle` or something?

Do we want the parameter to be labeled `IORef`? That's not very idiomatic; it doesn't read well or follow the Swift naming guidelines.

You say this is for both clients and servers. When does a TLS client have a listening connection that it `accept`s connections on?

Is it called at different times or in different ways than `onServerCreate`?

- onConnect

This will be called once a socket connection has been made, to setup the TLS connection, do the handshake and connection verification.

This is both a client and server method.

///
/// Processing on connection to a listening connection
///
/// - Parameter connectionRef: The connected I/O instance
///
func onConnect(IORef: TransportManagementDelegate) throws

The same as above, with appropriate substitutions.

- onSend

This will be called when data is to be written to an I/O instance. The input data buffer is written to the TLS connection associated with that I/O instance.

This is both a client and server method.

///
/// Low level writer
///
/// - Parameters:
/// - buffer: Buffer pointer
/// - bufSize: Size of the buffer
///
/// - Returns the number of bytes written. Zero indicates TLS shutdown, less than zero indicates error.
///
func onSend(buffer: UnsafeRawPointer, bufSize: Int) throws -> Int

Is there a reason you use an UnsafeRawPointer and a buffer size, instead of using an UnsafeRawBufferPointer which would encapsulate both?

Why is shutdown indicated with zero, rather than the return value being Optional and being nil? Why are errors signaled with negative values instead of being thrown? (Or are you saying that negative returns are invalid? That's different from saying "indicates error".)

If a TLSService return less than `bufSize`, will the enclosing later try to.re-send the remaining data in subsequent calls?

This sounds like the TLS engine owns the network connection (at least by this point) and is responsible for writing to it. Does that mean `accept` and `connect` take ownership of the connection and hold on to it? If you have several different simultaneous connections, how do you know which connection this should write to? Or does a given TLSService only own one connection at a time? If so, does the transport management layer create a new TLSService instance for each connection? How? If each TLSService is bound to one connection, shouldn't it be created already knowing the connection it's going to use?

- onReceive

This will be called when data is to be read from an I/O instance. Encrypted data is read from TLS connection associated with that I/O instance and decrypted and written to the buffer passed in.

This is both a client and server method.

///
/// Low level reader
///
/// - Parameters:
/// - buffer: Buffer pointer
/// - bufSize: Size of the buffer
///
/// - Returns the number of bytes read. Zero indicates TLS shutdown, less than zero indicates error.
///
func onReceive(buffer: UnsafeMutableRawPointer, bufSize: Int) throws -> Int

If I understand correctly, `buffer` is an uninitialized memory region that the type should fill with data. Is that correct?

Otherwise, the same as above, with appropriate substitutions.

5 - Non-goals

This proposal:

1- DOES NOT describe the TLS service configuration, which includes information on certificate types, formats and chains, cipher suites, etc. We expect this to be specified in a future proposal.

2- DOES NOT describe the TLS service trust policies, which define trust and validation policies of the incoming connection. We expect this to be specified in a future proposal.

3- DOES NOT describe the interface between the TLS service and the transport layer and any dependencies. We expect this to be specified in a future proposal.

I feel like #3 in particular really hurts this proposal. It's impossible to evaluate this without at least a general idea of how the TLS service and the transport layer communicate. It's okay to handwave the details—for instance, you could say "Type X represents a network connection, and has methods to read, write, and close it", without describing those methods in detail—but without at least an overview of how this will be used, it's very difficult to evaluate.

I think this is probably a good design that just isn't being explained very clearly. I hope you can clarify some of these points.

···

On Mar 20, 2017, at 1:04 PM, Gelareh Taban via swift-server-dev <swift-server-dev@swift.org> wrote:

--
Brent Royal-Gordon
Architechies


(Helge Heß) #4

Hi Gelareh,

I haven’t attended a single security API meeting yet but just curious if it is possible to make the api support vector write or write from swift Collection protocol?

The disadvantage of Collection over Array (or UnsafePointer) is that there is not standardised way to quickly get a raw pointer to it, right? How would you efficiently grab the data, by iterating over the Collection? :slight_smile:

Also, what you propose doesn’t actually work in Swift 3.0. Does it work in 3.1 or 4? (`error: cannot specialize non-generic type ‘Collection’`)

Like the followings:
func tlsWrite(buffer: Collection<UInt8>) throws
func tlsWritev(vectors: Collection<iovec>)

If it is possible it will be very helpful since
1) it is very swifty and user friendly
2) vectorIO could significantly reduce the overhead to merge fragments of bytes into a single buffer.

+1 on vectors.

My headache with the design is that it doesn’t support async I/O (even if you don’t implement that right away, you could still design the API to support it). For read/write I would propose to match the GCD channel API.

hh

···

On 23 Mar 2017, at 12:11, Michael Chiu via swift-server-dev <swift-server-dev@swift.org> wrote:

Michael.

On Mar 20, 2017, at 1:04 PM, Gelareh Taban via swift-server-dev <swift-server-dev@swift.org> wrote:

Hello all,

I would like to share the following API proposal draft for Server security. Once enough feedback has been received, I will initiate a PR for an official review period.

A more readable version of the proposal can be found at: https://github.com/gtaban/blogs/blob/master/TLSService_API_Proposal.md

Regards,
Gelareh

------------------------------------------

Proposal: SS-001 (TLS Service APIs)
Authors: Gelareh Taban, Bill Abt
Review Manager: TBD
Status: Awaiting review

1 - Introduction

This proposal presents the design of TLS service, which is a collection of Swift APIs that provide SSL/TLS functionality with a consistent API surface for both Apple and Linux platforms.

For the purposes of brevity, we use the term TLS to refer to all SSL and TLS related functionality in the remainder of the document.

2 - Motivation

Currently there is no standard set of Swift TLS APIs on Apple and Linux platforms which has resulted in projects implementing their own Swift security functionality or using their security library of choice (be it OpenSSL, LibreSSL, etc.). This has resulted in fragmentation of the space as well as incompatibility of project dependencies if more than one security package is needed by different modules.

This motivates the necessity for defining a standard set of protocols that define the behavior of the TLS service and how the application and the server and networking layers beneath it interact with the TLS service.

3 - Proposed solution

We propose a new model that is easily extensible and supports a plug-n-play architecture which allows an application to choose the underlying security framework of choice.

https://github.com/gtaban/blogs/blob/master/TLSServiceArchitecture.png

3.1 - Assumptions

At the time of writing this proposal, the Swift Server API work group has not yet defined any standards for the transport management or application layers. For our design, we have assumed a network stack that consists of:

- System transport layer
- Transport management
- HTTP request management
- (Optional) Web server
- Application

Our security stack is composed of:
- Underlying security framework (SecureTransport library in Security.Framework on Apple and OpenSSL or alternatives on Linux)
- TLS service

In our model diagram (https://github.com/gtaban/blogs/blob/master/TLSServiceArchitecture.png) we omit both the system level transport layer as well as the underlying security library and show the relationship between the TLS service protocol and the various application layers.

3.2 - Design description

TLS service protocol defines a set of methods that is implemented by TLS service and it interfaces with the transport management layer (for example, socket management). These methods are implemented by the TLS service which in turn uses its choice an of underlying security library. As an example, the TLS service uses SecurityTransport library on Apple platform and OpenSSL or alternatively LibreSSL on Linux.

If an application requires TLS for its use case, it creates a TLS service object and configures it based on its requirements. Note that the configuration of the TLS service is outside the scope of the current document.

The application then passes the TLS service object to its lower level frameworks that deal with networking and communication. Each lower level framework maintains an optional instance variable of type TLS service protocol. If the optional variable exists, it is further passed down until it gets to the lowest level that deals with the Swift transport layer APIs (in the diagram above, this is the HTTP Management layer). When this layer creates the connection using the transport layer APIs, it assigns the TLS service object to the transport layer delegate. The Swift socket layer is then responsible for calling the TLS service protocol methods that handle the TLS functionality at the appropriate times.

Note that in the diagram above, we abstract away the transport mechanism using transport management protocol. The definition for this protocol is out of the scope of this proposal and will be discussed in a separate, future proposal.

3.3. - Benefits

There are several benefits provided by this model:

1- It allows the application to choose the underlying security library of choice for their TLS by passing in the appropriate TLS service object.

As long as the TLS service object conforms to the specification of the TLS service protocol, it can implement its methods using any underlying security library. The application can then create the TLS service object of its choice and simply pass the object to the appropriate methods that invoke transport-level functionality.

This model is specially important for projects that involve multiple dependencies that use conflicting security libraries (e.g., LibreSSL and OpenSSL which share the same API surface and building a project with both of these libraries results in link errors). Now the application can choose its own TLS service object and pass it to all frameworks that require TLS.

2- Application do not have dependency on underlying security framework if TLS is not enabled.

By mandating Transport/HTTP Management layers to be dependent only on the TLS service protocol, they are agnostic of the underlying security framework and hence do not need to have them in the project if they are not used.

3- It allows users to use the same TLS library for both client and server applications.

This is especially important for many server side applications which need to behave as both clients and servers depending on external services they connect to. Also by decoupling the TLS service client and server objects, debugging an application with multiple types of connections will be simplified.

4- The underlying Transport/HTTP Management layers are agnostic of the underlying security functionality and simply hand off the TLS service object to the Transport layer delegate.

5- The underlying Transport layer is responsible for supporting blocking and non-blocking connections as long as the underlying security library supports both types of connection.

3.4 - A note on creating a Swifty TLS service

There are a number of cases in low-level systems programming where similar functionality is differentiated by a flag or type, such as SOCK_STREAM vs. SOCK_DGRAM or SSLv23_server_method vs. SSLv23_client_method.

The latter is an example of how OpenSSL differentiates between the server side or the client side of a TLS connection. In TLS, the client is the initiator of a TLS session; the server is the entity that accepts the requests for TLS sessions made by clients. Depending on the connection type (server or client), different actions and policies are supported such as cipher suite selection during a TLS handshake, request for handshake renegotiations and trust validation policies for server name and certificates.

Whilst using a flag or a type is convenient in languages such as C, it's not very Swift-like and does not adhere to the single responsibility principle [1] of object oriented paradigm. We recommend that different interfaces should be created that decouple the client and server concepts as far as the higher application layers are concerned. This is generalized approach that can be adopted by the other layers of the application stack. For example, the Transport layer can also decouple the interface of client and server connections.

4 - Detailed design

4.1 - TLS service protocol

The TLS service protocol describes the methods that the transport layer calls to handle transport-level events for the TLS service object.

- onClientCreate

This will be called when a client I/O connection is created and appropriate TLS connection needs to be configured, including context creation, the handshake and connection verification.

This is a client only method.

///
/// Setup the contexts and process the TLSService configurations (certificates, etc)
///

func onClientCreate() throws

- onServerCreate

This will be called when a server I/O connection is created and appropriate TLS connection needs to be setup, including context creation, the handshake and connection verification.

This is a server only method.

///
/// Setup the contexts and process the TLSService configurations (certificates, etc)
///

func onServerCreate() throws

- onDestroy

This will be called when an I/O instance connection is closed and any remaining TLS context needs to be destroyed.

This is both a client and server method.

///
/// Destroy any remaining contexts
///
func onDestroy()

- onAccept

This will be called once an I/O instance connection has been accepted, to setup the TLS connection, do the handshake and connection verification.

This is both a client and server method.

///
/// Processing on acceptance from a listening connection
///
///
/// - Parameter IORef: The connected I/O instance
///
func onAccept(IORef: TransportManagementDelegate) throws

- onConnect

This will be called once a socket connection has been made, to setup the TLS connection, do the handshake and connection verification.

This is both a client and server method.

///
/// Processing on connection to a listening connection
///
/// - Parameter connectionRef: The connected I/O instance
///
func onConnect(IORef: TransportManagementDelegate) throws

- onSend

This will be called when data is to be written to an I/O instance. The input data buffer is written to the TLS connection associated with that I/O instance.

This is both a client and server method.

///
/// Low level writer
///
/// - Parameters:
/// - buffer: Buffer pointer
/// - bufSize: Size of the buffer
///
/// - Returns the number of bytes written. Zero indicates TLS shutdown, less than zero indicates error.
///
func onSend(buffer: UnsafeRawPointer, bufSize: Int) throws -> Int

- onReceive

This will be called when data is to be read from an I/O instance. Encrypted data is read from TLS connection associated with that I/O instance and decrypted and written to the buffer passed in.

This is both a client and server method.

///
/// Low level reader
///
/// - Parameters:
/// - buffer: Buffer pointer
/// - bufSize: Size of the buffer
///
/// - Returns the number of bytes read. Zero indicates TLS shutdown, less than zero indicates error.
///
func onReceive(buffer: UnsafeMutableRawPointer, bufSize: Int) throws -> Int

5 - Non-goals

This proposal:

1- DOES NOT describe the TLS service configuration, which includes information on certificate types, formats and chains, cipher suites, etc. We expect this to be specified in a future proposal.

2- DOES NOT describe the TLS service trust policies, which define trust and validation policies of the incoming connection. We expect this to be specified in a future proposal.

3- DOES NOT describe the interface between the TLS service and the transport layer and any dependencies. We expect this to be specified in a future proposal.

6 - Source compatibility

The proposed change is designed to be as non-breaking as possible, however it does place several assumptions on the rest of the transport/application stack.

- The application layer must import and instantiate a TLS service object which implements the TLS service protocol if it wants to enable TLS service.

- Every framework layer above the transport management layer but below the application layer (which includes HTTP and server frameworks) has an optional object that implements the TLS service protocol. If the TLS object exists, it is passed down to each layer below.

- The HTTP layer which sets up the transport management layer assigns the TLS object to the transport management delegate if the object exists.

- The transport management layer which sets up the I/O communication implements the transport management protocol.

- The transport management layer which sets up the I/O communication and deals with the low level C system I/O, calls the appropriate TLS service protocol methods whenever I/O data needs to be secured.

- The long term goal for the location of the TLS service protocol is within the Foundation framework. In the short term, the protocol can live within the transport management framework.

7 - References

1 - https://web.archive.org/web/20150202200348/http://www.objectmentor.com/resources/articles/srp.pdf

_______________________________________________
swift-server-dev mailing list
swift-server-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-server-dev

_______________________________________________
swift-server-dev mailing list
swift-server-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-server-dev


#5

I also have a few thought on the TLS API, particularly on the stream side. I think we can leverage swift as a protocol oriented language, so instead of sending one / some buffers, we can do something like

protocol RawBytesRepresentable {
    var temporaryBytesBuffer: UnsafeRawBufferPointer { get } // allocation method provided in the user's side
    func bufferDestory() // deallocate method, provided by user and performed on the TLS’s side
}

func tlsWrite(contents: inout AnyCollection<RawBytesRepresentable>) {
    for buffer in contents {
        … copy to BIO …
        buffer.bufferDestory()
   }
   …omit….
}

We will have the benefit on transmitting swift structs / classes (as long as they confirms the protocol) and minimize unnecessary memory allocations. (For example for some structure implicitly contain its storage there’s will be no more allocation at all and the destroy method can simply be an empty function).

Personally an idea TLS framework for me is that it returns a TLSContext object that I can somehow poll just like a socket and file descriptor (can be done by eventfd/kqueue), I mean normal you have to read from a socket, then it to the TLS, and then to check if the TLS has enough data to process and if not you feed it again.

Having a customize reader (how to read from a source) and writer (how to write to a source) is just amazing since I can tell the TLS how to do that by themselves and hence reduce the complexity of the main event loop by a lot.

For example if I’m communicating to an embed device using TLS on top of I2C

public struct Little_device : TransportManagementDelegate {
    // teach the TLS framework to write with i2c
    public func writer(contents: AnyCollection<RawBytesRepresentable>) {
  i2c_write(my_device, contents)
    }
    // teach the TLS how to read from an i2c bus
    public func reader() -> AnyCollection<RawBytesRepresentable> {
       var readStack = Stack<Payload>()
       while (some_condition) {
           readStack.push(i2c_read(my_device,))
       }
       return readStack
    }
}

let tls_context = TLSServer(source: some_source_dont_have_2_be_fd)
my_event_loop.add(tls_context)

…omit….now some where in the event loop
var newConnection = triggered_server_context.accept() // tls accept performed here
my_event_loop.add(newConnection)

Why do these methods all have "on" prefixes? I'm not totally sure I understand the intended usage here, but I see two possibilities:

* These are imperative commands. `onAccept` says that the TLS engine should accept a connection, `onSend` means it should send some data, etc. In that case, these should not have any prefix—they should just be `accept`, `send`, etc.

* These are essentially delegate methods notifying the TLS engine of an event. `onAccept` says that the system has accepted a connection and the TLS engine should do what it needs to do with it, `onSend` means the system is about to send data and it needs the TLS engine to modify it, etc. If so, Swift APIs more often use words like `should`, `will`, or `did` than `on`, particularly since they're more precise about the timing of the delegate method compared to the actual event.

I totally agree that the on-prefix is not quite swift like and the ‘will' and ‘did’ part.

I take it the parameter is some sort of abstraction wrapping e.g. a socket. So why is it called a `TransportManagementDelegate`? Shouldn't its name include words like `Connection` or `Socket` or `IOHandle` or something?

Do we want the parameter to be labeled `IORef`? That's not very idiomatic; it doesn't read well or follow the Swift naming guidelines.

You say this is for both clients and servers. When does a TLS client have a listening connection that it `accept`s connections on?

Is it called at different times or in different ways than `onServerCreate`?

I can understand this one, even on LiberalSSL you have to manually call tls_accept() after you accept() it from the socket, so it will indeed make some sense to include this method, especially usefully when the user want to store the TLS context somewhere.

This sounds like the TLS engine owns the network connection (at least by this point) and is responsible for writing to it. Does that mean `accept` and `connect` take ownership of the connection and hold on to it? If you have several different simultaneous connections, how do you know which connection this should write to? Or does a given TLSService only own one connection at a time? If so, does the transport management layer create a new TLSService instance for each connection? How? If each TLSService is bound to one connection, shouldn't it be created already knowing the connection it's going to use?

The thing about TLS is that itself is a separate layer from the transport layer but it is act like an extra transport layer itself, where accept/connect need to do twice (one on the transport layer, one of the TLS layer), and I can understand the users might want to inject something in between each procedure. For some reasons, depends on how the user want to use TLS for it can be one (real) connection but multiple (tls) connection, for example wrapping TLS in an extra layer of protocol. In this case having an abstract TLSContext representing the TLS connection is more appropriate then have the TLS framework take over the control of the socket.

Michael


(Georgios Moschovitis) #6

* These are essentially delegate methods notifying the TLS engine of an event. `onAccept` says that the system has accepted a connection and the TLS engine should do what it needs to do with it, `onSend` means the system is about to send data and it needs the TLS engine to modify it, etc. If so, Swift APIs more often use words like `should`, `will`, or `did` than `on`, particularly since they're more precise about the timing of the delegate method compared to the actual event.

+1 onXXX methods look definitely non-Swifty to me.

One other thing that bothers me is the `Service` postfix of the main protocol.
`Service` is a common name used in higher-level applications.

Maybe we could replace it with another postfix (e.g. `Engine`) or even drop it completely?

-g.


(Gelareh Taban) #7

Hi Brent,
The answers are in line.
gelareh

      4.1 - TLS service protocol

      The TLS service protocol describes the methods that the transport
      layer calls to handle transport-level events for the TLS service
      object.

I have a bunch of questions about the design you're presenting, and I think
many of them ultimately stem from not understanding some of the high-level
aspects of the proposal. For instance:

* What types conform to this protocol? From the diagram, it looks like

there's a type for each "engine"—a SecureTransportService, an
OpenSSLService, etc.—and each instance represents a particular
configuration of that engine. So you create a WhateverTLSService, configure
it, and then hand it off (through several layers) to the transport
management layer, which calls methods on it to handle various events. The
transport management layer then uses that one TLSService to handle many
connections. Is that correct?

[g]
More or less. As long as the implementation conforms to the protocol, it
can use whatever underlying security library it wants. This way we can have
a plug and play architecture which allows the user to pick the security
library implementation of its choice (eg. LibreSSL, OpenSSL, etc)

* What is the lifecycle of a connection? Does the TLSService create them

itself, does the transport management layer create them and hand them off,
or does the transport management layer retain control over them from
beginning to end?

[g] The connection life cycle is handled by levels higher than TLS. For all
intents and purposes, the transport management layers owns a TLS service
delegate which calls the appropriate TLS-related functionality at the right
time (eg. at socket creation time, at connection time, etc).

* You discuss the higher layers creating a TLSService object and caching

it in a property, then ultimately handing it down to the transport
management layer, which then attaches it to socket objects. But presumably
you can have many socket objects, possibly simultaneously. Are they all
served by a single TLSService instance, or by many? If they share a
TLSService, how does the TLSService know which socket is talking to it at a
given moment? If they have separate ones, how does the transport management
layer acquire a new one when it needs it?

[g]
Each socket instance would have its own TLS service delegate -- this is
important because each socket might have its own specific TLS channel
properties.

You mention that this proposal is very small in scope, and it's fine to

describe some of these details in general ways. For instance, you don't
need to describe the interface to a Swift socket or the transport layer in
detail. But currently, the description of these surrounding systems is *so*
vague that I'm struggling to assess this design.

Perhaps some of these details have been described in other documents or
meetings; if so, they really need to be presented in this document, too.

[g] fair enough. The problem is that right now in the servers working
group, we have not defined *any* of our interfaces. This is the first
proposal and we are trying to only pin the things which this service
requires.
In any case, if you think other things should be included, please let us
know.

      - onClientCreate

Why do these methods all have "on" prefixes? I'm not totally sure I

understand the intended usage here, but I see two possibilities:

* These are imperative commands. `onAccept` says that the TLS engine

should accept a connection, `onSend` means it should send some data, etc.
In that case, these should not have any prefix—they should just be
`accept`, `send`, etc.

* These are essentially delegate methods notifying the TLS engine of an

event. `onAccept` says that the system has accepted a connection and the
TLS engine should do what it needs to do with it, `onSend` means the system
is about to send data and it needs the TLS engine to modify it, etc. If so,
Swift APIs more often use words like `should`, `will`, or `did` than `on`,
particularly since they're more precise about the timing of the delegate
method compared to the actual event.

In either case, I don't think "on" is the best naming for these. It

needlessly bucks platform conventions.

[g]
The latter description is the intended. Your points are fair and perhaps we
can modify the delegate method names to, perhaps:

onClientCreate --> didClientCreate
onServerCreate --> didServerCreate
onDestroy --> willDestroy
onAccept --> didAccept(on connection: TransportManagementDelegate)
onConnect --> didConnect(on connection: TransportManagementDelegate)
onSend --> willSend(with data: UnsafeRawPointer, dataSize: Int))
onReceive --> willReceive(with data: UnsafeRawPointer, dataSize: Int))

      This will be called when a client I/O connection is created and
      appropriate TLS connection needs to be configured, including context
      creation, the handshake and connection verification.

      This is a client only method.

      ///
      /// Setup the contexts and process the TLSService configurations
      (certificates, etc)
      ///

      func onClientCreate() throws

      - onServerCreate

      This will be called when a server I/O connection is created and
      appropriate TLS connection needs to be setup, including context
      creation, the handshake and connection verification.

      This is a server only method.

      ///
      /// Setup the contexts and process the TLSService configurations
      (certificates, etc)
      ///

      func onServerCreate() throws

What are these methods supposed to do, exactly?

* Do they put `self` into either a client state or a server state? If so,

what happens if you call both, or neither, or call one twice? Would it be
better to do this as part of initialization, or to have them make a client
TLS object or server TLS object, or to require whatever code hands the
TLSService to the TransportManager to pre-configure it as either client or
server?

[g]
They setup the TLS contexts and process the certificates. They do put self
in either a client or server state via associated parameter sets in OpenSSL
and SecureTransport.
(I am pretty sure) if you call them multiple times, they simply over write
the previous setting. But we can test this during implementation.

These methods are essentially initialization that gets called when the
TransportManager decides it has enough information to set it as client or
server.

* Do they create a new instance that's either a client or a server? If

so, how do they return it?

* Do they configure something recently created as either client or

server? If so, how do they access whatever they need to configure?

Basically, what state are these supposed to operate upon?

[g]
Most of the functionality is the same for client or server except for
setting the server/client flag/function in the underlying security library.
The instance is stored in the delegate.

      - onDestroy

      This will be called when an I/O instance connection is closed and any
      remaining TLS context needs to be destroyed.

      This is both a client and server method.

      ///
      /// Destroy any remaining contexts
      ///
      func onDestroy()

Is this called at the end of each connection, or is it called once when

the transport management layer is totally finished with the TLSService, or
are these the same thing?

If per-connection, how does it know which connection?

If during destruction, should we just class-constrain and use `deinit`

for this purpose?

[g] this is per-connection and the TLS service would keep a context for the
connection.
So the transport manager has a delegate instance pointing to this TLS
service instance which itself has a context.

      - onAccept

      This will be called once an I/O instance connection has been
      accepted, to setup the TLS connection, do the handshake and
      connection verification.

      This is both a client and server method.

      ///
      /// Processing on acceptance from a listening connection
      ///
      ///
      /// - Parameter IORef: The connected I/O instance
      ///
      func onAccept(IORef: TransportManagementDelegate) throws

I take it the parameter is some sort of abstraction wrapping e.g. a

socket. So why is it called a `TransportManagementDelegate`? Shouldn't its
name include words like `Connection` or `Socket` or `IOHandle` or
something?

[g] we can simply call it ConnectionDelegate. I was trying to be consistent
in my terminology.

Do we want the parameter to be labeled `IORef`? That's not very

idiomatic; it doesn't read well or follow the Swift naming guidelines.

[g] great feedback. Thanks!

You say this is for both clients and servers. When does a TLS client have

a listening connection that it `accept`s connections on?

Is it called at different times or in different ways than `onServerCreate`?

[g] Bad copy/paste! for both onConnect and onAccept. They are obviously
server and client specific.

      - onConnect

      This will be called once a socket connection has been made, to setup
      the TLS connection, do the handshake and connection verification.

      This is both a client and server method.

      ///
      /// Processing on connection to a listening connection
      ///
      /// - Parameter connectionRef: The connected I/O instance
      ///
      func onConnect(IORef: TransportManagementDelegate) throws

The same as above, with appropriate substitutions.

      - onSend

      This will be called when data is to be written to an I/O instance.
      The input data buffer is written to the TLS connection associated
      with that I/O instance.

      This is both a client and server method.

      ///
      /// Low level writer
      ///
      /// - Parameters:
      /// - buffer: Buffer pointer
      /// - bufSize: Size of the buffer
      ///
      /// - Returns the number of bytes written. Zero indicates TLS
      shutdown, less than zero indicates error.
      ///
      func onSend(buffer: UnsafeRawPointer, bufSize: Int) throws -> Int

Is there a reason you use an UnsafeRawPointer and a buffer size, instead

of using an UnsafeRawBufferPointer which would encapsulate both?

[g] it was pointed our rightly by others that it's not good to have either
of them :slight_smile: Right now, we are looking at have Data and more efficient
versions.

Why is shutdown indicated with zero, rather than the return value being

Optional and being nil? Why are errors signaled with negative values
instead of being thrown? (Or are you saying that negative returns are
invalid? That's different from saying "indicates error".)

[g] This is actually implementation details which shouldnt have been
included in this pitch in the first place.

If a TLSService return less than `bufSize`, will the enclosing later try

to.re-send the remaining data in subsequent calls?

This sounds like the TLS engine owns the network connection (at least by
this point) and is responsible for writing to it. Does that mean `accept`
and `connect` take ownership of the connection and hold on to it? If you
have several different simultaneous connections, how do you know which
connection this should write to? Or does a given TLSService only own one
connection at a time? If so, does the transport management layer create a
new TLSService instance for each connection? How? If each TLSService is
bound to one connection, shouldn't it be created already knowing the
connection it's going to use?

[g] I believe I have talked about the relationship between connections and
TLS service.
BlueSSLService implements a very similar protocol and you can get more
information on it here:

https://github.com/IBM-Swift/BlueSSLService/blob/master/Sources/SSLService.swift

      - onReceive

      This will be called when data is to be read from an I/O instance.
      Encrypted data is read from TLS connection associated with that I/O
      instance and decrypted and written to the buffer passed in.

      This is both a client and server method.

      ///
      /// Low level reader
      ///
      /// - Parameters:
      /// - buffer: Buffer pointer
      /// - bufSize: Size of the buffer
      ///
      /// - Returns the number of bytes read. Zero indicates TLS shutdown,
      less than zero indicates error.
      ///
      func onReceive(buffer: UnsafeMutableRawPointer, bufSize: Int) throws
      -> Int

If I understand correctly, `buffer` is an uninitialized memory region

that the type should fill with data. Is that correct?

Otherwise, the same as above, with appropriate substitutions.

      5 - Non-goals

      This proposal:

      1- DOES NOT describe the TLS service configuration, which includes
      information on certificate types, formats and chains, cipher suites,
      etc. We expect this to be specified in a future proposal.

      2- DOES NOT describe the TLS service trust policies, which define
      trust and validation policies of the incoming connection. We expect
      this to be specified in a future proposal.

      3- DOES NOT describe the interface between the TLS service and the
      transport layer and any dependencies. We expect this to be specified
      in a future proposal.

I feel like #3 in particular really hurts this proposal. It's impossible

to evaluate this without at least a general idea of how the TLS service and
the transport layer communicate. It's okay to handwave the details—for
instance, you could say "Type X represents a network connection, and has
methods to read, write, and close it", without describing those methods in
detail—but without at least an overview of how this will be used, it's very
difficult to evaluate.

I think this is probably a good design that just isn't being explained very
clearly. I hope you can clarify some of these points.

···

--
Brent Royal-Gordon
Architechies

From: Brent Royal-Gordon <brent@architechies.com>
To: Gelareh Taban/Austin/IBM@IBMUS
Cc: swift-server-dev@swift.org, Bill Abt/Cambridge/IBM@IBMUS
Date: 04/01/2017 01:15 AM
Subject: Re: [swift-server-dev] Draft proposal for TLS Service APIs
            (please review)

      On Mar 20, 2017, at 1:04 PM, Gelareh Taban via swift-server-dev < swift-server-dev@swift.org> wrote:

      4.1 - TLS service protocol

      The TLS service protocol describes the methods that the transport
      layer calls to handle transport-level events for the TLS service
      object.

I have a bunch of questions about the design you're presenting, and I think
many of them ultimately stem from not understanding some of the high-level
aspects of the proposal. For instance:

* What types conform to this protocol? From the diagram, it looks like
there's a type for each "engine"—a SecureTransportService, an
OpenSSLService, etc.—and each instance represents a particular
configuration of that engine. So you create a WhateverTLSService, configure
it, and then hand it off (through several layers) to the transport
management layer, which calls methods on it to handle various events. The
transport management layer then uses that one TLSService to handle many
connections. Is that correct?

* What is the lifecycle of a connection? Does the TLSService create them
itself, does the transport management layer create them and hand them off,
or does the transport management layer retain control over them from
beginning to end?

* You discuss the higher layers creating a TLSService object and caching it
in a property, then ultimately handing it down to the transport management
layer, which then attaches it to socket objects. But presumably you can
have many socket objects, possibly simultaneously. Are they all served by a
single TLSService instance, or by many? If they share a TLSService, how
does the TLSService know which socket is talking to it at a given moment?
If they have separate ones, how does the transport management layer acquire
a new one when it needs it?

You mention that this proposal is very small in scope, and it's fine to
describe some of these details in general ways. For instance, you don't
need to describe the interface to a Swift socket or the transport layer in
detail. But currently, the description of these surrounding systems is *so*
vague that I'm struggling to assess this design.

Perhaps some of these details have been described in other documents or
meetings; if so, they really need to be presented in this document, too.

      - onClientCreate

Why do these methods all have "on" prefixes? I'm not totally sure I
understand the intended usage here, but I see two possibilities:

* These are imperative commands. `onAccept` says that the TLS engine should
accept a connection, `onSend` means it should send some data, etc. In that
case, these should not have any prefix—they should just be `accept`,
`send`, etc.

* These are essentially delegate methods notifying the TLS engine of an
event. `onAccept` says that the system has accepted a connection and the
TLS engine should do what it needs to do with it, `onSend` means the system
is about to send data and it needs the TLS engine to modify it, etc. If so,
Swift APIs more often use words like `should`, `will`, or `did` than `on`,
particularly since they're more precise about the timing of the delegate
method compared to the actual event.

In either case, I don't think "on" is the best naming for these. It
needlessly bucks platform conventions.

      This will be called when a client I/O connection is created and
      appropriate TLS connection needs to be configured, including context
      creation, the handshake and connection verification.

      This is a client only method.

      ///
      /// Setup the contexts and process the TLSService configurations
      (certificates, etc)
      ///

      func onClientCreate() throws

      - onServerCreate

      This will be called when a server I/O connection is created and
      appropriate TLS connection needs to be setup, including context
      creation, the handshake and connection verification.

      This is a server only method.

      ///
      /// Setup the contexts and process the TLSService configurations
      (certificates, etc)
      ///

      func onServerCreate() throws

What are these methods supposed to do, exactly?

* Do they put `self` into either a client state or a server state? If so,
what happens if you call both, or neither, or call one twice? Would it be
better to do this as part of initialization, or to have them make a client
TLS object or server TLS object, or to require whatever code hands the
TLSService to the TransportManager to pre-configure it as either client or
server?

* Do they create a new instance that's either a client or a server? If so,
how do they return it?

* Do they configure something recently created as either client or server?
If so, how do they access whatever they need to configure?

Basically, what state are these supposed to operate upon?

      - onDestroy

      This will be called when an I/O instance connection is closed and any
      remaining TLS context needs to be destroyed.

      This is both a client and server method.

      ///
      /// Destroy any remaining contexts
      ///
      func onDestroy()

Is this called at the end of each connection, or is it called once when the
transport management layer is totally finished with the TLSService, or are
these the same thing?

If per-connection, how does it know which connection?

If during destruction, should we just class-constrain and use `deinit` for
this purpose?

      - onAccept

      This will be called once an I/O instance connection has been
      accepted, to setup the TLS connection, do the handshake and
      connection verification.

      This is both a client and server method.

      ///
      /// Processing on acceptance from a listening connection
      ///
      ///
      /// - Parameter IORef: The connected I/O instance
      ///
      func onAccept(IORef: TransportManagementDelegate) throws

I take it the parameter is some sort of abstraction wrapping e.g. a socket.
So why is it called a `TransportManagementDelegate`? Shouldn't its name
include words like `Connection` or `Socket` or `IOHandle` or something?

Do we want the parameter to be labeled `IORef`? That's not very idiomatic;
it doesn't read well or follow the Swift naming guidelines.

You say this is for both clients and servers. When does a TLS client have a
listening connection that it `accept`s connections on?

Is it called at different times or in different ways than `onServerCreate`?

      - onConnect

      This will be called once a socket connection has been made, to setup
      the TLS connection, do the handshake and connection verification.

      This is both a client and server method.

      ///
      /// Processing on connection to a listening connection
      ///
      /// - Parameter connectionRef: The connected I/O instance
      ///
      func onConnect(IORef: TransportManagementDelegate) throws

The same as above, with appropriate substitutions.

      - onSend

      This will be called when data is to be written to an I/O instance.
      The input data buffer is written to the TLS connection associated
      with that I/O instance.

      This is both a client and server method.

      ///
      /// Low level writer
      ///
      /// - Parameters:
      /// - buffer: Buffer pointer
      /// - bufSize: Size of the buffer
      ///
      /// - Returns the number of bytes written. Zero indicates TLS
      shutdown, less than zero indicates error.
      ///
      func onSend(buffer: UnsafeRawPointer, bufSize: Int) throws -> Int

Is there a reason you use an UnsafeRawPointer and a buffer size, instead of
using an UnsafeRawBufferPointer which would encapsulate both?

Why is shutdown indicated with zero, rather than the return value being
Optional and being nil? Why are errors signaled with negative values
instead of being thrown? (Or are you saying that negative returns are
invalid? That's different from saying "indicates error".)

If a TLSService return less than `bufSize`, will the enclosing later try
to.re-send the remaining data in subsequent calls?

This sounds like the TLS engine owns the network connection (at least by
this point) and is responsible for writing to it. Does that mean `accept`
and `connect` take ownership of the connection and hold on to it? If you
have several different simultaneous connections, how do you know which
connection this should write to? Or does a given TLSService only own one
connection at a time? If so, does the transport management layer create a
new TLSService instance for each connection? How? If each TLSService is
bound to one connection, shouldn't it be created already knowing the
connection it's going to use?

      - onReceive

      This will be called when data is to be read from an I/O instance.
      Encrypted data is read from TLS connection associated with that I/O
      instance and decrypted and written to the buffer passed in.

      This is both a client and server method.

      ///
      /// Low level reader
      ///
      /// - Parameters:
      /// - buffer: Buffer pointer
      /// - bufSize: Size of the buffer
      ///
      /// - Returns the number of bytes read. Zero indicates TLS shutdown,
      less than zero indicates error.
      ///
      func onReceive(buffer: UnsafeMutableRawPointer, bufSize: Int) throws
      -> Int

If I understand correctly, `buffer` is an uninitialized memory region that
the type should fill with data. Is that correct?

Otherwise, the same as above, with appropriate substitutions.

      5 - Non-goals

      This proposal:

      1- DOES NOT describe the TLS service configuration, which includes
      information on certificate types, formats and chains, cipher suites,
      etc. We expect this to be specified in a future proposal.

      2- DOES NOT describe the TLS service trust policies, which define
      trust and validation policies of the incoming connection. We expect
      this to be specified in a future proposal.

      3- DOES NOT describe the interface between the TLS service and the
      transport layer and any dependencies. We expect this to be specified
      in a future proposal.

I feel like #3 in particular really hurts this proposal. It's impossible to
evaluate this without at least a general idea of how the TLS service and
the transport layer communicate. It's okay to handwave the details—for
instance, you could say "Type X represents a network connection, and has
methods to read, write, and close it", without describing those methods in
detail—but without at least an overview of how this will be used, it's very
difficult to evaluate.

I think this is probably a good design that just isn't being explained very
clearly. I hope you can clarify some of these points.

--
Brent Royal-Gordon
Architechies


#8

The disadvantage of Collection over Array (or UnsafePointer) is that there is not standardised way to quickly get a raw pointer to it, right? How would you efficiently grab the data, by iterating over the Collection? :slight_smile:

Also, what you propose doesn’t actually work in Swift 3.0. Does it work in 3.1 or 4? (`error: cannot specialize non-generic type ‘Collection’`)

Like the followings:
func tlsWrite(buffer: Collection<UInt8>) throws
func tlsWritev(vectors: Collection<iovec>)

If it is possible it will be very helpful since
1) it is very swifty and user friendly
2) vectorIO could significantly reduce the overhead to merge fragments of bytes into a single buffer.

+1 on vectors.

My headache with the design is that it doesn’t support async I/O (even if you don’t implement that right away, you could still design the API to support it). For read/write I would propose to match the GCD channel API.

hh

Collection will never work since it is a generic protocol. But Collection<UInt8> will work.
I’m not quite sure if there’s way to write bytes into OpenSSL’s BIO, I guess it’s probably not, but if it’s possible we should be able to write bytes into the buffer directly.
An other alternative solution is to add an other layer of protocol, to force the type the collection contains must provide a method for raw byte access to the object it contains.

For example let’s say:

protocol RawBufferRepresentable {
    func rawBuffer() -> (UnsafeRawPointer, Int)
}

And on the TLS side:
fun tlsWrite(contents: AnyCollection<RawBufferRepresentable>)

Or even

protocol IOVectorRepresentable {
    func vectors() -> [iovec]
}

fun tlsWritev(vector: IOVectorRepresentable)

For vector IO, actually I started a project on DiscreteData type, which could theoretically archive zero copy (even on some writes) and reducing duplicated contents in the same buffer. The project is in very early stage but some fundamental feature is already working. Please check it out if interested. There should be still a lot of bugs, tho.

https://github.com/michael-yuji/DiscreteData

Michael


(Gelareh Taban) #9

Hi Helge,

My headache with the design is that it doesn’t support async I/O
(even if you don’t implement that right away, you could still design the

API to support it).

For read/write I would propose to match the GCD channel API.

Regarding I/O, current design of TLSService lets the implementation of
onReceive/onSend handle both blocking/non-blocking I/O. This is essentially
the model of SecureTransport and OpenSSL: if they receive less data than
expected and know that requested I/O hasn't completed, they return
errSSLWouldBlock or SSL_ERROR_WANT_READ/WRITE errors and let the caller
handle them appropriately. So TLSService would follow the same idea and
when implementing onReceive/onSend which handles the data, would return
EAGAIN and let the caller decide what to do if appropriate. The caller
layer (or layers above it) is the one that decides what I/O should be
configured.

Regards,
gelareh

···

From: Helge Heß via swift-server-dev <swift-server-dev@swift.org>
To: swift-server-dev <swift-server-dev@swift.org>
Date: 03/23/2017 06:25 AM
Subject: Re: [swift-server-dev] Draft proposal for TLS Service APIs
            (please review)
Sent by: swift-server-dev-bounces@swift.org

On 23 Mar 2017, at 12:11, Michael Chiu via swift-server-dev <swift-server-dev@swift.org> wrote:

Hi Gelareh,

I haven’t attended a single security API meeting yet but just curious if

it is possible to make the api support vector write or write from swift
Collection protocol?

The disadvantage of Collection over Array (or UnsafePointer) is that there
is not standardised way to quickly get a raw pointer to it, right? How
would you efficiently grab the data, by iterating over the Collection? :slight_smile:

Also, what you propose doesn’t actually work in Swift 3.0. Does it work in
3.1 or 4? (`error: cannot specialize non-generic type ‘Collection’`)

Like the followings:
func tlsWrite(buffer: Collection<UInt8>) throws
func tlsWritev(vectors: Collection<iovec>)

If it is possible it will be very helpful since
1) it is very swifty and user friendly
2) vectorIO could significantly reduce the overhead to merge fragments of

bytes into a single buffer.

+1 on vectors.

My headache with the design is that it doesn’t support async I/O (even if
you don’t implement that right away, you could still design the API to
support it). For read/write I would propose to match the GCD channel API.

hh

Michael.

On Mar 20, 2017, at 1:04 PM, Gelareh Taban via swift-server-dev <swift-server-dev@swift.org> wrote:

Hello all,

I would like to share the following API proposal draft for Server

security. Once enough feedback has been received, I will initiate a PR for
an official review period.

A more readable version of the proposal can be found at:

https://github.com/gtaban/blogs/blob/master/TLSService_API_Proposal.md

Regards,
Gelareh

------------------------------------------

Proposal: SS-001 (TLS Service APIs)
Authors: Gelareh Taban, Bill Abt
Review Manager: TBD
Status: Awaiting review

1 - Introduction

This proposal presents the design of TLS service, which is a collection

of Swift APIs that provide SSL/TLS functionality with a consistent API
surface for both Apple and Linux platforms.

For the purposes of brevity, we use the term TLS to refer to all SSL and

TLS related functionality in the remainder of the document.

2 - Motivation

Currently there is no standard set of Swift TLS APIs on Apple and Linux

platforms which has resulted in projects implementing their own Swift
security functionality or using their security library of choice (be it
OpenSSL, LibreSSL, etc.). This has resulted in fragmentation of the space
as well as incompatibility of project dependencies if more than one
security package is needed by different modules.

This motivates the necessity for defining a standard set of protocols

that define the behavior of the TLS service and how the application and the
server and networking layers beneath it interact with the TLS service.

3 - Proposed solution

We propose a new model that is easily extensible and supports a

plug-n-play architecture which allows an application to choose the
underlying security framework of choice.

https://github.com/gtaban/blogs/blob/master/TLSServiceArchitecture.png

3.1 - Assumptions

At the time of writing this proposal, the Swift Server API work group

has not yet defined any standards for the transport management or
application layers. For our design, we have assumed a network stack that
consists of:

- System transport layer
- Transport management
- HTTP request management
- (Optional) Web server
- Application

Our security stack is composed of:
- Underlying security framework (SecureTransport library in

Security.Framework on Apple and OpenSSL or alternatives on Linux)

- TLS service

In our model diagram (

https://github.com/gtaban/blogs/blob/master/TLSServiceArchitecture.png) we
omit both the system level transport layer as well as the underlying
security library and show the relationship between the TLS service protocol
and the various application layers.

3.2 - Design description

TLS service protocol defines a set of methods that is implemented by TLS

service and it interfaces with the transport management layer (for example,
socket management). These methods are implemented by the TLS service which
in turn uses its choice an of underlying security library. As an example,
the TLS service uses SecurityTransport library on Apple platform and
OpenSSL or alternatively LibreSSL on Linux.

If an application requires TLS for its use case, it creates a TLS

service object and configures it based on its requirements. Note that the
configuration of the TLS service is outside the scope of the current
document.

The application then passes the TLS service object to its lower level

frameworks that deal with networking and communication. Each lower level
framework maintains an optional instance variable of type TLS service
protocol. If the optional variable exists, it is further passed down until
it gets to the lowest level that deals with the Swift transport layer APIs
(in the diagram above, this is the HTTP Management layer). When this layer
creates the connection using the transport layer APIs, it assigns the TLS
service object to the transport layer delegate. The Swift socket layer is
then responsible for calling the TLS service protocol methods that handle
the TLS functionality at the appropriate times.

Note that in the diagram above, we abstract away the transport mechanism

using transport management protocol. The definition for this protocol is
out of the scope of this proposal and will be discussed in a separate,
future proposal.

3.3. - Benefits

There are several benefits provided by this model:

1- It allows the application to choose the underlying security library

of choice for their TLS by passing in the appropriate TLS service object.

As long as the TLS service object conforms to the specification of the

TLS service protocol, it can implement its methods using any underlying
security library. The application can then create the TLS service object of
its choice and simply pass the object to the appropriate methods that
invoke transport-level functionality.

This model is specially important for projects that involve multiple

dependencies that use conflicting security libraries (e.g., LibreSSL and
OpenSSL which share the same API surface and building a project with both
of these libraries results in link errors). Now the application can choose
its own TLS service object and pass it to all frameworks that require TLS.

2- Application do not have dependency on underlying security framework

if TLS is not enabled.

By mandating Transport/HTTP Management layers to be dependent only on

the TLS service protocol, they are agnostic of the underlying security
framework and hence do not need to have them in the project if they are not
used.

3- It allows users to use the same TLS library for both client and

server applications.

This is especially important for many server side applications which

need to behave as both clients and servers depending on external services
they connect to. Also by decoupling the TLS service client and server
objects, debugging an application with multiple types of connections will
be simplified.

4- The underlying Transport/HTTP Management layers are agnostic of the

underlying security functionality and simply hand off the TLS service
object to the Transport layer delegate.

5- The underlying Transport layer is responsible for supporting blocking

and non-blocking connections as long as the underlying security library
supports both types of connection.

3.4 - A note on creating a Swifty TLS service

There are a number of cases in low-level systems programming where

similar functionality is differentiated by a flag or type, such as
SOCK_STREAM vs. SOCK_DGRAM or SSLv23_server_method vs.
SSLv23_client_method.

The latter is an example of how OpenSSL differentiates between the

server side or the client side of a TLS connection. In TLS, the client is
the initiator of a TLS session; the server is the entity that accepts the
requests for TLS sessions made by clients. Depending on the connection type
(server or client), different actions and policies are supported such as
cipher suite selection during a TLS handshake, request for handshake
renegotiations and trust validation policies for server name and
certificates.

Whilst using a flag or a type is convenient in languages such as C, it's

not very Swift-like and does not adhere to the single responsibility
principle [1] of object oriented paradigm. We recommend that different
interfaces should be created that decouple the client and server concepts
as far as the higher application layers are concerned. This is generalized
approach that can be adopted by the other layers of the application stack.
For example, the Transport layer can also decouple the interface of client
and server connections.

4 - Detailed design

4.1 - TLS service protocol

The TLS service protocol describes the methods that the transport layer

calls to handle transport-level events for the TLS service object.

- onClientCreate

This will be called when a client I/O connection is created and

appropriate TLS connection needs to be configured, including context
creation, the handshake and connection verification.

This is a client only method.

///
/// Setup the contexts and process the TLSService configurations

(certificates, etc)

///

func onClientCreate() throws

- onServerCreate

This will be called when a server I/O connection is created and

appropriate TLS connection needs to be setup, including context creation,
the handshake and connection verification.

This is a server only method.

///
/// Setup the contexts and process the TLSService configurations

(certificates, etc)

///

func onServerCreate() throws

- onDestroy

This will be called when an I/O instance connection is closed and any

remaining TLS context needs to be destroyed.

This is both a client and server method.

///
/// Destroy any remaining contexts
///
func onDestroy()

- onAccept

This will be called once an I/O instance connection has been accepted,

to setup the TLS connection, do the handshake and connection verification.

This is both a client and server method.

///
/// Processing on acceptance from a listening connection
///
///
/// - Parameter IORef: The connected I/O instance
///
func onAccept(IORef: TransportManagementDelegate) throws

- onConnect

This will be called once a socket connection has been made, to setup the

TLS connection, do the handshake and connection verification.

This is both a client and server method.

///
/// Processing on connection to a listening connection
///
/// - Parameter connectionRef: The connected I/O instance
///
func onConnect(IORef: TransportManagementDelegate) throws

- onSend

This will be called when data is to be written to an I/O instance. The

input data buffer is written to the TLS connection associated with that I/O
instance.

This is both a client and server method.

///
/// Low level writer
///
/// - Parameters:
/// - buffer: Buffer pointer
/// - bufSize: Size of the buffer
///
/// - Returns the number of bytes written. Zero indicates TLS shutdown,

less than zero indicates error.

///
func onSend(buffer: UnsafeRawPointer, bufSize: Int) throws -> Int

- onReceive

This will be called when data is to be read from an I/O instance.

Encrypted data is read from TLS connection associated with that I/O
instance and decrypted and written to the buffer passed in.

This is both a client and server method.

///
/// Low level reader
///
/// - Parameters:
/// - buffer: Buffer pointer
/// - bufSize: Size of the buffer
///
/// - Returns the number of bytes read. Zero indicates TLS shutdown,

less than zero indicates error.

///
func onReceive(buffer: UnsafeMutableRawPointer, bufSize: Int) throws ->

Int

5 - Non-goals

This proposal:

1- DOES NOT describe the TLS service configuration, which includes

information on certificate types, formats and chains, cipher suites, etc.
We expect this to be specified in a future proposal.

2- DOES NOT describe the TLS service trust policies, which define trust

and validation policies of the incoming connection. We expect this to be
specified in a future proposal.

3- DOES NOT describe the interface between the TLS service and the

transport layer and any dependencies. We expect this to be specified in a
future proposal.

6 - Source compatibility

The proposed change is designed to be as non-breaking as possible,

however it does place several assumptions on the rest of the
transport/application stack.

- The application layer must import and instantiate a TLS service object

which implements the TLS service protocol if it wants to enable TLS
service.

- Every framework layer above the transport management layer but below

the application layer (which includes HTTP and server frameworks) has an
optional object that implements the TLS service protocol. If the TLS object
exists, it is passed down to each layer below.

- The HTTP layer which sets up the transport management layer assigns

the TLS object to the transport management delegate if the object exists.

- The transport management layer which sets up the I/O communication

implements the transport management protocol.

- The transport management layer which sets up the I/O communication and

deals with the low level C system I/O, calls the appropriate TLS service
protocol methods whenever I/O data needs to be secured.

- The long term goal for the location of the TLS service protocol is

within the Foundation framework. In the short term, the protocol can live
within the transport management framework.

7 - References

1 -

https://web.archive.org/web/20150202200348/http://www.objectmentor.com/resources/articles/srp.pdf

_______________________________________________
swift-server-dev mailing list
swift-server-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-server-dev

_______________________________________________
swift-server-dev mailing list
swift-server-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-server-dev

_______________________________________________
swift-server-dev mailing list
swift-server-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-server-dev


(Gelareh Taban) #10

Btw Helge, really interesting about the GCD channel APIs. How would your
propose they be integrated? Would they replace or modify onSend/onReceive
methods?

gelareh

···

From: Gelareh Taban via swift-server-dev <swift-server-dev@swift.org>
To: Helge Heß <me@helgehess.eu>
Cc: swift-server-dev <swift-server-dev@swift.org>
Date: 03/23/2017 09:46 AM
Subject: Re: [swift-server-dev] Draft proposal for TLS Service APIs
            (please review)
Sent by: swift-server-dev-bounces@swift.org

Hi Helge,

My headache with the design is that it doesn’t support async I/O
(even if you don’t implement that right away, you could still design the

API to support it).

For read/write I would propose to match the GCD channel API.

Regarding I/O, current design of TLSService lets the implementation of
onReceive/onSend handle both blocking/non-blocking I/O. This is essentially
the model of SecureTransport and OpenSSL: if they receive less data than
expected and know that requested I/O hasn't completed, they return
errSSLWouldBlock or SSL_ERROR_WANT_READ/WRITE errors and let the caller
handle them appropriately. So TLSService would follow the same idea and
when implementing onReceive/onSend which handles the data, would return
EAGAIN and let the caller decide what to do if appropriate. The caller
layer (or layers above it) is the one that decides what I/O should be
configured.

Regards,
gelareh

Inactive hide details for Helge Heß via swift-server-dev ---03/23/2017
06:25:51 AM---On 23 Mar 2017, at 12:11, Michael Chiu viaHelge Heß via
swift-server-dev ---03/23/2017 06:25:51 AM---On 23 Mar 2017, at 12:11,
Michael Chiu via swift-server-dev <swift-server-dev@swift.org> wrote: > Hi

From: Helge Heß via swift-server-dev <swift-server-dev@swift.org>
To: swift-server-dev <swift-server-dev@swift.org>
Date: 03/23/2017 06:25 AM
Subject: Re: [swift-server-dev] Draft proposal for TLS Service APIs (please
review)
Sent by: swift-server-dev-bounces@swift.org

On 23 Mar 2017, at 12:11, Michael Chiu via swift-server-dev <swift-server-dev@swift.org> wrote:

Hi Gelareh,

I haven’t attended a single security API meeting yet but just curious if

it is possible to make the api support vector write or write from swift
Collection protocol?

The disadvantage of Collection over Array (or UnsafePointer) is that there
is not standardised way to quickly get a raw pointer to it, right? How
would you efficiently grab the data, by iterating over the Collection? :slight_smile:

Also, what you propose doesn’t actually work in Swift 3.0. Does it work in
3.1 or 4? (`error: cannot specialize non-generic type ‘Collection’`)

Like the followings:
func tlsWrite(buffer: Collection<UInt8>) throws
func tlsWritev(vectors: Collection<iovec>)

If it is possible it will be very helpful since
1) it is very swifty and user friendly
2) vectorIO could significantly reduce the overhead to merge fragments of

bytes into a single buffer.

+1 on vectors.

My headache with the design is that it doesn’t support async I/O (even if
you don’t implement that right away, you could still design the API to
support it). For read/write I would propose to match the GCD channel API.

hh

Michael.

On Mar 20, 2017, at 1:04 PM, Gelareh Taban via swift-server-dev <swift-server-dev@swift.org> wrote:

Hello all,

I would like to share the following API proposal draft for Server

security. Once enough feedback has been received, I will initiate a PR for
an official review period.

A more readable version of the proposal can be found at:

https://github.com/gtaban/blogs/blob/master/TLSService_API_Proposal.md

Regards,
Gelareh

------------------------------------------

Proposal: SS-001 (TLS Service APIs)
Authors: Gelareh Taban, Bill Abt
Review Manager: TBD
Status: Awaiting review

1 - Introduction

This proposal presents the design of TLS service, which is a collection

of Swift APIs that provide SSL/TLS functionality with a consistent API
surface for both Apple and Linux platforms.

For the purposes of brevity, we use the term TLS to refer to all SSL and

TLS related functionality in the remainder of the document.

2 - Motivation

Currently there is no standard set of Swift TLS APIs on Apple and Linux

platforms which has resulted in projects implementing their own Swift
security functionality or using their security library of choice (be it
OpenSSL, LibreSSL, etc.). This has resulted in fragmentation of the space
as well as incompatibility of project dependencies if more than one
security package is needed by different modules.

This motivates the necessity for defining a standard set of protocols

that define the behavior of the TLS service and how the application and the
server and networking layers beneath it interact with the TLS service.

3 - Proposed solution

We propose a new model that is easily extensible and supports a

plug-n-play architecture which allows an application to choose the
underlying security framework of choice.

https://github.com/gtaban/blogs/blob/master/TLSServiceArchitecture.png

3.1 - Assumptions

At the time of writing this proposal, the Swift Server API work group

has not yet defined any standards for the transport management or
application layers. For our design, we have assumed a network stack that
consists of:

- System transport layer
- Transport management
- HTTP request management
- (Optional) Web server
- Application

Our security stack is composed of:
- Underlying security framework (SecureTransport library in

Security.Framework on Apple and OpenSSL or alternatives on Linux)

- TLS service

In our model diagram (

https://github.com/gtaban/blogs/blob/master/TLSServiceArchitecture.png) we
omit both the system level transport layer as well as the underlying
security library and show the relationship between the TLS service protocol
and the various application layers.

3.2 - Design description

TLS service protocol defines a set of methods that is implemented by TLS

service and it interfaces with the transport management layer (for example,
socket management). These methods are implemented by the TLS service which
in turn uses its choice an of underlying security library. As an example,
the TLS service uses SecurityTransport library on Apple platform and
OpenSSL or alternatively LibreSSL on Linux.

If an application requires TLS for its use case, it creates a TLS

service object and configures it based on its requirements. Note that the
configuration of the TLS service is outside the scope of the current
document.

The application then passes the TLS service object to its lower level

frameworks that deal with networking and communication. Each lower level
framework maintains an optional instance variable of type TLS service
protocol. If the optional variable exists, it is further passed down until
it gets to the lowest level that deals with the Swift transport layer APIs
(in the diagram above, this is the HTTP Management layer). When this layer
creates the connection using the transport layer APIs, it assigns the TLS
service object to the transport layer delegate. The Swift socket layer is
then responsible for calling the TLS service protocol methods that handle
the TLS functionality at the appropriate times.

Note that in the diagram above, we abstract away the transport mechanism

using transport management protocol. The definition for this protocol is
out of the scope of this proposal and will be discussed in a separate,
future proposal.

3.3. - Benefits

There are several benefits provided by this model:

1- It allows the application to choose the underlying security library

of choice for their TLS by passing in the appropriate TLS service object.

As long as the TLS service object conforms to the specification of the

TLS service protocol, it can implement its methods using any underlying
security library. The application can then create the TLS service object of
its choice and simply pass the object to the appropriate methods that
invoke transport-level functionality.

This model is specially important for projects that involve multiple

dependencies that use conflicting security libraries (e.g., LibreSSL and
OpenSSL which share the same API surface and building a project with both
of these libraries results in link errors). Now the application can choose
its own TLS service object and pass it to all frameworks that require TLS.

2- Application do not have dependency on underlying security framework

if TLS is not enabled.

By mandating Transport/HTTP Management layers to be dependent only on

the TLS service protocol, they are agnostic of the underlying security
framework and hence do not need to have them in the project if they are not
used.

3- It allows users to use the same TLS library for both client and

server applications.

This is especially important for many server side applications which

need to behave as both clients and servers depending on external services
they connect to. Also by decoupling the TLS service client and server
objects, debugging an application with multiple types of connections will
be simplified.

4- The underlying Transport/HTTP Management layers are agnostic of the

underlying security functionality and simply hand off the TLS service
object to the Transport layer delegate.

5- The underlying Transport layer is responsible for supporting blocking

and non-blocking connections as long as the underlying security library
supports both types of connection.

3.4 - A note on creating a Swifty TLS service

There are a number of cases in low-level systems programming where

similar functionality is differentiated by a flag or type, such as
SOCK_STREAM vs. SOCK_DGRAM or SSLv23_server_method vs.
SSLv23_client_method.

The latter is an example of how OpenSSL differentiates between the

server side or the client side of a TLS connection. In TLS, the client is
the initiator of a TLS session; the server is the entity that accepts the
requests for TLS sessions made by clients. Depending on the connection type
(server or client), different actions and policies are supported such as
cipher suite selection during a TLS handshake, request for handshake
renegotiations and trust validation policies for server name and
certificates.

Whilst using a flag or a type is convenient in languages such as C, it's

not very Swift-like and does not adhere to the single responsibility
principle [1] of object oriented paradigm. We recommend that different
interfaces should be created that decouple the client and server concepts
as far as the higher application layers are concerned. This is generalized
approach that can be adopted by the other layers of the application stack.
For example, the Transport layer can also decouple the interface of client
and server connections.

4 - Detailed design

4.1 - TLS service protocol

The TLS service protocol describes the methods that the transport layer

calls to handle transport-level events for the TLS service object.

- onClientCreate

This will be called when a client I/O connection is created and

appropriate TLS connection needs to be configured, including context
creation, the handshake and connection verification.

This is a client only method.

///
/// Setup the contexts and process the TLSService configurations

(certificates, etc)

///

func onClientCreate() throws

- onServerCreate

This will be called when a server I/O connection is created and

appropriate TLS connection needs to be setup, including context creation,
the handshake and connection verification.

This is a server only method.

///
/// Setup the contexts and process the TLSService configurations

(certificates, etc)

///

func onServerCreate() throws

- onDestroy

This will be called when an I/O instance connection is closed and any

remaining TLS context needs to be destroyed.

This is both a client and server method.

///
/// Destroy any remaining contexts
///
func onDestroy()

- onAccept

This will be called once an I/O instance connection has been accepted,

to setup the TLS connection, do the handshake and connection verification.

This is both a client and server method.

///
/// Processing on acceptance from a listening connection
///
///
/// - Parameter IORef: The connected I/O instance
///
func onAccept(IORef: TransportManagementDelegate) throws

- onConnect

This will be called once a socket connection has been made, to setup the

TLS connection, do the handshake and connection verification.

This is both a client and server method.

///
/// Processing on connection to a listening connection
///
/// - Parameter connectionRef: The connected I/O instance
///
func onConnect(IORef: TransportManagementDelegate) throws

- onSend

This will be called when data is to be written to an I/O instance. The

input data buffer is written to the TLS connection associated with that I/O
instance.

This is both a client and server method.

///
/// Low level writer
///
/// - Parameters:
/// - buffer: Buffer pointer
/// - bufSize: Size of the buffer
///
/// - Returns the number of bytes written. Zero indicates TLS shutdown,

less than zero indicates error.

///
func onSend(buffer: UnsafeRawPointer, bufSize: Int) throws -> Int

- onReceive

This will be called when data is to be read from an I/O instance.

Encrypted data is read from TLS connection associated with that I/O
instance and decrypted and written to the buffer passed in.

This is both a client and server method.

///
/// Low level reader
///
/// - Parameters:
/// - buffer: Buffer pointer
/// - bufSize: Size of the buffer
///
/// - Returns the number of bytes read. Zero indicates TLS shutdown,

less than zero indicates error.

///
func onReceive(buffer: UnsafeMutableRawPointer, bufSize: Int) throws ->

Int

5 - Non-goals

This proposal:

1- DOES NOT describe the TLS service configuration, which includes

information on certificate types, formats and chains, cipher suites, etc.
We expect this to be specified in a future proposal.

2- DOES NOT describe the TLS service trust policies, which define trust

and validation policies of the incoming connection. We expect this to be
specified in a future proposal.

3- DOES NOT describe the interface between the TLS service and the

transport layer and any dependencies. We expect this to be specified in a
future proposal.

6 - Source compatibility

The proposed change is designed to be as non-breaking as possible,

however it does place several assumptions on the rest of the
transport/application stack.

- The application layer must import and instantiate a TLS service object

which implements the TLS service protocol if it wants to enable TLS
service.

- Every framework layer above the transport management layer but below

the application layer (which includes HTTP and server frameworks) has an
optional object that implements the TLS service protocol. If the TLS object
exists, it is passed down to each layer below.

- The HTTP layer which sets up the transport management layer assigns

the TLS object to the transport management delegate if the object exists.

- The transport management layer which sets up the I/O communication

implements the transport management protocol.

- The transport management layer which sets up the I/O communication and

deals with the low level C system I/O, calls the appropriate TLS service
protocol methods whenever I/O data needs to be secured.

- The long term goal for the location of the TLS service protocol is

within the Foundation framework. In the short term, the protocol can live
within the transport management framework.

7 - References

1 -

https://web.archive.org/web/20150202200348/http://www.objectmentor.com/resources/articles/srp.pdf

_______________________________________________
swift-server-dev mailing list
swift-server-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-server-dev

_______________________________________________
swift-server-dev mailing list
swift-server-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-server-dev

_______________________________________________
swift-server-dev mailing list
swift-server-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-server-dev

_______________________________________________
swift-server-dev mailing list
swift-server-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-server-dev


(Helge Heß) #11

Hi Gelareh,

I suppose what you have been saying about EAGAIN etc may work out. It would be really nice to actually try the API with an NIO consumer, did you do this already? Just to make sure and test out that we aren’t missing something.

Wrt the GCD channel stuff. Well, your design has that separate service object which is ‘used' by some ‘Socket’ class. With GCD in mind I would design the TLS support as a new custom channel which is just wrapping a GCD channel. It would provide the same API like GCD plus the necessary extras. Kinda like OpenSSL BIOs, just with async.

But this would tie the I/O to a GCD API style (not necessarily to GCD, you could factor out the channel API as a ’stream' protocol). Quite likely this is still not desirable for everyone.

I haven’t seen a proposal on how (byte) streams are going to work for Server Side Swift (i.e. how the API of the HTTP Request/Response body streams would look like). That kinda ties into this.

Summary: The service object as you proposed should be fine as a support wrapper used by higher level I/O objects. I guess it is the right way to go to support various I/O styles. Just wrap OpenSSL and the macOS thing. Do not add anything else.
But *if* we would get a proper (byte) streaming abstraction, I would prefer if a TLS stream would live on top of this (be a stream itself and wrap the stream representing the connection).

hh

···

On 23 Mar 2017, at 16:52, Gelareh Taban <gtaban@us.ibm.com> wrote:

Btw Helge, really interesting about the GCD channel APIs. How would your propose they be integrated? Would they replace or modify onSend/onReceive methods?


(Gelareh Taban) #12

> Like the followings:
> func tlsWrite(buffer: Collection<UInt8>) throws
> func tlsWritev(vectors: Collection<iovec>)
>
> If it is possible it will be very helpful since
> 1) it is very swifty and user friendly
> 2) vectorIO could significantly reduce the overhead to merge fragments

of bytes into a single buffer.

+1 on vectors.

+1 on need to make it more Swifty.

What is the advantage of using vectored IO for TLS service?
I am not sure if TLS would support processing of gathered multiple input
buffers.


(Gelareh Taban) #13

There are 2 hard problems in computer science: cache invalidation, **naming
things**, and off-by-1 errors. -- Leon Bambrick

:slight_smile:

> * These are essentially delegate methods notifying the TLS engine of an

event.

> `onAccept` says that the system has accepted a connection and the TLS

engine should

> do what it needs to do with it, `onSend` means the system is about to

send data and

> it needs the TLS engine to modify it, etc. If so, Swift APIs more often

use words like

> `should`, `will`, or `did` than `on`, particularly since they're more

precise about the

> timing of the delegate method compared to the actual event.

+1 onXXX methods look definitely non-Swifty to me.

Fair enough. What do you recommend?

One other thing that bothers me is the `Service` postfix of the main

protocol.

`Service` is a common name used in higher-level applications.

Maybe we could replace it with another postfix (e.g. `Engine`) or even

drop it completely?

I agree that Service is not a particularly good name but that was the best
I could think of. I liked Engine but (1) we are not implementing anything,
just overlaying and (2) I didnt want any confusion with with the SSL Engine
of OpenSSL.

So again, I would be interested in your recommendations!

gelareh

···

From: Georgios Moschovitis <george.moschovitis@icloud.com>
To: Brent Royal-Gordon <brent@architechies.com>
Cc: Gelareh Taban/Austin/IBM@IBMUS, swift-server-dev@swift.org,
            Bill Abt/Cambridge/IBM@IBMUS
Date: 04/01/2017 01:23 AM
Subject: Re: [swift-server-dev] Draft proposal for TLS Service APIs
            (please review)

* These are essentially delegate methods notifying the TLS engine of an

event. `onAccept` says that the system has accepted a connection and the
TLS engine should do what it needs to do with it, `onSend` means the system
is about to send data and it needs the TLS engine to modify it, etc. If so,
Swift APIs more often use words like `should`, `will`, or `did` than `on`,
particularly since they're more precise about the timing of the delegate
method compared to the actual event.

+1 onXXX methods look definitely non-Swifty to me.

One other thing that bothers me is the `Service` postfix of the main
protocol.
`Service` is a common name used in higher-level applications.

Maybe we could replace it with another postfix (e.g. `Engine`) or even drop
it completely?

-g.


(Brent Royal-Gordon) #14

Hi Brent,
The answers are in line.
gelareh

Thank you for your detailed answers. I'm going to try to synthesize your answers about the lifecycle into a single description of how the TLSService is used. For clarity, I'll use the names from the proposal, even when we've discussed changing the names.

Some high-level part of the application configures a TLSService-conforming instance and conveys it through several layers to the transport management layer. At this point, the TLSService knows the certificates, domains, security settings, etc. it should use, but does not know whether it will be handling the client side or the server side of a connection.

Time passes. Eventually, the transport manager looks at its configuration and figures out whether it's a client or a server.

If it is a client, it:

Calls `onClientCreate()`.

Attempts to establish one or more TCP connections to other hosts.

For each connection it successfully establishes, creates a `TransportManagementDelegate` and passes it as the parameter to `onConnect(IORef:)`.

Goes to step 5 below.

If it is a server, it:

Calls `onServerCreate()`.

Starts listening for TCP connections from other hosts.

For each connection it receives and accepts, creates a `TransportManagementDelegate` and passes it as the parameter to `onAccept(IORef:)`.

Goes to step 5 below.

It enters a "connected" state where:

After it receives data from the connection, it allocates an empty buffer and passes it to `onReceive(buffer:bufSize:)`. `onReceive(buffer:bufSize:)` reads bytes from the network from [where?], interpreting TLS protocol messages and extracting whatever plaintext is present in them, using state from [where?] to distinguish between this connection and other simultaneous connections. `onReceive(buffer:bufSize:)` then writes the extracted plaintext to the buffer and returns.

Before it sends data to the connection, it puts that data in a buffer and passes it to `onSend(buffer:bufSize:)`. `onSend(buffer:bufSize:)` processes the data, converting it to ciphertext and adding TLS protocol messages, using state from [where?] to distinguish between this connection and other simultaneous connections. `onSend(buffer:bufSize:)` then [does what?] with the TLS protocol data to cause it to be sent and returns. `onSend(buffer:bufSize:)` can indicate that the connection should be closed by returning 0.

If the connection is closed. either because the peer closed it or because `onSend(buffer:bufSize:)` indicated it should be closed, it calls `onDestroy()`. `onDestroy()` tears down the SSL engine's state for the connection, using state from [where?] to distinguish between this connection and other simultaneous connections.

Eventually, the transport manager may stop listening or shut down entirely. The TLSService gets no signal that this has happened.

First of all, if any of the above is inaccurate, please correct me.

Assuming I haven't misunderstood any of the above in a material way: As you can see, there are a number of holes in my understanding. I would like your help in filling them in.

Since this is to be used for a server, I assume that the transport manager may have several TLS connections open to different clients at the same time. However, I don't see a means for the TLSService to know which of these connections the transport manager is currently talking about. Only the `onAccept(IORef:)` and `onConnect(IORef:)` calls are passed some kind of connection object, so how do `onReceive(buffer:bufSize:)`, `onSend(buffer:bufSize:)`, and `onDestroy()` know which connection is experiencing the event in question?

There are a few possibilities I can think of, but none of them really fit my understanding of the design:

The transport manager makes sure that there is only one TLS connection open at a time—it accepts one connection, handles it from start to finish with the help of the TLSService, and then accepts the next connection and begins handling it. `onAccept(IORef:)` and `onConnection(IORef:)` should store the `IORef` parameter into a property on `self`, and `onSend`, `onReceive`, and `onDestroy` should use that property later.

The transport manager handles many TLS connections at a time. At the beginning of a connection, it copies the TLSService instance [how?] and uses the copy for that connection. `onAccept(IORef:)` and `onConnection(IORef:)` should store the `IORef` parameter into a property on `self`, and `onSend`, `onReceive`, and `onDestroy` should use that property later.

The transport manager handles many TLS connections at a time. At the beginning of a connection, it asks higher layers in the stack to make a new TLSService instance [how?] and uses the new instance for that connection. `onAccept(IORef:)` and `onConnection(IORef:)` should store the `IORef` parameter into a property on `self`, and `onSend`, `onReceive`, and `onDestroy` should use that property later.

The transport manager handles many TLS connections at a time, up to the number of TLSService instances provided by higher layers [how?] to use as a pool of TLSService instances. At the beginning of a connection, it chooses an unused TLSService instance from its pool and assigns it to the connection. `onAccept(IORef:)` and `onConnection(IORef:)` should store the `IORef` parameter into a property on `self`, and `onSend`, `onReceive`, and `onDestroy` should use that property later.

The transport manager handles many TLS connections at a time using a single TLSService instance. Thus, a TLSService instance may receive calls concerning many different connections, all interleaved with one another. `onAccept(IORef:)` and `onConnection(IORef:)` know which connection to use because it's passed in their `IORef` parameter; `onSend`, `onReceive`, and `onDestroy` know [how?].

How does `onReceive(buffer:bufSize:)` get the data that was received from the network? Does it call some not-yet-specified method on the `IORef` that fetches data to act upon, or does it use some other mechanism?

What does `onSend(buffer:bufSize:)` do with the data to cause it to be sent across the network? Does it call some not-yet-specified method on the `IORef` that sends data over the network, or does it use some other mechanism?

I think that, once I have these final questions locked down, I'll be able to understand what's being proposed here, and I can make more detailed suggestions about names and similar issues.

Thank you for your patience with me on this!

···

On Apr 3, 2017, at 12:40 PM, Gelareh Taban <gtaban@us.ibm.com> wrote:
4.1 - TLS service protocol

The TLS service protocol describes the methods that the transport layer calls to handle transport-level events for the TLS service object.

I have a bunch of questions about the design you're presenting, and I think many of them ultimately stem from not understanding some of the high-level aspects of the proposal. For instance:

> * What types conform to this protocol? From the diagram, it looks like there's a type for each "engine"—a SecureTransportService, an OpenSSLService, etc.—and each instance represents a particular configuration of that engine. So you create a WhateverTLSService, configure it, and then hand it off (through several layers) to the transport management layer, which calls methods on it to handle various events. The transport management layer then uses that one TLSService to handle many connections. Is that correct?

[g]
More or less. As long as the implementation conforms to the protocol, it can use whatever underlying security library it wants. This way we can have a plug and play architecture which allows the user to pick the security library implementation of its choice (eg. LibreSSL, OpenSSL, etc)

> * What is the lifecycle of a connection? Does the TLSService create them itself, does the transport management layer create them and hand them off, or does the transport management layer retain control over them from beginning to end?

[g] The connection life cycle is handled by levels higher than TLS. For all intents and purposes, the transport management layers owns a TLS service delegate which calls the appropriate TLS-related functionality at the right time (eg. at socket creation time, at connection time, etc).

> * You discuss the higher layers creating a TLSService object and caching it in a property, then ultimately handing it down to the transport management layer, which then attaches it to socket objects. But presumably you can have many socket objects, possibly simultaneously. Are they all served by a single TLSService instance, or by many? If they share a TLSService, how does the TLSService know which socket is talking to it at a given moment? If they have separate ones, how does the transport management layer acquire a new one when it needs it?

[g]
Each socket instance would have its own TLS service delegate -- this is important because each socket might have its own specific TLS channel properties.

> You mention that this proposal is very small in scope, and it's fine to describe some of these details in general ways. For instance, you don't need to describe the interface to a Swift socket or the transport layer in detail. But currently, the description of these surrounding systems is *so* vague that I'm struggling to assess this design.

Perhaps some of these details have been described in other documents or meetings; if so, they really need to be presented in this document, too.

[g] fair enough. The problem is that right now in the servers working group, we have not defined *any* of our interfaces. This is the first proposal and we are trying to only pin the things which this service requires.
In any case, if you think other things should be included, please let us know.

- onClientCreate
> Why do these methods all have "on" prefixes? I'm not totally sure I understand the intended usage here, but I see two possibilities:

> * These are imperative commands. `onAccept` says that the TLS engine should accept a connection, `onSend` means it should send some data, etc. In that case, these should not have any prefix—they should just be `accept`, `send`, etc.

> * These are essentially delegate methods notifying the TLS engine of an event. `onAccept` says that the system has accepted a connection and the TLS engine should do what it needs to do with it, `onSend` means the system is about to send data and it needs the TLS engine to modify it, etc. If so, Swift APIs more often use words like `should`, `will`, or `did` than `on`, particularly since they're more precise about the timing of the delegate method compared to the actual event.

> In either case, I don't think "on" is the best naming for these. It needlessly bucks platform conventions.

[g]
The latter description is the intended. Your points are fair and perhaps we can modify the delegate method names to, perhaps:

onClientCreate --> didClientCreate
onServerCreate --> didServerCreate
onDestroy --> willDestroy
onAccept --> didAccept(on connection: TransportManagementDelegate)
onConnect --> didConnect(on connection: TransportManagementDelegate)
onSend --> willSend(with data: UnsafeRawPointer, dataSize: Int))
onReceive --> willReceive(with data: UnsafeRawPointer, dataSize: Int))
This will be called when a client I/O connection is created and appropriate TLS connection needs to be configured, including context creation, the handshake and connection verification.

This is a client only method.

///
/// Setup the contexts and process the TLSService configurations (certificates, etc)
///

func onClientCreate() throws
- onServerCreate

This will be called when a server I/O connection is created and appropriate TLS connection needs to be setup, including context creation, the handshake and connection verification.

This is a server only method.

///
/// Setup the contexts and process the TLSService configurations (certificates, etc)
///

func onServerCreate() throws

> What are these methods supposed to do, exactly?

> * Do they put `self` into either a client state or a server state? If so, what happens if you call both, or neither, or call one twice? Would it be better to do this as part of initialization, or to have them make a client TLS object or server TLS object, or to require whatever code hands the TLSService to the TransportManager to pre-configure it as either client or server?

[g]
They setup the TLS contexts and process the certificates. They do put self in either a client or server state via associated parameter sets in OpenSSL and SecureTransport.
(I am pretty sure) if you call them multiple times, they simply over write the previous setting. But we can test this during implementation.

These methods are essentially initialization that gets called when the TransportManager decides it has enough information to set it as client or server.

> * Do they create a new instance that's either a client or a server? If so, how do they return it?
> * Do they configure something recently created as either client or server? If so, how do they access whatever they need to configure?

> Basically, what state are these supposed to operate upon?

[g]
Most of the functionality is the same for client or server except for setting the server/client flag/function in the underlying security library. The instance is stored in the delegate.

- onDestroy

This will be called when an I/O instance connection is closed and any remaining TLS context needs to be destroyed.

This is both a client and server method.

///
/// Destroy any remaining contexts
///
func onDestroy()
> Is this called at the end of each connection, or is it called once when the transport management layer is totally finished with the TLSService, or are these the same thing?

> If per-connection, how does it know which connection?

> If during destruction, should we just class-constrain and use `deinit` for this purpose?

[g] this is per-connection and the TLS service would keep a context for the connection.
So the transport manager has a delegate instance pointing to this TLS service instance which itself has a context.
- onAccept

This will be called once an I/O instance connection has been accepted, to setup the TLS connection, do the handshake and connection verification.

This is both a client and server method.

///
/// Processing on acceptance from a listening connection
///
///
/// - Parameter IORef: The connected I/O instance
///
func onAccept(IORef: TransportManagementDelegate) throws
> I take it the parameter is some sort of abstraction wrapping e.g. a socket. So why is it called a `TransportManagementDelegate`? Shouldn't its name include words like `Connection` or `Socket` or `IOHandle` or something?

[g] we can simply call it ConnectionDelegate. I was trying to be consistent in my terminology.

> Do we want the parameter to be labeled `IORef`? That's not very idiomatic; it doesn't read well or follow the Swift naming guidelines.

[g] great feedback. Thanks!

> You say this is for both clients and servers. When does a TLS client have a listening connection that it `accept`s connections on?

Is it called at different times or in different ways than `onServerCreate`?

[g] Bad copy/paste! for both onConnect and onAccept. They are obviously server and client specific.
- onConnect

This will be called once a socket connection has been made, to setup the TLS connection, do the handshake and connection verification.

This is both a client and server method.

///
/// Processing on connection to a listening connection
///
/// - Parameter connectionRef: The connected I/O instance
///
func onConnect(IORef: TransportManagementDelegate) throws
The same as above, with appropriate substitutions.
- onSend

This will be called when data is to be written to an I/O instance. The input data buffer is written to the TLS connection associated with that I/O instance.

This is both a client and server method.

///
/// Low level writer
///
/// - Parameters:
/// - buffer: Buffer pointer
/// - bufSize: Size of the buffer
///
/// - Returns the number of bytes written. Zero indicates TLS shutdown, less than zero indicates error.
///
func onSend(buffer: UnsafeRawPointer, bufSize: Int) throws -> Int
> Is there a reason you use an UnsafeRawPointer and a buffer size, instead of using an UnsafeRawBufferPointer which would encapsulate both?
[g] it was pointed our rightly by others that it's not good to have either of them :slight_smile: Right now, we are looking at have Data and more efficient versions.

> Why is shutdown indicated with zero, rather than the return value being Optional and being nil? Why are errors signaled with negative values instead of being thrown? (Or are you saying that negative returns are invalid? That's different from saying "indicates error".)

[g] This is actually implementation details which shouldnt have been included in this pitch in the first place.

> If a TLSService return less than `bufSize`, will the enclosing later try to.re-send the remaining data in subsequent calls?

This sounds like the TLS engine owns the network connection (at least by this point) and is responsible for writing to it. Does that mean `accept` and `connect` take ownership of the connection and hold on to it? If you have several different simultaneous connections, how do you know which connection this should write to? Or does a given TLSService only own one connection at a time? If so, does the transport management layer create a new TLSService instance for each connection? How? If each TLSService is bound to one connection, shouldn't it be created already knowing the connection it's going to use?

[g] I believe I have talked about the relationship between connections and TLS service.
BlueSSLService implements a very similar protocol and you can get more information on it here:
https://github.com/IBM-Swift/BlueSSLService/blob/master/Sources/SSLService.swift

- onReceive

This will be called when data is to be read from an I/O instance. Encrypted data is read from TLS connection associated with that I/O instance and decrypted and written to the buffer passed in.

This is both a client and server method.

///
/// Low level reader
///
/// - Parameters:
/// - buffer: Buffer pointer
/// - bufSize: Size of the buffer
///
/// - Returns the number of bytes read. Zero indicates TLS shutdown, less than zero indicates error.
///
func onReceive(buffer: UnsafeMutableRawPointer, bufSize: Int) throws -> Int
> If I understand correctly, `buffer` is an uninitialized memory region that the type should fill with data. Is that correct?

Otherwise, the same as above, with appropriate substitutions.
5 - Non-goals

This proposal:

1- DOES NOT describe the TLS service configuration, which includes information on certificate types, formats and chains, cipher suites, etc. We expect this to be specified in a future proposal.

2- DOES NOT describe the TLS service trust policies, which define trust and validation policies of the incoming connection. We expect this to be specified in a future proposal.

3- DOES NOT describe the interface between the TLS service and the transport layer and any dependencies. We expect this to be specified in a future proposal.
> I feel like #3 in particular really hurts this proposal. It's impossible to evaluate this without at least a general idea of how the TLS service and the transport layer communicate. It's okay to handwave the details—for instance, you could say "Type X represents a network connection, and has methods to read, write, and close it", without describing those methods in detail—but without at least an overview of how this will be used, it's very difficult to evaluate.

I think this is probably a good design that just isn't being explained very clearly. I hope you can clarify some of these points.

--
Brent Royal-Gordon
Architechies

<graycol.gif>Brent Royal-Gordon ---04/01/2017 01:15:32 AM---> On Mar 20, 2017, at 1:04 PM, Gelareh Taban via swift-server-dev <swift-server-dev@swift.org> wrote

From: Brent Royal-Gordon <brent@architechies.com>
To: Gelareh Taban/Austin/IBM@IBMUS
Cc: swift-server-dev@swift.org, Bill Abt/Cambridge/IBM@IBMUS
Date: 04/01/2017 01:15 AM
Subject: Re: [swift-server-dev] Draft proposal for TLS Service APIs (please review)

On Mar 20, 2017, at 1:04 PM, Gelareh Taban via swift-server-dev <swift-server-dev@swift.org <mailto:swift-server-dev@swift.org>> wrote:
4.1 - TLS service protocol

The TLS service protocol describes the methods that the transport layer calls to handle transport-level events for the TLS service object.

I have a bunch of questions about the design you're presenting, and I think many of them ultimately stem from not understanding some of the high-level aspects of the proposal. For instance:

* What types conform to this protocol? From the diagram, it looks like there's a type for each "engine"—a SecureTransportService, an OpenSSLService, etc.—and each instance represents a particular configuration of that engine. So you create a WhateverTLSService, configure it, and then hand it off (through several layers) to the transport management layer, which calls methods on it to handle various events. The transport management layer then uses that one TLSService to handle many connections. Is that correct?

* What is the lifecycle of a connection? Does the TLSService create them itself, does the transport management layer create them and hand them off, or does the transport management layer retain control over them from beginning to end?

* You discuss the higher layers creating a TLSService object and caching it in a property, then ultimately handing it down to the transport management layer, which then attaches it to socket objects. But presumably you can have many socket objects, possibly simultaneously. Are they all served by a single TLSService instance, or by many? If they share a TLSService, how does the TLSService know which socket is talking to it at a given moment? If they have separate ones, how does the transport management layer acquire a new one when it needs it?

You mention that this proposal is very small in scope, and it's fine to describe some of these details in general ways. For instance, you don't need to describe the interface to a Swift socket or the transport layer in detail. But currently, the description of these surrounding systems is *so* vague that I'm struggling to assess this design.

Perhaps some of these details have been described in other documents or meetings; if so, they really need to be presented in this document, too.
- onClientCreate
Why do these methods all have "on" prefixes? I'm not totally sure I understand the intended usage here, but I see two possibilities:

* These are imperative commands. `onAccept` says that the TLS engine should accept a connection, `onSend` means it should send some data, etc. In that case, these should not have any prefix—they should just be `accept`, `send`, etc.

* These are essentially delegate methods notifying the TLS engine of an event. `onAccept` says that the system has accepted a connection and the TLS engine should do what it needs to do with it, `onSend` means the system is about to send data and it needs the TLS engine to modify it, etc. If so, Swift APIs more often use words like `should`, `will`, or `did` than `on`, particularly since they're more precise about the timing of the delegate method compared to the actual event.

In either case, I don't think "on" is the best naming for these. It needlessly bucks platform conventions.
This will be called when a client I/O connection is created and appropriate TLS connection needs to be configured, including context creation, the handshake and connection verification.

This is a client only method.

///
/// Setup the contexts and process the TLSService configurations (certificates, etc)
///

func onClientCreate() throws
- onServerCreate

This will be called when a server I/O connection is created and appropriate TLS connection needs to be setup, including context creation, the handshake and connection verification.

This is a server only method.

///
/// Setup the contexts and process the TLSService configurations (certificates, etc)
///

func onServerCreate() throws

What are these methods supposed to do, exactly?

* Do they put `self` into either a client state or a server state? If so, what happens if you call both, or neither, or call one twice? Would it be better to do this as part of initialization, or to have them make a client TLS object or server TLS object, or to require whatever code hands the TLSService to the TransportManager to pre-configure it as either client or server?

* Do they create a new instance that's either a client or a server? If so, how do they return it?

* Do they configure something recently created as either client or server? If so, how do they access whatever they need to configure?

Basically, what state are these supposed to operate upon?
- onDestroy

This will be called when an I/O instance connection is closed and any remaining TLS context needs to be destroyed.

This is both a client and server method.

///
/// Destroy any remaining contexts
///
func onDestroy()
Is this called at the end of each connection, or is it called once when the transport management layer is totally finished with the TLSService, or are these the same thing?

If per-connection, how does it know which connection?

If during destruction, should we just class-constrain and use `deinit` for this purpose?
- onAccept

This will be called once an I/O instance connection has been accepted, to setup the TLS connection, do the handshake and connection verification.

This is both a client and server method.

///
/// Processing on acceptance from a listening connection
///
///
/// - Parameter IORef: The connected I/O instance
///
func onAccept(IORef: TransportManagementDelegate) throws
I take it the parameter is some sort of abstraction wrapping e.g. a socket. So why is it called a `TransportManagementDelegate`? Shouldn't its name include words like `Connection` or `Socket` or `IOHandle` or something?

Do we want the parameter to be labeled `IORef`? That's not very idiomatic; it doesn't read well or follow the Swift naming guidelines.

You say this is for both clients and servers. When does a TLS client have a listening connection that it `accept`s connections on?

Is it called at different times or in different ways than `onServerCreate`?
- onConnect

This will be called once a socket connection has been made, to setup the TLS connection, do the handshake and connection verification.

This is both a client and server method.

///
/// Processing on connection to a listening connection
///
/// - Parameter connectionRef: The connected I/O instance
///
func onConnect(IORef: TransportManagementDelegate) throws
The same as above, with appropriate substitutions.
- onSend

This will be called when data is to be written to an I/O instance. The input data buffer is written to the TLS connection associated with that I/O instance.

This is both a client and server method.

///
/// Low level writer
///
/// - Parameters:
/// - buffer: Buffer pointer
/// - bufSize: Size of the buffer
///
/// - Returns the number of bytes written. Zero indicates TLS shutdown, less than zero indicates error.
///
func onSend(buffer: UnsafeRawPointer, bufSize: Int) throws -> Int
Is there a reason you use an UnsafeRawPointer and a buffer size, instead of using an UnsafeRawBufferPointer which would encapsulate both?

Why is shutdown indicated with zero, rather than the return value being Optional and being nil? Why are errors signaled with negative values instead of being thrown? (Or are you saying that negative returns are invalid? That's different from saying "indicates error".)

If a TLSService return less than `bufSize`, will the enclosing later try to.re-send the remaining data in subsequent calls?

This sounds like the TLS engine owns the network connection (at least by this point) and is responsible for writing to it. Does that mean `accept` and `connect` take ownership of the connection and hold on to it? If you have several different simultaneous connections, how do you know which connection this should write to? Or does a given TLSService only own one connection at a time? If so, does the transport management layer create a new TLSService instance for each connection? How? If each TLSService is bound to one connection, shouldn't it be created already knowing the connection it's going to use?
- onReceive

This will be called when data is to be read from an I/O instance. Encrypted data is read from TLS connection associated with that I/O instance and decrypted and written to the buffer passed in.

This is both a client and server method.

///
/// Low level reader
///
/// - Parameters:
/// - buffer: Buffer pointer
/// - bufSize: Size of the buffer
///
/// - Returns the number of bytes read. Zero indicates TLS shutdown, less than zero indicates error.
///
func onReceive(buffer: UnsafeMutableRawPointer, bufSize: Int) throws -> Int
If I understand correctly, `buffer` is an uninitialized memory region that the type should fill with data. Is that correct?

Otherwise, the same as above, with appropriate substitutions.
5 - Non-goals

This proposal:

1- DOES NOT describe the TLS service configuration, which includes information on certificate types, formats and chains, cipher suites, etc. We expect this to be specified in a future proposal.

2- DOES NOT describe the TLS service trust policies, which define trust and validation policies of the incoming connection. We expect this to be specified in a future proposal.

3- DOES NOT describe the interface between the TLS service and the transport layer and any dependencies. We expect this to be specified in a future proposal.
I feel like #3 in particular really hurts this proposal. It's impossible to evaluate this without at least a general idea of how the TLS service and the transport layer communicate. It's okay to handwave the details—for instance, you could say "Type X represents a network connection, and has methods to read, write, and close it", without describing those methods in detail—but without at least an overview of how this will be used, it's very difficult to evaluate.

I think this is probably a good design that just isn't being explained very clearly. I hope you can clarify some of these points.

--
Brent Royal-Gordon
Architechies

--
Brent Royal-Gordon
Architechies


(Bill Abt) #15

I agree with Gelareh with regard to this. The TLSService should be the lowest layer and closest to the C API. Async I/O can be added at a higher level later on and still use TLSService. Additional read/write methods supporting different input/output types again, could be added at a higher level.. As the TLSService is currently designed, any number of separate higher level implementations can be built on top of it. I think this gives the most flexibility both now and in the future.

-Bill Abt

···

On Mar 23, 2017, at 12:53 PM, Helge Heß via swift-server-dev <swift-server-dev@swift.org> wrote:

Hi Gelareh,

I suppose what you have been saying about EAGAIN etc may work out. It would be really nice to actually try the API with an NIO consumer, did you do this already? Just to make sure and test out that we aren’t missing something.

Wrt the GCD channel stuff. Well, your design has that separate service object which is ‘used' by some ‘Socket’ class. With GCD in mind I would design the TLS support as a new custom channel which is just wrapping a GCD channel. It would provide the same API like GCD plus the necessary extras. Kinda like OpenSSL BIOs, just with async.

But this would tie the I/O to a GCD API style (not necessarily to GCD, you could factor out the channel API as a ’stream' protocol). Quite likely this is still not desirable for everyone.

I haven’t seen a proposal on how (byte) streams are going to work for Server Side Swift (i.e. how the API of the HTTP Request/Response body streams would look like). That kinda ties into this.

Summary: The service object as you proposed should be fine as a support wrapper used by higher level I/O objects. I guess it is the right way to go to support various I/O styles. Just wrap OpenSSL and the macOS thing. Do not add anything else.
But *if* we would get a proper (byte) streaming abstraction, I would prefer if a TLS stream would live on top of this (be a stream itself and wrap the stream representing the connection).

hh

On 23 Mar 2017, at 16:52, Gelareh Taban <gtaban@us.ibm.com> wrote:

Btw Helge, really interesting about the GCD channel APIs. How would your propose they be integrated? Would they replace or modify onSend/onReceive methods?

_______________________________________________
swift-server-dev mailing list
swift-server-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-server-dev


(Gelareh Taban) #16

Hi Helge,
sorry for the delayed response.

I suppose what you have been saying about EAGAIN etc may work out.
It would be really nice to actually try the API with an NIO consumer, did

you do this already?

Just to make sure and test out that we aren’t missing something.

What is an NIO consumer? I see that Java has New I/O (
https://en.wikipedia.org/wiki/New_I/O_(Java)) - how would this work in the
context of Swift?

(that should also answer your question :wink:

If you are talking about non-blocking by any chance, we have an
implementation of this in BlueSSLService (
https://github.com/IBM-Swift/BlueSSLService) which interfaces with a socket
management layer (BlueSocket - https://github.com/IBM-Swift/BlueSocket).

Ideally we want to abstract away the transport layer and that's actually
the next protocol that I referred to in the original proposal that we want
to tackle and you have started alluding to in the next paragraphs.

Wrt the GCD channel stuff. Well, your design has that separate service

object which is ‘used' by some ‘Socket’ class.

With GCD in mind I would design the TLS support as a new custom channel

which is just wrapping a GCD channel.

It would provide the same API like GCD plus the necessary extras. Kinda

like OpenSSL BIOs, just with async.

But this would tie the I/O to a GCD API style (not necessarily to GCD,

you could factor out the channel API

as a ’stream' protocol). Quite likely this is still not desirable for

everyone.

Really interesting. Dispatch IO has:

https://developer.apple.com/reference/dispatch/dispatchio
class func read(fromFileDescriptor: Int32, maxLength: Int, runningHandlerOn
queue: DispatchQueue, handler: @escaping (DispatchData, Int32) -> Void)
class func write(toFileDescriptor: Int32, data: DispatchData,
runningHandlerOn queue: DispatchQueue, handler: @escaping (DispatchData?,
Int32) -> Void)

Are you talking about these APIs? How would it work with stream?

I haven’t seen a proposal on how (byte) streams are going to work for

Server Side Swift

(i.e. how the API of the HTTP Request/Response body streams would look

like). That kinda ties into this.

Summary: The service object as you proposed should be fine as a support

wrapper used by higher level

I/O objects. I guess it is the right way to go to support various I/O

styles. Just wrap OpenSSL and

the macOS thing. Do not add anything else.
But *if* we would get a proper (byte) streaming abstraction, I would

prefer if a TLS stream would

live on top of this (be a stream itself and wrap the stream representing

the connection).

I agree about the stream abstraction. In order for us to completely
abstract away the transport layer, streams are ideal and preferred.

What is tricky here is that Streams in (mac) Foundation has TLS support via
property keys.

https://developer.apple.com/reference/foundation/stream

Most probably they are using the SecureTransport APIs and passing the
stream by reference while implementing the TLS read/write operations from
the stream via callsbacks.

So the question would be, do we only provide APIs that stream can invoke on
Linux (which probably take in file descriptors with a transport type) or do
we provide an extended set of APIs that Stream can use, while we also have
Stream based transport abstraction? Should stream have multiple TLS
supports?

cheers,
gelareh

···

From: Helge Heß via swift-server-dev <swift-server-dev@swift.org>
To: swift-server-dev <swift-server-dev@swift.org>
Date: 03/23/2017 11:53 AM
Subject: Re: [swift-server-dev] Draft proposal for TLS Service APIs
            (pleasereview)
Sent by: swift-server-dev-bounces@swift.org

Hi Gelareh,

I suppose what you have been saying about EAGAIN etc may work out. It would
be really nice to actually try the API with an NIO consumer, did you do
this already? Just to make sure and test out that we aren’t missing
something.

Wrt the GCD channel stuff. Well, your design has that separate service
object which is ‘used' by some ‘Socket’ class. With GCD in mind I would
design the TLS support as a new custom channel which is just wrapping a GCD
channel. It would provide the same API like GCD plus the necessary extras.
Kinda like OpenSSL BIOs, just with async.

But this would tie the I/O to a GCD API style (not necessarily to GCD, you
could factor out the channel API as a ’stream' protocol). Quite likely this
is still not desirable for everyone.

I haven’t seen a proposal on how (byte) streams are going to work for
Server Side Swift (i.e. how the API of the HTTP Request/Response body
streams would look like). That kinda ties into this.

Summary: The service object as you proposed should be fine as a support
wrapper used by higher level I/O objects. I guess it is the right way to go
to support various I/O styles. Just wrap OpenSSL and the macOS thing. Do
not add anything else.
But *if* we would get a proper (byte) streaming abstraction, I would prefer
if a TLS stream would live on top of this (be a stream itself and wrap the
stream representing the connection).

hh

On 23 Mar 2017, at 16:52, Gelareh Taban <gtaban@us.ibm.com> wrote:

Btw Helge, really interesting about the GCD channel APIs. How would your

propose they be integrated? Would they replace or modify onSend/onReceive
methods?

[attachment "signature.asc" deleted by Gelareh Taban/Austin/IBM]
_______________________________________________
swift-server-dev mailing list
swift-server-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-server-dev


(Helge Heß) #17

? Less copying and/or using a single system call for a buffer.

If I have a set of blocks I want to write, I would either have to copy them into a big one just to pass them over to your API, or I would have to call your write API multiple times, which could then result in multiple write() calls.

I guess you could just use DispatchData. I’m not sure whether this is too expensive for some reason (because you may need to have to construct them, etc), or just fine.

hh

P.S.: The DispatchData.Deallocator enum has no .nofree option. Can someone please fix that? Thanks. :slight_smile:

···

On 30 Mar 2017, at 16:49, Gelareh Taban <gtaban@us.ibm.com> wrote:

> > Like the followings:
> > func tlsWrite(buffer: Collection<UInt8>) throws
> > func tlsWritev(vectors: Collection<iovec>)
> >
> > If it is possible it will be very helpful since
> > 1) it is very swifty and user friendly
> > 2) vectorIO could significantly reduce the overhead to merge fragments of bytes into a single buffer.
>
> +1 on vectors.
>

+1 on need to make it more Swifty.

What is the advantage of using vectored IO for TLS service?


(Georgios Moschovitis) #18

There are 2 hard problems in computer science: cache invalidation, **naming things**, and off-by-1 errors. -- Leon Bambrick

:slight_smile:

> > * These are essentially delegate methods notifying the TLS engine of an event.
> > `onAccept` says that the system has accepted a connection and the TLS engine should
> > do what it needs to do with it, `onSend` means the system is about to send data and
> > it needs the TLS engine to modify it, etc. If so, Swift APIs more often use words like
> > `should`, `will`, or `did` than `on`, particularly since they're more precise about the
> > timing of the delegate method compared to the actual event.
>
> +1 onXXX methods look definitely non-Swifty to me.

Fair enough. What do you recommend?

what about:

onAccept -> didAccept
onDestroy -> didDestroy / wasDestroyed
onClientCreate -> clientWasCreated
onServerCreate -> serverWasCreated

etc, you get the point.

I agree that Service is not a particularly good name but that was the best I could think of. I liked Engine but (1) we are not implementing anything, just overlaying and (2) I didnt want any confusion with with the SSL Engine of OpenSSL.

So again, I would be interested in your recommendations!

(1) `Service` has the same problem
      if it’s just overlaying, maybe you should name it ‘TLSOverlay’, ‘TLSMiddleware’ or something.
      I still think, `Engine` is a pretty good name though, but will keep thinking for better ones.

···

On 3 Apr 2017, at 5:33 PM, Gelareh Taban <gtaban@us.ibm.com> wrote:


(Gelareh Taban) #19

Hi Brent,

I have some answers in line.

I think perhaps there is misunderstanding about the role of
TransportManager (perhaps naming is the problem after all!). This is not a
singleton type of manager - rather it is a per connection manager and it
"manages" for example the system socket calls. An instance of transport
manager handles one connection. An instance of transport manager has one
TLS delegate associated with that connection. Therefore, the delegate is
associated with only one connection at a time.

If we assume the above, then it is not hard to see that once a context is
created for a specific TLS connection, and stored by the TLS service
object, the context can be passed between the various OpenSSL or Secure
Transport calls.
With OpenSSL, we set the connection fd say to the socket pointer.
In Secure Transport, we implement connection read, write callback functions
that read/write from the socket.

Hopefully that makes more sense!

gelareh

···

From: Brent Royal-Gordon <brent@architechies.com>
To: Gelareh Taban/Austin/IBM@IBMUS
Cc: Bill Abt/Cambridge/IBM@IBMUS, swift-server-dev@swift.org
Date: 04/04/2017 07:06 AM
Subject: Re: [swift-server-dev] Draft proposal for TLS Service APIs
            (please review)

      On Apr 3, 2017, at 12:40 PM, Gelareh Taban <gtaban@us.ibm.com> wrote:

      Hi Brent,
      The answers are in line.
      gelareh

Thank you for your detailed answers. I'm going to try to synthesize your
answers about the lifecycle into a single description of how the TLSService
is used. For clarity, I'll use the names from the proposal, even when we've
discussed changing the names.

      Some high-level part of the application configures a
      TLSService-conforming instance and conveys it through several layers
      to the transport management layer. At this point, the TLSService
      knows the certificates, domains, security settings, etc. it should
      use, but does not know whether it will be handling the client side or
      the server side of a connection.

      Time passes. Eventually, the transport manager looks at its
      configuration and figures out whether it's a client or a server.

      If it is a client, it:

            Calls `onClientCreate()`.

            Attempts to establish one or more TCP connections to other
            hosts.

            For each connection it successfully establishes, creates a
            `TransportManagementDelegate` and passes it as the parameter to
            `onConnect(IORef:)`.

            Goes to step 5 below.

      If it is a server, it:

            Calls `onServerCreate()`.

            Starts listening for TCP connections from other hosts.

            For each connection it receives and accepts, creates a
            `TransportManagementDelegate` and passes it as the parameter to
            `onAccept(IORef:)`.

            Goes to step 5 below.

      It enters a "connected" state where:

            After it receives data from the connection, it allocates an
            empty buffer and passes it to `onReceive(buffer:bufSize:)`.
            `onReceive(buffer:bufSize:)` reads bytes from the network from
            [where?], interpreting TLS protocol messages and extracting
            whatever plaintext is present in them, using state from
            [where?] to distinguish between this connection and other
            simultaneous connections. `onReceive(buffer:bufSize:)` then
            writes the extracted plaintext to the buffer and returns.

            [g] Assume IORef is a socket pointer. If using OpenSSL, the SSL
            socket is created using SSL_set_fd() and from then on,
            onReceive, once data is received on the socket, it is extracted
            and decrypted from the socket by OpenSSL using SSL_read and
            copied into buffer. This is all handled by OpenSSL, for a given
            context that is passed between the various OpenSSL calls for a
            specific connection.

            Before it sends data to the connection, it puts that data in a
            buffer and passes it to `onSend(buffer:bufSize:)`. `onSend
            (buffer:bufSize:)` processes the data, converting it to
            ciphertext and adding TLS protocol messages, using state from
            [where?] to distinguish between this connection and other
            simultaneous connections. `onSend(buffer:bufSize:)` then [does
            what?] with the TLS protocol data to cause it to be sent and
            returns. `onSend(buffer:bufSize:)` can indicate that the
            connection should be closed by returning 0.

            [g] SImilar to above, once a given context is created
            associated with an SSL socket, then SSL_write would obtain
            data from buffer, and encrypt and pass to connection to send.

            If the connection is closed. either because the peer closed it
            or because `onSend(buffer:bufSize:)` indicated it should be
            closed, it calls `onDestroy()`. `onDestroy()` tears down the
            SSL engine's state for the connection, using state from
            [where?] to distinguish between this connection and other
            simultaneous connections.

      Eventually, the transport manager may stop listening or shut down
      entirely. The TLSService gets no signal that this has happened.

First of all, if any of the above is inaccurate, please correct me.

Assuming I haven't misunderstood any of the above in a material way: As you
can see, there are a number of holes in my understanding. I would like your
help in filling them in.

      Since this is to be used for a server, I assume that the transport
      manager may have several TLS connections open to different clients at
      the same time. However, I don't see a means for the TLSService to
      know which of these connections the transport manager is currently
      talking about. Only the `onAccept(IORef:)` and `onConnect(IORef:)`
      calls are passed some kind of connection object, so how do `onReceive
      (buffer:bufSize:)`, `onSend(buffer:bufSize:)`, and `onDestroy()` know
      which connection is experiencing the event in question?

      [g] once socket is passed, a context is created and stored by that
      instance of TLS service which is then passed to other
      OpenSSL/SecureTranport calls.
      https://github.com/IBM-Swift/BlueSSLService/blob/master/Sources/SSLService.swift#L308
        for OpenSSL

      There are a few possibilities I can think of, but none of them really
      fit my understanding of the design:

            The transport manager makes sure that there is only one TLS
            connection open at a time—it accepts one connection, handles it
            from start to finish with the help of the TLSService, and then
            accepts the next connection and begins handling it. `onAccept
            (IORef:)` and `onConnection(IORef:)` should store the `IORef`
            parameter into a property on `self`, and `onSend`, `onReceive`,
            and `onDestroy` should use that property later.

            The transport manager handles many TLS connections at a time.
            At the beginning of a connection, it copies the TLSService
            instance [how?] and uses the copy for that connection.
            `onAccept(IORef:)` and `onConnection(IORef:)` should store the
            `IORef` parameter into a property on `self`, and `onSend`,
            `onReceive`, and `onDestroy` should use that property later.

            The transport manager handles many TLS connections at a time.
            At the beginning of a connection, it asks higher layers in the
            stack to make a new TLSService instance [how?] and uses the new
            instance for that connection. `onAccept(IORef:)` and
            `onConnection(IORef:)` should store the `IORef` parameter into
            a property on `self`, and `onSend`, `onReceive`, and
            `onDestroy` should use that property later.

            The transport manager handles many TLS connections at a time,
            up to the number of TLSService instances provided by higher
            layers [how?] to use as a pool of TLSService instances. At the
            beginning of a connection, it chooses an unused TLSService
            instance from its pool and assigns it to the connection.
            `onAccept(IORef:)` and `onConnection(IORef:)` should store the
            `IORef` parameter into a property on `self`, and `onSend`,
            `onReceive`, and `onDestroy` should use that property later.

            The transport manager handles many TLS connections at a time
            using a single TLSService instance. Thus, a TLSService instance
            may receive calls concerning many different connections, all
            interleaved with one another. `onAccept(IORef:)` and
            `onConnection(IORef:)` know which connection to use because
            it's passed in their `IORef` parameter; `onSend`, `onReceive`,
            and `onDestroy` know [how?].

      How does `onReceive(buffer:bufSize:)` get the data that was received
      from the network? Does it call some not-yet-specified method on the
      `IORef` that fetches data to act upon, or does it use some other
      mechanism?

      What does `onSend(buffer:bufSize:)` do with the data to cause it to
      be sent across the network? Does it call some not-yet-specified
      method on the `IORef` that sends data over the network, or does it
      use some other mechanism?

I think that, once I have these final questions locked down, I'll be able
to understand what's being proposed here, and I can make more detailed
suggestions about names and similar issues.

Thank you for your patience with me on this!

                  4.1 - TLS service protocol

                  The TLS service protocol describes the methods that the
                  transport layer calls to handle transport-level events
                  for the TLS service object.

      I have a bunch of questions about the design you're presenting, and I
      think many of them ultimately stem from not understanding some of the
      high-level aspects of the proposal. For instance:

      > * What types conform to this protocol? From the diagram, it looks
      like there's a type for each "engine"—a SecureTransportService, an
      OpenSSLService, etc.—and each instance represents a particular
      configuration of that engine. So you create a WhateverTLSService,
      configure it, and then hand it off (through several layers) to the
      transport management layer, which calls methods on it to handle
      various events. The transport management layer then uses that one
      TLSService to handle many connections. Is that correct?

      [g]
      More or less. As long as the implementation conforms to the protocol,
      it can use whatever underlying security library it wants. This way we
      can have a plug and play architecture which allows the user to pick
      the security library implementation of its choice (eg. LibreSSL,
      OpenSSL, etc)

      > * What is the lifecycle of a connection? Does the TLSService create
      them itself, does the transport management layer create them and hand
      them off, or does the transport management layer retain control over
      them from beginning to end?

      [g] The connection life cycle is handled by levels higher than TLS.
      For all intents and purposes, the transport management layers owns a
      TLS service delegate which calls the appropriate TLS-related
      functionality at the right time (eg. at socket creation time, at
      connection time, etc).

      > * You discuss the higher layers creating a TLSService object and
      caching it in a property, then ultimately handing it down to the
      transport management layer, which then attaches it to socket objects.
      But presumably you can have many socket objects, possibly
      simultaneously. Are they all served by a single TLSService instance,
      or by many? If they share a TLSService, how does the TLSService know
      which socket is talking to it at a given moment? If they have
      separate ones, how does the transport management layer acquire a new
      one when it needs it?

      [g]
      Each socket instance would have its own TLS service delegate -- this
      is important because each socket might have its own specific TLS
      channel properties.

      > You mention that this proposal is very small in scope, and it's
      fine to describe some of these details in general ways. For instance,
      you don't need to describe the interface to a Swift socket or the
      transport layer in detail. But currently, the description of these
      surrounding systems is *so* vague that I'm struggling to assess this
      design.

      Perhaps some of these details have been described in other documents
      or meetings; if so, they really need to be presented in this
      document, too.

      [g] fair enough. The problem is that right now in the servers working
      group, we have not defined *any* of our interfaces. This is the first
      proposal and we are trying to only pin the things which this service
      requires.
      In any case, if you think other things should be included, please let
      us know.

                  - onClientCreate
      > Why do these methods all have "on" prefixes? I'm not totally sure I
      understand the intended usage here, but I see two possibilities:

      > * These are imperative commands. `onAccept` says that the TLS
      engine should accept a connection, `onSend` means it should send some
      data, etc. In that case, these should not have any prefix—they should
      just be `accept`, `send`, etc.

      > * These are essentially delegate methods notifying the TLS engine
      of an event. `onAccept` says that the system has accepted a
      connection and the TLS engine should do what it needs to do with it,
      `onSend` means the system is about to send data and it needs the TLS
      engine to modify it, etc. If so, Swift APIs more often use words like
      `should`, `will`, or `did` than `on`, particularly since they're more
      precise about the timing of the delegate method compared to the
      actual event.

      > In either case, I don't think "on" is the best naming for these. It
      needlessly bucks platform conventions.

      [g]
      The latter description is the intended. Your points are fair and
      perhaps we can modify the delegate method names to, perhaps:

      onClientCreate --> didClientCreate
      onServerCreate --> didServerCreate
      onDestroy --> willDestroy
      onAccept --> didAccept(on connection: TransportManagementDelegate)
      onConnect --> didConnect(on connection: TransportManagementDelegate)
      onSend --> willSend(with data: UnsafeRawPointer, dataSize: Int))
      onReceive --> willReceive(with data: UnsafeRawPointer, dataSize:
      Int))
                  This will be called when a client I/O connection is
                  created and appropriate TLS connection needs to be
                  configured, including context creation, the handshake and
                  connection verification.

                  This is a client only method.

                  ///
                  /// Setup the contexts and process the TLSService
                  configurations (certificates, etc)
                  ///

                  func onClientCreate() throws

                  - onServerCreate

                  This will be called when a server I/O connection is
                  created and appropriate TLS connection needs to be setup,
                  including context creation, the handshake and connection
                  verification.

                  This is a server only method.

                  ///
                  /// Setup the contexts and process the TLSService
                  configurations (certificates, etc)
                  ///

                  func onServerCreate() throws

      > What are these methods supposed to do, exactly?

      > * Do they put `self` into either a client state or a server state?
      If so, what happens if you call both, or neither, or call one twice?
      Would it be better to do this as part of initialization, or to have
      them make a client TLS object or server TLS object, or to require
      whatever code hands the TLSService to the TransportManager to
      pre-configure it as either client or server?

      [g]
      They setup the TLS contexts and process the certificates. They do put
      self in either a client or server state via associated parameter sets
      in OpenSSL and SecureTransport.
      (I am pretty sure) if you call them multiple times, they simply over
      write the previous setting. But we can test this during
      implementation.

      These methods are essentially initialization that gets called when
      the TransportManager decides it has enough information to set it as
      client or server.

      > * Do they create a new instance that's either a client or a server?
      If so, how do they return it?
      > * Do they configure something recently created as either client or
      server? If so, how do they access whatever they need to configure?

      > Basically, what state are these supposed to operate upon?

      [g]
      Most of the functionality is the same for client or server except for
      setting the server/client flag/function in the underlying security
      library. The instance is stored in the delegate.

                  - onDestroy

                  This will be called when an I/O instance connection is
                  closed and any remaining TLS context needs to be
                  destroyed.

                  This is both a client and server method.

                  ///
                  /// Destroy any remaining contexts
                  ///
                  func onDestroy()
      > Is this called at the end of each connection, or is it called once
      when the transport management layer is totally finished with the
      TLSService, or are these the same thing?

      > If per-connection, how does it know which connection?

      > If during destruction, should we just class-constrain and use
      `deinit` for this purpose?

      [g] this is per-connection and the TLS service would keep a context
      for the connection.
      So the transport manager has a delegate instance pointing to this TLS
      service instance which itself has a context.
                  - onAccept

                  This will be called once an I/O instance connection has
                  been accepted, to setup the TLS connection, do the
                  handshake and connection verification.

                  This is both a client and server method.

                  ///
                  /// Processing on acceptance from a listening connection
                  ///
                  ///
                  /// - Parameter IORef: The connected I/O instance
                  ///
                  func onAccept(IORef: TransportManagementDelegate) throws
      > I take it the parameter is some sort of abstraction wrapping e.g. a
      socket. So why is it called a `TransportManagementDelegate`?
      Shouldn't its name include words like `Connection` or `Socket` or
      `IOHandle` or something?

      [g] we can simply call it ConnectionDelegate. I was trying to be
      consistent in my terminology.

      > Do we want the parameter to be labeled `IORef`? That's not very
      idiomatic; it doesn't read well or follow the Swift naming
      guidelines.

      [g] great feedback. Thanks!

      > You say this is for both clients and servers. When does a TLS
      client have a listening connection that it `accept`s connections on?

      Is it called at different times or in different ways than
      `onServerCreate`?

      [g] Bad copy/paste! for both onConnect and onAccept. They are
      obviously server and client specific.
                  - onConnect

                  This will be called once a socket connection has been
                  made, to setup the TLS connection, do the handshake and
                  connection verification.

                  This is both a client and server method.

                  ///
                  /// Processing on connection to a listening connection
                  ///
                  /// - Parameter connectionRef: The connected I/O instance
                  ///
                  func onConnect(IORef: TransportManagementDelegate) throws
      The same as above, with appropriate substitutions.
                  - onSend

                  This will be called when data is to be written to an I/O
                  instance. The input data buffer is written to the TLS
                  connection associated with that I/O instance.

                  This is both a client and server method.

                  ///
                  /// Low level writer
                  ///
                  /// - Parameters:
                  /// - buffer: Buffer pointer
                  /// - bufSize: Size of the buffer
                  ///
                  /// - Returns the number of bytes written. Zero indicates
                  TLS shutdown, less than zero indicates error.
                  ///
                  func onSend(buffer: UnsafeRawPointer, bufSize: Int)
                  throws -> Int
      > Is there a reason you use an UnsafeRawPointer and a buffer size,
      instead of using an UnsafeRawBufferPointer which would encapsulate
      both?

      [g] it was pointed our rightly by others that it's not good to have
      either of them :slight_smile: Right now, we are looking at have Data and more
      efficient versions.

      > Why is shutdown indicated with zero, rather than the return value
      being Optional and being nil? Why are errors signaled with negative
      values instead of being thrown? (Or are you saying that negative
      returns are invalid? That's different from saying "indicates error".)

      [g] This is actually implementation details which shouldnt have been
      included in this pitch in the first place.

      > If a TLSService return less than `bufSize`, will the enclosing
      later try to.re-send the remaining data in subsequent calls?

      This sounds like the TLS engine owns the network connection (at least
      by this point) and is responsible for writing to it. Does that mean
      `accept` and `connect` take ownership of the connection and hold on
      to it? If you have several different simultaneous connections, how do
      you know which connection this should write to? Or does a given
      TLSService only own one connection at a time? If so, does the
      transport management layer create a new TLSService instance for each
      connection? How? If each TLSService is bound to one connection,
      shouldn't it be created already knowing the connection it's going to
      use?

      [g] I believe I have talked about the relationship between
      connections and TLS service.
      BlueSSLService implements a very similar protocol and you can get
      more information on it here:
      https://github.com/IBM-Swift/BlueSSLService/blob/master/Sources/SSLService.swift

                  - onReceive

                  This will be called when data is to be read from an I/O
                  instance. Encrypted data is read from TLS connection
                  associated with that I/O instance and decrypted and
                  written to the buffer passed in.

                  This is both a client and server method.

                  ///
                  /// Low level reader
                  ///
                  /// - Parameters:
                  /// - buffer: Buffer pointer
                  /// - bufSize: Size of the buffer
                  ///
                  /// - Returns the number of bytes read. Zero indicates
                  TLS shutdown, less than zero indicates error.
                  ///
                  func onReceive(buffer: UnsafeMutableRawPointer, bufSize:
                  Int) throws -> Int
      > If I understand correctly, `buffer` is an uninitialized memory
      region that the type should fill with data. Is that correct?

      Otherwise, the same as above, with appropriate substitutions.
                  5 - Non-goals

                  This proposal:

                  1- DOES NOT describe the TLS service configuration, which
                  includes information on certificate types, formats and
                  chains, cipher suites, etc. We expect this to be
                  specified in a future proposal.

                  2- DOES NOT describe the TLS service trust policies,
                  which define trust and validation policies of the
                  incoming connection. We expect this to be specified in a
                  future proposal.

                  3- DOES NOT describe the interface between the TLS
                  service and the transport layer and any dependencies. We
                  expect this to be specified in a future proposal.
      > I feel like #3 in particular really hurts this proposal. It's
      impossible to evaluate this without at least a general idea of how
      the TLS service and the transport layer communicate. It's okay to
      handwave the details—for instance, you could say "Type X represents a
      network connection, and has methods to read, write, and close it",
      without describing those methods in detail—but without at least an
      overview of how this will be used, it's very difficult to evaluate.

      I think this is probably a good design that just isn't being
      explained very clearly. I hope you can clarify some of these points.

      --
      Brent Royal-Gordon
      Architechies

      <graycol.gif>Brent Royal-Gordon ---04/01/2017 01:15:32 AM---> On Mar
      20, 2017, at 1:04 PM, Gelareh Taban via swift-server-dev <
      swift-server-dev@swift.org> wrote

      From: Brent Royal-Gordon <brent@architechies.com>
      To: Gelareh Taban/Austin/IBM@IBMUS
      Cc: swift-server-dev@swift.org, Bill Abt/Cambridge/IBM@IBMUS
      Date: 04/01/2017 01:15 AM
      Subject: Re: [swift-server-dev] Draft proposal for TLS Service APIs
      (please review)

                  On Mar 20, 2017, at 1:04 PM, Gelareh Taban via swift-server-dev <swift-server-dev@swift.org> wrote:

                  4.1 - TLS service protocol

                  The TLS service protocol describes the methods that the
                  transport layer calls to handle transport-level events
                  for the TLS service object.

      I have a bunch of questions about the design you're presenting, and I
      think many of them ultimately stem from not understanding some of the
      high-level aspects of the proposal. For instance:

      * What types conform to this protocol? From the diagram, it looks
      like there's a type for each "engine"—a SecureTransportService, an
      OpenSSLService, etc.—and each instance represents a particular
      configuration of that engine. So you create a WhateverTLSService,
      configure it, and then hand it off (through several layers) to the
      transport management layer, which calls methods on it to handle
      various events. The transport management layer then uses that one
      TLSService to handle many connections. Is that correct?

      * What is the lifecycle of a connection? Does the TLSService create
      them itself, does the transport management layer create them and hand
      them off, or does the transport management layer retain control over
      them from beginning to end?

      * You discuss the higher layers creating a TLSService object and
      caching it in a property, then ultimately handing it down to the
      transport management layer, which then attaches it to socket objects.
      But presumably you can have many socket objects, possibly
      simultaneously. Are they all served by a single TLSService instance,
      or by many? If they share a TLSService, how does the TLSService know
      which socket is talking to it at a given moment? If they have
      separate ones, how does the transport management layer acquire a new
      one when it needs it?

      You mention that this proposal is very small in scope, and it's fine
      to describe some of these details in general ways. For instance, you
      don't need to describe the interface to a Swift socket or the
      transport layer in detail. But currently, the description of these
      surrounding systems is *so* vague that I'm struggling to assess this
      design.

      Perhaps some of these details have been described in other documents
      or meetings; if so, they really need to be presented in this
      document, too.
                  - onClientCreate
      Why do these methods all have "on" prefixes? I'm not totally sure I
      understand the intended usage here, but I see two possibilities:

      * These are imperative commands. `onAccept` says that the TLS engine
      should accept a connection, `onSend` means it should send some data,
      etc. In that case, these should not have any prefix—they should just
      be `accept`, `send`, etc.

      * These are essentially delegate methods notifying the TLS engine of
      an event. `onAccept` says that the system has accepted a connection
      and the TLS engine should do what it needs to do with it, `onSend`
      means the system is about to send data and it needs the TLS engine to
      modify it, etc. If so, Swift APIs more often use words like `should`,
      `will`, or `did` than `on`, particularly since they're more precise
      about the timing of the delegate method compared to the actual event.

      In either case, I don't think "on" is the best naming for these. It
      needlessly bucks platform conventions.
                  This will be called when a client I/O connection is
                  created and appropriate TLS connection needs to be
                  configured, including context creation, the handshake and
                  connection verification.

                  This is a client only method.

                  ///
                  /// Setup the contexts and process the TLSService
                  configurations (certificates, etc)
                  ///

                  func onClientCreate() throws

                  - onServerCreate

                  This will be called when a server I/O connection is
                  created and appropriate TLS connection needs to be setup,
                  including context creation, the handshake and connection
                  verification.

                  This is a server only method.

                  ///
                  /// Setup the contexts and process the TLSService
                  configurations (certificates, etc)
                  ///

                  func onServerCreate() throws

      What are these methods supposed to do, exactly?

      * Do they put `self` into either a client state or a server state? If
      so, what happens if you call both, or neither, or call one twice?
      Would it be better to do this as part of initialization, or to have
      them make a client TLS object or server TLS object, or to require
      whatever code hands the TLSService to the TransportManager to
      pre-configure it as either client or server?

      * Do they create a new instance that's either a client or a server?
      If so, how do they return it?

      * Do they configure something recently created as either client or
      server? If so, how do they access whatever they need to configure?


(Helge Heß) #20

> I suppose what you have been saying about EAGAIN etc may work out.
> It would be really nice to actually try the API with an NIO consumer, did you do this already?
> Just to make sure and test out that we aren’t missing something.

What is an NIO consumer? I see that Java has New I/O (https://en.wikipedia.org/wiki/New_I/O_(Java)) -

NIO as in non-blocking I/O.

how would this work in the context of Swift?

Presumably the demo would use DispatchIO and demonstrate the use of the client in the context of that.

(that should also answer your question :wink:

Oh, you tried to be funny ;->

If you are talking about non-blocking by any chance, we have an implementation of this in BlueSSLService (https://github.com/IBM-Swift/BlueSSLService) which interfaces with a socket management layer (BlueSocket - https://github.com/IBM-Swift/BlueSocket).

Sounds good. Do you have an example demonstrating the use of BlueSocket in NIO mode? Like a simple client which just opens a socket connection to a TLS server and reads with NIO configured.

Ideally we want to abstract away the transport layer and that's actually the next protocol that I referred to in the original proposal that we want to tackle and you have started alluding to in the next paragraphs.

> Wrt the GCD channel stuff. Well, your design has that separate service object which is ‘used' by some ‘Socket’ class.
> With GCD in mind I would design the TLS support as a new custom channel which is just wrapping a GCD channel.
> It would provide the same API like GCD plus the necessary extras. Kinda like OpenSSL BIOs, just with async.

> But this would tie the I/O to a GCD API style (not necessarily to GCD, you could factor out the channel API
> as a ’stream' protocol). Quite likely this is still not desirable for everyone.

Really interesting. Dispatch IO has:

https://developer.apple.com/reference/dispatch/dispatchio
class func read(fromFileDescriptor: Int32, maxLength: Int, runningHandlerOn queue: DispatchQueue, handler: @escaping (DispatchData, Int32) -> Void)
class func write(toFileDescriptor: Int32, data: DispatchData, runningHandlerOn queue: DispatchQueue, handler: @escaping (DispatchData?, Int32) -> Void)

Are you talking about these APIs?

Well not specifically the class funcs, but yes, DispatchIO in stream mode:

  https://developer.apple.com/reference/dispatch/dispatchio.streamtype/1780994-stream
  https://developer.apple.com/reference/dispatch/dispatchio/1388941-read

How would it work with stream?

Not sure what you mean, a dispatch channel in stream mode *is* a stream to me :slight_smile: What do you mean by this? NSStream?

FYI: This has much more and is overkill for this thing here, but I did an implementation of a Node.js-style pull stream on top of DispatchIO, you can check it out here, etc:

  https://github.com/NozeIO/Noze.io/blob/master/Sources/streams/GReadableStreamType.swift#L68

Something like this, just optimised for byte streams would be cool (but maybe too opinionated). And I think that such a stream protocol could be kinda like DispatchIO channels.

Though I’m not sure though whether escaping closures are too much of a performance bottleneck and a delegate would be preferred.

> I haven’t seen a proposal on how (byte) streams are going to work for Server Side Swift
> (i.e. how the API of the HTTP Request/Response body streams would look like). That kinda ties into this.

> Summary: The service object as you proposed should be fine as a support wrapper used by higher level
> I/O objects. I guess it is the right way to go to support various I/O styles. Just wrap OpenSSL and
> the macOS thing. Do not add anything else.
> But *if* we would get a proper (byte) streaming abstraction, I would prefer if a TLS stream would
> live on top of this (be a stream itself and wrap the stream representing the connection).

I agree about the stream abstraction. In order for us to completely abstract away the transport layer, streams are ideal and preferred.

I don’t think they are universally preferred as they may tie you into a specific IO dispatch approach. Some may want to do libmill, some DispatchIO, others plain Posix, and then some NIO. etc.

What is tricky here is that Streams in (mac) Foundation has TLS support via property keys.

https://developer.apple.com/reference/foundation/stream

I wasn’t referring to NSStream, but the concept of a stream in general. A stream would probably be a protocol (or multiple, read/write/duplex etc) which is then implemented by the TLS stream, the Socket, etc.

So the question would be, do we only provide APIs that stream can invoke on Linux (which probably take in file descriptors with a transport type) or do we provide an extended set of APIs that Stream can use, while we also have Stream based transport abstraction? Should stream have multiple TLS supports?

Presumably you are primarily thinking NSStream here. I don’t know, maybe one can build on top of them, but I would prefer to build on top of GCD's channel API. Probably nothing really for the Server-Side-Swift effort though.

hh

···

On 27 Mar 2017, at 20:40, Gelareh Taban <gtaban@us.ibm.com> wrote:

cheers,
gelareh

<graycol.gif>Helge Heß via swift-server-dev ---03/23/2017 11:53:37 AM---Hi Gelareh, I suppose what you have been saying about EAGAIN etc may work out. It would be really ni

From: Helge Heß via swift-server-dev <swift-server-dev@swift.org>
To: swift-server-dev <swift-server-dev@swift.org>
Date: 03/23/2017 11:53 AM
Subject: Re: [swift-server-dev] Draft proposal for TLS Service APIs (pleasereview)
Sent by: swift-server-dev-bounces@swift.org

Hi Gelareh,

I suppose what you have been saying about EAGAIN etc may work out. It would be really nice to actually try the API with an NIO consumer, did you do this already? Just to make sure and test out that we aren’t missing something.

Wrt the GCD channel stuff. Well, your design has that separate service object which is ‘used' by some ‘Socket’ class. With GCD in mind I would design the TLS support as a new custom channel which is just wrapping a GCD channel. It would provide the same API like GCD plus the necessary extras. Kinda like OpenSSL BIOs, just with async.

But this would tie the I/O to a GCD API style (not necessarily to GCD, you could factor out the channel API as a ’stream' protocol). Quite likely this is still not desirable for everyone.

I haven’t seen a proposal on how (byte) streams are going to work for Server Side Swift (i.e. how the API of the HTTP Request/Response body streams would look like). That kinda ties into this.

Summary: The service object as you proposed should be fine as a support wrapper used by higher level I/O objects. I guess it is the right way to go to support various I/O styles. Just wrap OpenSSL and the macOS thing. Do not add anything else.
But *if* we would get a proper (byte) streaming abstraction, I would prefer if a TLS stream would live on top of this (be a stream itself and wrap the stream representing the connection).

hh

> On 23 Mar 2017, at 16:52, Gelareh Taban <gtaban@us.ibm.com> wrote:
>
> Btw Helge, really interesting about the GCD channel APIs. How would your propose they be integrated? Would they replace or modify onSend/onReceive methods?

[attachment "signature.asc" deleted by Gelareh Taban/Austin/IBM] _______________________________________________
swift-server-dev mailing list
swift-server-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-server-dev