From 844d071268abaadfc70a9c9f7003b08900b4d1e4 Mon Sep 17 00:00:00 2001 From: xdrm-brackets Date: Mon, 23 Apr 2018 15:33:09 -0400 Subject: [PATCH] Update page 'STSP' --- STSP.md | 441 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 441 insertions(+) create mode 100644 STSP.md diff --git a/STSP.md b/STSP.md new file mode 100644 index 0000000..1ea61e8 --- /dev/null +++ b/STSP.md @@ -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$
(*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()$.
(*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 !*
(*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 + +Variables +- $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 + +Variables + +- $W$ the fixed window size +- $K$ the shifting key (same on both machines) +- $n^1$ the shifting nonce (same on both machines) + +Received data + +- $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 + + + +One-way algorithm + +- 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. + + + +Hash function + +- 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. + + + + + + +Environment + +- 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 + + ​ + + + +On the client + +- 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$ + + + +Request data + +- $T_{req} = 4a77cea2836187e66925f7c74074a9b74032b3c9$ +- $m_C=0$ + + + +On the server + +- 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 + + ​ + +On the client + +- 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$ + + + +Request data + +- $T_{req} = 4a77cea2836187e66925f7c74074a9b74032b3c9$ +- $m_C=0$ + + + +On the server + +- 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 + + ​ + +On the client + +- 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$ + +Request data + +- $T_{req} = 4a77cea2836187e66925f7c74074a9b74032b3c9$ +- $m_C=0$ + +On the server + +- 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$. \ No newline at end of file