schastsp/PROTOCOL.md

194 lines
11 KiB
Markdown

# Stateless Time-Scrambled Cyclic Hash Protocol
**S**a**TS CH**i**P**
<u>Vocabulary</u>
- A `hash` or `digest` is a fixed-length string that is the output of a hash function.
- A `token` is a fixed-length string sent by a client to a server to authenticate.
- A `private key` is a fairly unique and hardly guessable data that is generated by a machine and never shared.
- A `request` is sent from a client to a server, the server sends back a `response`.
## Abstract
After designing some APIs, I found out that you must have at some point a *token* system to authenticate to your server. For the simplest design, the server attributes a unique token for each client, this token will then be sent over each request to authenticate.
> The server may also regenerate the token at some time to avoid token theft.
An easy MITM attack could be to repeat a previously sent request while the token is still valid or even catch the client *token* and build a malicious request with its authentication.
> In this document we will assume that what travels through the network is public, so any MITM can store it. Obviously I highly recommend using TLS for communicating, but we look here for a consistent token system, it only can be better through TLS.
A good alternative could be to use a *one-time token* where each request features a unique token. This design implies that the token is sent from the server to the client before being used ; so any MITM can catch it. To overcome this flaw we could have a system where *one key* is used to generate all the tokens in a consistent manner. If so, the tokens do not have to be sent and can be guessed from the key. But this also implies that the key is shared at some point between the client and the server.
A better solution would be to generate a private key on each client and use it to generate a token for each request. Also, to avoid to share the key and allow the server to easily check tokens, we would like a system where each token can be checked from the previous token without having to know the private key (because it is private...). It would avoid attackers to repeat requests (because each token is unique) or guess the private key because it never travels over the network. In addition short-lived one-time passwords have a mechanism that we could use to build a time-dependent system.
**What we need**
1. Generate a *unique token* for each request from a fixed *private key*
2. The token *never* to be the same
3. The token to be easily checked directly from the previous one
4. Each token to be only valid a few seconds after sending it
5. Each token to give no clue that could help guessing the next token.
6. A system where no key is shared ; only clients do the key processing and the server only to process a simple checking algorithm
**Technology requirements**
1. Mixing 2 hashes in a way that without one of them, the other is *cryptographically impossible* to guess (*i.e. [one-time pad](https://en.wikipedia.org/wiki/One-time_pad)*).
2. Having a time-dependent unique feature, that could be found only a few seconds after sending it (as for *[TOTP](https://tools.ietf.org/html/rfc6238)*).
3. A cryptographic hash function that, from an input of any length, outputs a fixed-length digest in a way that is *impossible* to guess the input back from it.
**Protocols to define**
This document will define and bundle 2 distinct protocols to make up a token system that implements the previous statements.
1. a <u>Stateless Time Scrambling Protocol</u> to take care of the request's invalidation over time.
2. a <u>Stateless Cyclic Hash Algorithm</u> to use a private key as a one-time token generator in a way that no clue is given over published tokens (*i.e. one-way function*).
3. A key <u>renewal mechanism</u> in a way that no clue is given over neither the old nor the new key.
4. A <u>rescue protocol</u> to resynchronise the client with a new key in a way that no clue is given over the network and the client has to process a "proof of work".
## General knowledge & Notations
##### Notations
| Symbols | Description |
|:-----:|:----------|
| $\parallel a\parallel $ | The absolute value of $a$ ; *e.g.* $\parallel -12 \parallel = 12$ |
| $\mid a \mid$ | The integer value of $a$ ; *e.g.* $\mid 12.34 \mid = 12$ |
| $a \oplus b$ | The bitwise operation XOR between binary words $a$ and $b$ |
| $a \And b$ | The bitwise operation AND between binary words $a$ and $b$ |
| $h(m)$ | The digest of the message $m$ by a (consistent) secure cryptographic hash function $h()$ ; *e.g. sha512* |
| $h^n(m)$ | The digest of the $n$-recursive hash function $h()$ with the input data $m$ ; <br>$h^2(m) \equiv h(h(m))$, $h^1(m) \equiv h(m)$, and $h^0(m) \equiv m$. |
| $a \ \mathbb{Z}_{(b)}$ | The result of $a$ modulo $b$ ; *i.e.* the remainder of the Euclidean division of $a$ by $b$ |
| $t_{now}$ | The current *Unix Timestamp* in seconds |
## Variables
The whole system share a common **context**, each client holds a **private keyset**, and the server stores the **last valid token** of each client.
#### 1. Common context
These variables are both on the server and clients. They are specific to the server so each client must match its values. These variables shape the system's **context** $(W, min, belt, max)$.
| Variable | Name | Description |
|:--------:|:----:|:------------|
| $W$ | time window | A number of seconds that is typically the maximum transmission time from end to end. It will be used by the *time-scrambling protocol*. The lower the number, the less time an attacker has to try to brute-force the tokens. |
| $min$ | resynchonization range | A number that is used to resynchronize the client if there is a communication issue (*e.g. lost request, lost response, attack*). The higher the value, the higher the challenge for the client to recover the authentication, thus the harder for an attacker to guess it. |
| $belt$ | security range | A number that is used to resynchronize the client if there is a communication shift (*e.g. lost request, lost response, attack*). It corresponds of the number of desynchronizations the client can handle before never being able to gain the authentication again. |
| $max$ | maximum nonce | A number that is used to cap the value of client's nonces. A too high value will result on keys that will never be replaced, thus making them open to long-processing attacks (*e.g. brute-force*). |
#### 2. Client keyset
Every client holds a **keyset** $(K, n, s)$. It represents its private key and is used to generate the tokens. The secure hash function is extended to a **one-way function** to generate all the tokens from the keyset. Note that the client may hold a secondary keyset between the generation of a new keyset and the server's validation of it.
| Variable | Name | Description |
|:--------:|:----:|:------------|
| $K$ | private key | A secret binary data that must be large and *random* enough not to be brute forced. |
| $n$ | key nonce | A number that is decremented at each token generation. Before $n$ reaches 0, a new keyset must be generated. |
| $s$ | key state | A number that reflects the state of the keyset. It is used to know what to do on the **next request** : <br>- $0$ : normal request<br>- $1$ : will switch to the new key<br>- $2$ : rescue proof of work sent, waiting for the server's acknowledgement |
#### 3. Server variables
| Variable | Name | Description |
|:--------:|:----:|:------------|
| $T$ | last valid token | The server stores the last valid token from the client to check the next one. |
## Protocol
## 1. Client request generation
In each request, the client will send a pair of time-scrambled hashes $(x_1, x_2)$ :
- $x1$ will hold the current one-time token
- $x2$ will hold the necessary data to check the next token
The client implements 3 protocols according to the **keyset state** :
- 0 : `NORMAL` - default authentication protocol.
- 1 : `SWITCH` - default protocol variation to switch to a new keyset when the current one is consumed (*i.e. when $n$ $\leq$ $min+belt$*).
- 2 : `RESCUE` - process the proof of work after receiving the server's challenge when there is a desynchronisation and generate a new keyset.
When the client switches to a new key, it has to store the new keyset along the current one, in order not to lose its authentication if the network fails.
- $(K,n,s)$ - the current keyset
- $(K',n',s')$ - the new keyset
#### A. `NORMAL` protocol (state 0)
| Step | Calculation |
|:--------:|:------|
| `1` | Decrement $n$ |
| `2` | $t\_c = \mid \frac{t\_{now}}{W}\mid, m\_c = t\_c \ \mathbb{Z}\_{(2)}$ |
| `3` | $x\_1 = h^{n}(K) \oplus h(t\_c)$ |
| `4` | $x\_2 = x\_1 \oplus m\_c$ |
| `5` | if $i <= min+belt \Rightarrow s = 1$ |
Send $x_1$ and $x_2$.
#### B. `SWITCH` protocol (state 1)
| Step | Calculation |
|:--------:|:------|
| `1` | $t\_c = \mid \frac{t\_{now}}{W}\mid, m\_c = t\_c \ \mathbb{Z}\_{(2)}$ |
| `2` | $x\_1 = h^{n}(K) \oplus h(t\_c)$ |
| `3` | Generate $(K',n',s')$ until <br><ul><li>$[(h^n(K) \oplus h^{n'}(K')) \And 0x1] \ \mathbb{Z}\_{(2)} = m\_c$</li><li>$[(h^N(K) \oplus h^{n'}(K')) \And 0xf0] \ \mathbb{Z}\_{(3)} = s$</li></ul> |
| `4` | $x\_2 = h^{n'}(K') \oplus h(t\_c)$ |
Send $x_1$ and $x_2$.
If the response succeeds, replace the key with the new one : $K = K', n = n', s = 0$
#### C. `RESCUE` protocol (state 2)
This protocol is processed when the server sends the 2 hashes $(y_1, y_2)$ to the client (instead of the standard response). It means that the server has received a wrong hash, so it sends the rescue challenge to the client.
| Step | Calculation |
|:--------:|:------|
| `1` | $t\_c = \mid \frac{t\_{now}}{W} \mid $, $m\_c = t\_c \ \mathbb{Z}\_{(2)}$
| `2` | $t'\_s = t\_c - \parallel m\_c - (y_1 \oplus y_2)\parallel$
| `3` | $T = x\_1 \oplus h(t'\_s)$
| `4` | Find $N \in [min ; n-min[,\ h^{N}(K) = T$.
| `5` | $x\_1 = h^N(K) \oplus h(t\_c)$
| `6` | Generate $(K',n',s')$ until :<br><ul><li>$[(h^N(K) \oplus h^{n'}(K')) \And 1] \ \mathbb{Z}\_{(2)} = m\_c$</li><li>$[(h^N(K) \oplus h^{n'}(K')) \And 11110000] \ \mathbb{Z}\_{(3)} = s$</li></ul> |
| `7` | $x\_2 = h^{n'}(K') \oplus h(t\_c)$ |
Send $x_1$ and $x_2$.
If the response succeeds, replace the key with the new one : $K = K', n = n', s = 0$
## 2. Server management
When receiving a pair of hashes $(x_1, x_2)$, the server has to :
1. Unscramble the request using the time-scrambling algorithm
2. Check the token $t_1$ held into $x_1$ :
- `VALID` $\rightarrow$ authenticated : store the token $t_2$ held into $x_2$, forward request to the logic unit
- `INVALID` $\rightarrow$ not authenticated : send the challenge hashes $(y_1, y_2)$.
#### 1. Unscramble the request
| Step | Calculation |
|:--------:|:------|
| `1` | $t\_s = \mid \frac{t\_{now}}{W}\mid, m\_s = t\_s \ \mathbb{Z}\_{(2)}$ |
| `2` | $m\_c = x\_1 \oplus x\_2$ |
| `3` | $t'\_c = t\_s - \parallel m_c - m_s \parallel$ |
| `4` | $t\_1 = x\_1 \oplus h(t'\_c)$ |
| `5` | $t\_2 = x\_2 \oplus h(t'\_c)$ |
If $h(t_1) = T$ :
- $t_1$ is valid
- store the next token $T=t_2$
Else, go to the [invalid request management](#2-invalid-request-management).
#### 2. Invalid request management
When receiving an invalid token, the server may send back to the client a challenge to get the authentication back. This challenge is formed by 2 hashes $(y_1, y_2)$.
| Step | Calculation |
|:--------:|:------|
| `1` | $t\_s = \mid \frac{t\_{now}}{W}\mid, m\_s = t\_s \ \mathbb{Z}\_{(2)}$ |
| `2` | $y\_1 = T \oplus h(t\_s)$ |
| `3` | $y\_2 = y\_1 \oplus m\_s$ |
Send $y_1$ and $y_2$.