# Secure Streams
Secure Streams is a networking api that strictly separates payload from any
metadata. That includes the client endpoint address for the connection, the tls
trust chain and even the protocol used to connect to the endpoint.
The user api just receives and transmits payload, and receives advisory
connection state information.
The details about how the connections for different types of secure stream should
be made are held in JSON "policy database" initially passed in to the context
creation, but able to be updated from a remote copy.
Both client and server networking can be handled using Secure Streams APIS.
![overview](/doc-assets/ss-operation-modes.png)
## Secure Streams CLIENT State lifecycle
![overview](/doc-assets/ss-state-flow.png)
Secure Streams are created using `lws_ss_create()`, after that they may acquire
underlying connections, and lose them, but the lifecycle of the Secure Stream
itself is not directly related to any underlying connection.
Once created, Secure Streams may attempt connections, these may fail and once
the number of failures exceeds the count of attempts to conceal in the retry /
backoff policy, the stream reaches `LWSSSCS_ALL_RETRIES_FAILED`. The stream becomes
idle again until another explicit connection attempt is given.
Once connected, the user code can use `lws_ss_request_tx()` to ask for a slot
to write to the peer, when this if forthcoming the tx handler can send a message.
If the underlying protocol gives indications of transaction success, such as,
eg, a 200 for http, or an ACK from MQTT, the stream state is called back with
an `LWSSSCS_QOS_ACK_REMOTE` or `LWSSSCS_QOS_NACK_REMOTE`.
## SS Callback return handling
SS state(), rx() and tx() can indicate with their return code some common
situations that should be handled by the caller.
Constant|Scope|Meaning
---|---|---
LWSSSSRET_TX_DONT_SEND|tx|This opportunity to send something was passed on
LWSSSSRET_OK|state, rx, tx|No error, continue doing what we're doing
LWSSSSRET_DISCONNECT_ME|state, rx|assertively disconnect from peer
LWSSSSRET_DESTROY_ME|state, rx|Caller should now destroy the stream itself
LWSSSSRET_SS_HANDLE_DESTROYED|state|Something handled a request to destroy the stream
Destruction of the stream we're calling back on inside the callback is tricky,
it's preferable to return `LWSSSSRET_DESTROY_ME` if it is required, and let the
caller handle it. But in some cases, helpers called from the callbacks may
destroy the handle themselves, in that case the handler should return
`LWSSSSRET_SS_HANDLE_DESTROYED` indicating that the handle is already destroyed.
## Secure Streams SERVER State lifecycle
![overview](/doc-assets/ss-state-flow-server.png)
You can also run servers defined using Secure Streams, the main difference is
that the user code must assertively create a secure stream of the server type
in order to create the vhost and listening socket. When this stream is
destroyed, the vhost is destroyed and the listen socket closed, otherwise it
does not perform any rx or tx, it just represents the server lifecycle.
When client connections randomly arrive at the listen socket, new Secure Stream
objects are created along with accept sockets to represent each client
connection. As they represent the incoming connection, their lifecycle is the
same as that of the underlying connection. There is no retry concept since as
with eg, http servers, the clients may typically not be routable for new
connections initiated by the server.
Since connections at socket level are already established, new connections are
immediately taken through CREATING, CONNECTING, CONNECTED states for
consistency.
Some underlying protocols like http are "transactional", the server receives
a logical request and must reply with a logical response. The additional
state `LWSSSCS_SERVER_TXN` provides a point where the user code can set
transaction metadata before or in place of sending any payload. It's also
possible to defer this until any rx related to the transaction was received,
but commonly with http requests, there is no rx / body. Configuring the
response there may look like
```
/*
* We do want to ack the transaction...
*/
lws_ss_server_ack(m->ss, 0);
/*
* ... it's going to be text/html...
*/
lws_ss_set_metadata(m->ss, "mime", "text/html", 9);
/*
* ...it's going to be 128 byte (and request tx)
*/
lws_ss_request_tx_len(m->ss, 128);
```
Otherwise the general api usage is very similar to client usage.
## Convention for rx and tx callback return
Function|Return|Meaning
---|---|---
tx|`LWSSSSRET_OK`|Send the amount of `buf` stored in `*len`
tx|`LWSSSSRET_TX_DONT_SEND`|Do not send anything
tx|`LWSSSSRET_DISCONNECT_ME`|Close the current connection
tx|`LWSSSSRET_DESTROY_ME`|Destroy the Secure Stream
rx|>=0|accepted
rx|<0|Close the current connection
# JSON Policy Database
Example JSON policy... formatting is shown for clarity but whitespace can be
omitted in the actual policy.
Ordering is not critical in itself, but forward references are not allowed,
things must be defined before they are allowed to be referenced later in the
JSON.
```
{
"release": "01234567",
"product": "myproduct",
"schema-version": 1,
"retry": [{
"default": {
"backoff": [1000, 2000, 3000, 5000, 10000],
"conceal": 5,
"jitterpc": 20
}
}],
"certs": [{
"isrg_root_x1": "MIIFazCCA1OgAw...AnX5iItreGCc="
}, {
"LEX3_isrg_root_x1": "MIIFjTCCA3WgAwIB...WEsikxqEt"
}],
"trust_stores": [{
"le_via_isrg": ["isrg_root_x1", "LEX3_isrg_root_x1"]
}],
"s": [{
"mintest": {
"endpoint": "warmcat.com",
"port": 4443,
"protocol": "h1get",
"aux": "index.html",
"plugins": [],
"tls": true,
"opportunistic": true,
"retry": "default",
"tls_trust_store": "le_via_isrg"
}
}]
}
```
### `Release`
Identifies the policy version
### `Product`
Identifies the product the policy should apply to
### `Schema-version`
The minimum version of the policy parser required to parse this policy
### `via-socks5`
Optional redirect for Secure Streams client traffic through a socks5
proxy given in the format `address:port`, eg, `127.0.0.1:12345`.
### `retry`
A list of backoff schemes referred to in the policy
### `backoff`
An array of ms delays for each retry in turn
### `conceal`
The number of retries to conceal from higher layers before giving errors. If
this is larger than the number of times in the backoff array, then the last time
is used for the extra delays. 65535 means never stop trying.
### `jitterpc`
Percentage of the delay times mentioned in the backoff array that may be
randomly added to the figure from the array. For example with an array entry of
1000ms, and jitterpc of 20%, actual delays will be chosen randomly from 1000ms
through 1200ms. This is to stop retry storms triggered by a single event like
an outage becoming synchronized into a DoS.
### `certs`
Certificates needed for validation should be listed here each with a name. The
format is base64 DER, which is the same as the part of PEM that is inside the
start and end lines.
### `trust_stores`
Chains of certificates given in the `certs` section may be named and described
inside the `trust_stores` section. Each entry in `trust_stores` is created as
a vhost + tls context with the given name. Stream types can later be associated
with one of these to enforce validity checking of the remote server.
Entries should be named using "name" and the stack array defined using "stack"
### `auth`
Optional section describing a map of available authentication streamtypes to
auth token blob indexes.
```
...
"auth": [{"name":"newauth","type":"sigv4", "blob":0}]
...
```
Streams can indicate they depend on a valid auth token from one of these schemes
by using the `"use_auth": "name"` member in the streamtype definition, where name
is, eg, "sigv4" in the example above. If "use_auth" is not in the streamtype
definition, default auth is lwa if "http_auth_header" i