Update page 'STSP'
parent
1568260fcc
commit
844d071268
|
@ -0,0 +1,441 @@
|
|||
# Stateless Time Scrambling Protocol
|
||||
|
||||
|
||||
|
||||
## Motivation
|
||||
|
||||
After designing some APIs, I found out that you must have at some point a *token* system where the token is a fixed-length string. An easy MITM attack could be to repeat a previously sent request while the token is still valid. Or in worse conditions, catch the client *token* and build a malicious request with its authenticated session.
|
||||
|
||||
In the rest of this document we will admit 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 solution could be to use a one-time token so the server sends back a new token for each response. This behavior means that the token travels through the network - which we consider public - before being used. In addition we would like the server to create only a secret key once to fasten the authentication system, because it could manage millions of clients.
|
||||
|
||||
A better solution would be to keep a private key and wrap it in a one-time system that generates a public token for each request. It would avoid attackers to repeat our requests or guess the private key from the token. Also short-lived one-time passwords have a mechanism that we could use to build a time-dependent system.
|
||||
|
||||
**What we need**
|
||||
1. Generate a *public token* for each request from a fixed *private key* (protocol documentation [here](./scha.md))
|
||||
2. The *public token* never to be the same
|
||||
3. Each *public token* to be only valid a few seconds after sending it (protocol documentation [here](./stsp.md))
|
||||
|
||||
**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 hash, that could be found only a few seconds after sending it (as for *[TOTP](https://tools.ietf.org/html/rfc6238)*).
|
||||
|
||||
|
||||
**Documents**
|
||||
|
||||
- The stateless cyclic hash algorithm that generates a unique public token from a fixed private key has its documentation available [here](./scha.md).
|
||||
|
||||
## General knowledge & Notations
|
||||
|
||||
**Notation**
|
||||
|
||||
|Symbols|Description|
|
||||
|--|--|
|
||||
|$\|a\|$|The absolute value of $a$. $\|a\|=\|-a\|$|
|
||||
|$\mid a \mid$|The integer value of $a$<br />(*e.g.* $\mid 12.34 \mid = 12$)|
|
||||
|$a \oplus b$|The bitwise operation XOR between binary words $a$ and $b$|
|
||||
|$h(m)$|The digest of the message $m$ by a consistent cryptographic hashing function $h()$.<br />(*e.g. sha-512*)|
|
||||
|$L$|The length in bits of the digest of the hash function $h()$; *i.e. $L$ is 4 times the length of the hexadecimal representation*|
|
||||
|$f(x, n^1)$|A one-way function that will, from an input $x$ and a nonce $n^1$ result in a value $y$ of $L$ bits. From $y$ and $n^1$, it must be cryptographically impossible to guess $x$. Note that 2 different nonces with the same input must *never* output the same value.|
|
||||
|$a \mod b$|The result of *a* modulo *b* (the remainder of the Euclidean division of $a$ by $b$)|
|
||||
|$T_{now}$|The current *Unix Timestamp* in seconds|
|
||||
|
||||
>One-way function $f()$
|
||||
>
|
||||
>- The function $f$ should be replaced with any consistent algorithm such as *one-time password* ([OTC](https://tools.ietf.org/html/rfc4226)) or the *cyclic-hash* algorithm for instance.
|
||||
>- If you only want the time-dependent feature, you can set $f(x, n^1)$ to always return $x$. The key $K$ will be only protected by the time protection protocol described in this document. I highly recommend not to choose this option. The private key is easily extractable from attackers sniffing the requests.
|
||||
|
||||
**Entities**
|
||||
|
||||
- A machine $C$ (*i.e. typically a client*)
|
||||
- A machine $S$ (*i.e. typically a server*)
|
||||
|
||||
|
||||
**Variables**
|
||||
|Variable|Name|Description|
|
||||
|--|--|--|
|
||||
|$W$|window size|A fixed number of seconds; *i.e. typically the maximum transmission time*|
|
||||
|$K$|shifting key|A fixed private key. $C$ and $S$ both must have the same value and eventually a way to resynchronize it the key have been compromised or have been used for too long.|
|
||||
|$n^1$|shifting nonce|A unique request index. It is updated on both $C$ and $S$ after each valid request-response exchange. *Warning: 2 requests must not have the same index !*<br />(*e.g. the index could be incremented after each request on both machines*)|
|
||||
|
||||
|
||||
|
||||
|
||||
## Description of the problem
|
||||
|
||||
$C$ wants to send a token that will only be valid one time and within a fixed time window.
|
||||
|
||||
*Note: This document only gives a solution for the time-dependent feature, the one-time aspect is wrapped into the implementation of the $f()$ function. If you only need the time-dependent feature, you can set $f(x, n^1)$ to always return $x$ so the key $K$ will be only protected by the time protection algorithm.*
|
||||
|
||||
**Constraints**
|
||||
- $S$ must be able to recover the token only if the data is received within the time window.
|
||||
- If the window expired, no one will never be able to recover the token from the request itself (not considering the headers containing the date).
|
||||
|
||||
**Requirements**
|
||||
- The window size constant $W$ must be the same on the 2 machines.
|
||||
- The shifting key constant $K$ must always be the same on the 2 machines.
|
||||
- The shifting nonce $n^1$ must always be the same on the 2 machines over requests.
|
||||
|
||||
**Limitations**
|
||||
- If an arbitrary catches, then blocks a request from $C$ to $S$ and sends it after, it will be authenticated. This case is equivalent to being $C$ (with all secret variables), which can *never* occur if you use TLS. Notice that you won't be able to extract anything from the caught token anyway.
|
||||
- With requests meta data (*e.g. HTTP headers containing the date*), an attacker knowing $W$ can forge the time hash $h_n$ and be able to recover the private key $K$ by processing a simple *XOR* on the public token. If the function $f()$ is strong enough to generate a unique pseudo-random token from $K$ for each request, this issue is no more relevant.
|
||||
|
||||
|
||||
|
||||
|
||||
## Protocol
|
||||
|
||||
|
||||
|
||||
### 1. Scramble the request
|
||||
|
||||
<u>Variables</u>
|
||||
- $W$ the fixed window size
|
||||
- $K$ the shifting key (same on both machines)
|
||||
- $n^1$ the shifting nonce (same on both machines)
|
||||
|
||||
|Step|Description|Formula|
|
||||
|:-:|--|:--|
|
||||
|`c1`|Calculate the one-time token $T_C$|$T_C = f(K, n^1)$|
|
||||
|`c2`|Get the current window id $n_C$|$n_C =\ \mid \frac{T_{now}}{W} \mid$|
|
||||
|`c3`|Calculate $m_C$, the parity of $n_C$|$m_C = n_C \mod 2$|
|
||||
|`c4`|Calculate the time hash $h_{n_C}$|$h_{n_C} = h(n_C)$|
|
||||
|`c5`|Calculate $T_{req}$, the scrambled *request token*|$T_{req} = T_C \oplus h_{n_C}$|
|
||||
|
||||
**Steps explanation**
|
||||
|
||||
- `c2` - The window id corresponds to the index of the time slice where slices are $W$ seconds wide. By dividing the time in slices of $W$ seconds, if we process the same calculation at an interval of $W$ or less seconds, we will have either the same result or a result greater by 1.
|
||||
- `c3` - The window id parity $m_C$ allows us to adjust the value of $n_S$ made on $S$ when it receives the request. This difference of 1 second is caused by the division of time in slices, the precision is also divided by $W$.
|
||||
- $T_{now}\mod W = 0 \implies \mid \frac{T_{now}}{W} \mid = \mid \frac{T_{now}+(W-1)}{W} \mid$; no need for adjustment
|
||||
- $T_{now}\mod W = 1 \implies \mid \frac{T_{now}}{W} \mid = \mid \frac{T_{now}+(W-1)}{W} \mid + 1$; need to subtract $1$
|
||||
- $T_{now}\mod W = 2 \implies \mid \frac{T_{now}}{W} \mid = \mid \frac{T_{now}+(W-1)}{W} \mid + 1$; need to subtract $1$
|
||||
- $...$
|
||||
- $T_{now}\mod W = (W-1) \implies \mid \frac{T_{now}}{W} \mid = \mid \frac{T_{now}+(W-1)}{W} \mid + 1$; need to subtract $1$
|
||||
- `c4` - $h(n_C)$ allows $h_{n_C}$ to be $L$ bits long and protects $n_C$ to be predictable from $h_{n_C}$.
|
||||
- `c5` - we process a one-time pad between $T_C$ and $h_{n_C}$ it is crucial that both values have the same size of $L$ bits. It makes $T_C$ impossible to extract without having the value $h_{n_C}$, this property applies in both ways.
|
||||
|
||||
|
||||
|
||||
|
||||
**Short formulas**
|
||||
|
||||
| Field to send | Short formula |
|
||||
| :-----------: | ----------------------------------------------- |
|
||||
| $T_{req}$ | $f(K, n^1) \oplus h(\mid\frac{T_{now}}{W}\mid)$ |
|
||||
| $m_C$ | $\mid\frac{T_{now}}{W}\mid \mod 2$ |
|
||||
|
||||
> Note
|
||||
>
|
||||
> - In order to send all the data in one request, for instance you can simply concatenate the 2 variables.
|
||||
|
||||
|
||||
|
||||
### 2. Unscramble the request
|
||||
|
||||
<u>Variables</u>
|
||||
|
||||
- $W$ the fixed window size
|
||||
- $K$ the shifting key (same on both machines)
|
||||
- $n^1$ the shifting nonce (same on both machines)
|
||||
|
||||
<u>Received data</u>
|
||||
|
||||
- $T_{req}$ the received request token
|
||||
- $m_C$ the received time id parity
|
||||
|
||||
|Step|Description|Formula|
|
||||
|:-:|--|--|
|
||||
|`s1`|Calculate the one-time token $T_S$|$T_S = f(K, n^1)$|
|
||||
|`s2`|Store the reception time window id $n'$|$n' = \mid \frac{T_{now}}{W}\mid$|
|
||||
|`s3`|Calculate $m_S$, the parity of $n'$|$m_S = n' \mod 2$|
|
||||
|`s4`|Use $m_C$ to try to correct the reception window id and guess the sender window id|$n_S = n' - \| m_C - m_S \|$|
|
||||
|`s5`|Calculate the time hash $h_{n_S}$|$h_{n_S} = h(n_S)$|
|
||||
|`s6`|Cancel $h_{n_S}$ to extract $T_{C'}$|$T_{C'} = T_{req} \oplus h_{n_S}$|
|
||||
|`s7`|Check if $T_{C'}$ matches $T_S$|$T_{C'} = T_S ?$|
|
||||
|
||||
> If $T_{C'} = T_S$, $S$ can consider that $C$ sent the request $0$ to $(W + \frac{W}{2})$ seconds ago.
|
||||
|
||||
|
||||
|
||||
**Steps explanation**
|
||||
|
||||
- `s1` - If $C$ and $S$ have the same values for $K$ and $n^1$, $f(K,n^1)$ must result in the same output; in other words $T_C=T_S$.
|
||||
|
||||
- `s3`/`s4` - $\| m_C - m_s\|$ is the difference between $m_C$ and $m_S$.
|
||||
|
||||
- If the receiver time window id ($n'$) is the same as the sender ($n_C$)
|
||||
|
||||
$n'=n_C \implies m_S=m_C$
|
||||
|
||||
$m_C=m_S \implies \| m_C - m_S\| = 0$
|
||||
|
||||
$n_S = n_C - 0$
|
||||
|
||||
$n_S=n_C$, the time ids are the same, $S$ can now unscramble the request to check the token
|
||||
|
||||
- If the receiver time window if further the sender by $1$
|
||||
|
||||
$n'=n_C+1 \implies \| m_C - m_S \| = 1$
|
||||
|
||||
$n_S = n_C + 1 - 1$
|
||||
|
||||
$n_S = n_C$, the time ids are the same, $S$ can now unscramble the request to check the token
|
||||
|
||||
- If the receiver time window if further the sender by $2$ or more, let $k \in \N$
|
||||
|
||||
$n'= n_C+2+k \implies \| m_C - m_S\| \in \{0,1\}$
|
||||
|
||||
$- \| m_C - m_S\| \in \{-1,0\}$
|
||||
|
||||
$n_S \in \{n_C + 2 + k - 1, n_C + 2 + k + 0\}$
|
||||
|
||||
$n_S \in \{n_C + k + 1, n_C + k + 2\}$
|
||||
|
||||
$\rarr n_S = n_C + k + 1 \implies \forall (k\in \N), n_S \gt n_C$, the time ids differ, $S$ cannot extract $T_S$
|
||||
|
||||
$\rarr n_S = n_C + k + 2 \implies \forall (k \in \N), n_S \gt n_C+1$, the time ids differ, $S$ cannot extract $T_S$
|
||||
|
||||
- `s6` - By the *non-idempotency* (*i.e. $a\oplus b \oplus a = b$*) and *associativity* properties of the *XOR* operator, considering $h_{n_S}=h_{n_C}$:
|
||||
|
||||
$T_{C'} = T_{req} \oplus h_{n_S} = (T_C \oplus h_{n_C}) \oplus h_{n_S}$
|
||||
|
||||
$h_{n_S} = h_{n_C} \implies T_{C'} = T_C \oplus h_{n_C} \oplus h_{n_C}$
|
||||
|
||||
$T_{C'} = T_C$, the one-time token of $C$ have successfully been extracted
|
||||
|
||||
- `s7` - If $K$ and $n^1$ are the same on both machines, $T_S=T_C$. Furthermore, if the time ids are the same (*c.f. step `s6`*) the 2 tokens should match.
|
||||
|
||||
|
||||
|
||||
**Short formulas**
|
||||
|
||||
The whole unscrambling process can be shortened into the following formula resulting in $0$ if the client is authenticated.
|
||||
|
||||
$T_{req} \oplus h(\mid \frac{T_{now}}{W}\mid - \| m_C - (\mid \frac{T_{now}}{W}\mid \mod 2) \|) \oplus f(K, n^1)$
|
||||
|
||||
|
||||
|
||||
## Practical Example
|
||||
|
||||
|
||||
|
||||
<u>One-way algorithm</u>
|
||||
|
||||
- Lets consider our one-way function $f(x, n^1)$ to always return $x$ itself. By using this definition, we only have the time protection protocol "scrambling" the key $K$, it is for example only.
|
||||
|
||||
|
||||
> Warning
|
||||
>
|
||||
> - Always consider choosing a strong function $f()$ for security.
|
||||
|
||||
|
||||
|
||||
<u>Hash function</u>
|
||||
|
||||
- We will use the *sha-128* algorithm as our hash function.
|
||||
|
||||
> Warning
|
||||
>
|
||||
> - The *sha-128* algorithm is no more secure !! I am using it only because its length fits well this document, newer hash functions have too long values impossible to be displayed properly.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<u>Environment</u>
|
||||
|
||||
- Let $K$ be the hexadecimal representation $83ff9f4e0d16d61727cbdf47d769fb707b652217$
|
||||
- $L=128$ because of our hash function $h()$
|
||||
- Let $n^1=2$ (*i.e. 2 successful requests have already been sent*)
|
||||
- Let $W = 4$ because we consider our maximum transmission time to 4 seconds.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### Case 1: valid exchange (same window)
|
||||
|
||||
- Let $T_{now}=1523276226$ on the client
|
||||
|
||||
- Let $T_{now} \in 1523276226+W=152327630$ on the server
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<u>On the client</u>
|
||||
|
||||
- Process $T_C = f(83ff9f4e0d16d61727cbdf47d769fb707b652217, 2)$
|
||||
|
||||
$= 83ff9f4e0d16d61727cbdf47d769fb707b652217$
|
||||
|
||||
|
||||
- Process $n_C = \| \frac{1523276226}{4} \| = \mid 380819056.5 \mid = 380819056$
|
||||
|
||||
- Process $m_C = 380819056 \mod 2 = 0$
|
||||
|
||||
- Process $T_{req} = T_C \otimes h(n)$$
|
||||
|
||||
$= 83ff9f4e0d16d61727cbdf47d769fb707b652217 \otimes h(380819056)$
|
||||
|
||||
$= 83ff9f4e0d16d61727cbdf47d769fb707b652217 \otimes c98851ec8e7751f14eee2880971d52c73b5791de$
|
||||
|
||||
$=4a77cea2836187e66925f7c74074a9b74032b3c9$
|
||||
|
||||
|
||||
|
||||
<u>Request data</u>
|
||||
|
||||
- $T_{req} = 4a77cea2836187e66925f7c74074a9b74032b3c9$
|
||||
- $m_C=0$
|
||||
|
||||
|
||||
|
||||
<u>On the server</u>
|
||||
|
||||
- Process $T_S = f(83ff9f4e0d16d61727cbdf47d769fb707b652217, 2)$
|
||||
|
||||
$= 83ff9f4e0d16d61727cbdf47d769fb707b652217$
|
||||
|
||||
- Process $n' = \| \frac{152327630}{4} \| = \mid 380819057.5 \mid = 380819057$
|
||||
|
||||
- Process $m_S = 380819057 \mod 2 = 1$
|
||||
|
||||
- Process $n_S = 380819057 - \| 0 - 1 \| = 380819057 - 1 = 380819056$
|
||||
|
||||
- Process $T_{C'} = 4a77cea2836187e66925f7c74074a9b74032b3c9 \otimes h(380819056)$
|
||||
|
||||
$= 4a77cea2836187e66925f7c74074a9b74032b3c9 \otimes c98851ec8e7751f14eee2880971d52c73b5791de$
|
||||
|
||||
$=83ff9f4e0d16d61727cbdf47d769fb707b652217%$
|
||||
|
||||
$= T_S$
|
||||
|
||||
|
||||
|
||||
$\implies T_{C'} = T_{S}$ the client is authenticated, we can increment $n^1=3$.
|
||||
|
||||
|
||||
|
||||
### Case 2: invalid exchange (next half-window)
|
||||
|
||||
- Let $T_{now}=1523276226$ on the client
|
||||
|
||||
- Let $T_{now} \in 1523276226+W+\frac{W}{2}=1523276232$ on the server
|
||||
|
||||
|
||||
|
||||
<u>On the client</u>
|
||||
|
||||
- Process $T_C = f(83ff9f4e0d16d61727cbdf47d769fb707b652217, 2)$
|
||||
|
||||
$= 83ff9f4e0d16d61727cbdf47d769fb707b652217$
|
||||
|
||||
|
||||
- Process $n_C = \| \frac{1523276226}{4} \| = \mid 380819056.5 \mid = 380819056$
|
||||
|
||||
- Process $m_C = 380819056 \mod 2 = 0$
|
||||
|
||||
- Process $T_{req} = T_C \otimes h(n_C)$$
|
||||
|
||||
$= 83ff9f4e0d16d61727cbdf47d769fb707b652217 \otimes h(380819056)$
|
||||
|
||||
$= 83ff9f4e0d16d61727cbdf47d769fb707b652217 \otimes c98851ec8e7751f14eee2880971d52c73b5791de$
|
||||
|
||||
$=4a77cea2836187e66925f7c74074a9b74032b3c9$
|
||||
|
||||
|
||||
|
||||
<u>Request data</u>
|
||||
|
||||
- $T_{req} = 4a77cea2836187e66925f7c74074a9b74032b3c9$
|
||||
- $m_C=0$
|
||||
|
||||
|
||||
|
||||
<u>On the server</u>
|
||||
|
||||
- Process $T_S = f(83ff9f4e0d16d61727cbdf47d769fb707b652217, 2)$
|
||||
|
||||
$= 83ff9f4e0d16d61727cbdf47d769fb707b652217$
|
||||
|
||||
|
||||
- Process $n' = \| \frac{1523276232}{4} \| = \mid 380819058 \mid = 380819058$
|
||||
|
||||
- Process $m_S = 380819058 \mod 2 = 0$
|
||||
|
||||
- Process $n_S = 380819058 - \| 0 - 0' \| = 380819058 - 0 = 380819058$
|
||||
|
||||
- Process $T_{C'} = 4a77cea2836187e66925f7c74074a9b74032b3c9 \otimes h(380819058)$
|
||||
|
||||
$= 4a77cea2836187e66925f7c74074a9b74032b3c9 \otimes a86890d123a29c1115e7a2a9900d7f7a8b286103$
|
||||
|
||||
$=e21f5e73a0c31bf77cc2556ed079d6cdcb1ad2ca$
|
||||
|
||||
$\neq T_S$
|
||||
|
||||
|
||||
|
||||
$\implies T_{C'} \neq T_S$ the client is **not** authenticated, we do **not** increment $n^1$.
|
||||
|
||||
|
||||
|
||||
### Case 3: invalid exchange (same window but wrong key)
|
||||
|
||||
- Let $T_{now}=1523276226$ on the client
|
||||
|
||||
- Let $T_{now} \in 1523276226+W=152327630$ on the server
|
||||
|
||||
|
||||
|
||||
<u>On the client</u>
|
||||
|
||||
- Process $T_C = f(secret\_key, 2)$
|
||||
|
||||
$= h(secret\_key)$
|
||||
|
||||
$= 83ff9f4e0d16d61727cbdf47d769fb707b652217$
|
||||
|
||||
|
||||
- Process $n_C = \| \frac{1523276226}{4} \| = \mid 380819056.5 \mid = 380819056$
|
||||
|
||||
- Process $m_C = 380819056 \mod 2 = 0$
|
||||
|
||||
- Process $T_{req} = T_C \otimes h(n)$$
|
||||
|
||||
$= 83ff9f4e0d16d61727cbdf47d769fb707b652217 \otimes h(380819056)$
|
||||
|
||||
$= 83ff9f4e0d16d61727cbdf47d769fb707b652217 \otimes c98851ec8e7751f14eee2880971d52c73b5791de$
|
||||
|
||||
$=4a77cea2836187e66925f7c74074a9b74032b3c9$
|
||||
|
||||
<u>Request data</u>
|
||||
|
||||
- $T_{req} = 4a77cea2836187e66925f7c74074a9b74032b3c9$
|
||||
- $m_C=0$
|
||||
|
||||
<u>On the server</u>
|
||||
|
||||
- Process $T_S = f(435c07ed18d15b8896d21441f9189982191cba8b, 3)$ **WRONG KEY**
|
||||
|
||||
$= 435c07ed18d15b8896d21441f9189982191cba8b$
|
||||
|
||||
- Process $n' = \| \frac{152327630}{4} \| = \mid 380819057.5 \mid = 380819057$
|
||||
|
||||
- Process $m_S = 380819057 \mod 2 = 1$
|
||||
|
||||
- Process $n_S = 380819057 - \| 0 - 1 \| = 380819057 - 1 = 380819056$
|
||||
|
||||
- Process $T_{C'} = 435c07ed18d15b8896d21441f9189982191cba8b \otimes h(380819056)$
|
||||
|
||||
$= 435c07ed18d15b8896d21441f9189982191cba8b \otimes c98851ec8e7751f14eee2880971d52c73b5791de$
|
||||
|
||||
$=8ad4560196a60a79d83c3cc16e05cb45224b2b55$
|
||||
|
||||
$\neq T_S$
|
||||
|
||||
|
||||
|
||||
$\implies T_{C'} \neq T_{S}$ the client is **not** authenticated, we do **not** can increment $n^1$.
|
Loading…
Reference in New Issue