12 KiB
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
- Generate a public token for each request from a fixed private key
- The public token never to be the same
- Each public token to be only valid a few seconds after sending it
- Each public token to give no clue that could help guessing the next token.
- A system where the server does not have to share a private key with each client and does not need to.
Technology requirements
- Mixing 2 hashes in a way that without one of them, the other is cryptographically impossible to guess (i.e. one-time pad).
- Having a time-dependent unique hash, that could be found only a few seconds after sending it (as for TOTP).
- 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 implement a token system that implements the previous statements.
- a Stateless Time Scrambling Protocol to take care of the request's expiration over time
- a Stateless Cyclic Hash Algorithm to generate several public keys from a single secret key in a way that no clue is given over published keys.
General knowledge & Notations
Notation
Symbols | Description |
---|---|
\parallel a\parallel |
The absolute value of a ; e.g. \parallel a \parallel = \parallel -a \parallel |
\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. sha512 |
h^n(m) |
The digest of the n -recursive hashing function h() with the input data m ; e.g. h^2(m) \equiv h(h(m)) , h^1(m) \equiv h(m) and h^0(m) \equiv m . |
a \mod 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 |
Entities
- A machine
C
(typically a client) - A machine
S
(typically a server)
Common variables
These variables are both on the server and clients. They are specific to the server so each client must match these.
Notation | Name | Description |
---|---|---|
W |
time window divider | A fixed number of seconds that is typically the maximum transmission time from end to end. |
Client variables
These variables defines the state of each client, each having different values.
Notation | Name | Description |
---|---|---|
K |
shifting key | The client private key. It is only known by the client, it must be large enough not to be brute forced. |
n_0 |
shifting nonce | A private number that is decremented for each request. It is unique to each private key K . Before n_0 reaches 0, a new key K must be generated and n_0 is set to its higher value. |
s |
next request order | The next request order is a number that, according on its value, will change the client's behavior : - 0 : normal request- 1 : new key generated- 2 : rescue mode (resynchronize with the server) |
Server variables
Notation | Name | Description |
---|---|---|
H |
last valid hash | The server stores the last valid hash from the client to check the next one. |
If a client sends its token h^{n_0}(K)
, if the token is valid the server stores it inside H
.
Note that for the first synchronization, the server has to "blindly" consider the token as valid.
When the client sends its next token h^{n_0-1}(K)
, the server has to hash it and compare it with the last token H
.
h(h^{n_0-1}) = h^{n_0}(K)
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 setf(x, n^1)
to always returnx
so the keyK
will be only protected by the time protection algorithm.
Constraints
S
must be able to recover the token if the data is received within the time window.- If the window expired, the token must be invalidated by the server.
Limitations
- If an arbitrary catches, then blocks a request from
C
toS
and sends it afterwards, it will be authenticated. This case is equivalent to beingC
(with all secret variables), which can never occur if you use TLS. Notice that you won't be able to extract anything from the token anyway. - With requests meta data (e.g. HTTP headers containing the date), an attacker knowing
W
can forge the time hashh_n
and be able to recover the private keyK
by processing a simple XOR on the public token. Because the cyclic-hash algorithm generates a unique pseudo-random token fromK
for each request, this case does not give the attacker any clue about the next token to be sent.
Protocol
Each request and response will hold a pair of tokens.
1. Client request
This case is the default one where n_0
is far from 1
so there is no key generation to do.
Step | Description | Formula |
---|---|---|
c1 |
Decrement the shifting nonce | n_0 = n_0 - 1 |
c2 |
Calculate the one-time token T_C |
T_C = h^{n_0}(K) |
c3 |
Get the current window id n_C |
n_C =\ \mid \frac{T_{now}}{W} \mid |
c4 |
Calculate m_C , the parity of n_C |
m_C = n_C \mod 2 |
c5 |
Calculate the time hash h_{n_C} |
h_{n_C} = h(n_C) |
c6 |
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 areW
seconds wide. By dividing the time in slices ofW
seconds, if we process the same calculation at an interval ofW
or less seconds, we will have either the same result or a result greater by 1.c3
- The window id paritym_C
allows us to adjust the value ofn_S
made onS
when it receives the request. This difference of 1 second is caused by the division of time in slices, the precision is also divided byW
.T_{now}\mod W = 0 \implies \mid \frac{T_{now}}{W} \mid = \mid \frac{T_{now}+(W-1)}{W} \mid
; no need for adjustmentT_{now}\mod W = 1 \implies \mid \frac{T_{now}}{W} \mid = \mid \frac{T_{now}+(W-1)}{W} \mid + 1
; need to subtract1
T_{now}\mod W = 2 \implies \mid \frac{T_{now}}{W} \mid = \mid \frac{T_{now}+(W-1)}{W} \mid + 1
; need to subtract1
...
T_{now}\mod W = (W-1) \implies \mid \frac{T_{now}}{W} \mid = \mid \frac{T_{now}+(W-1)}{W} \mid + 1
; need to subtract1
c4
-h(n_C)
allowsh_{n_C}
to beL
bits long and protectsn_C
to be predictable fromh_{n_C}
.c5
- we process a one-time pad betweenT_C
andh_{n_C}
it is crucial that both values have the same size ofL
bits. It makesT_C
impossible to extract without having the valueh_{n_C}
, this property applies in both ways.
Short formulas
Field to send | Short formula |
---|---|
T_{req} |
h^{n_0}(K) \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. Server check
Received data
T_{req}
the received request tokenm_C
the received time id parity
Step | Description | Formula |
---|---|---|
s1 |
Store the reception time window id n' |
n' = \mid \frac{T_{now}}{W}\mid |
s2 |
Calculate m_S , the parity of n' |
m_S = n' \mod 2 |
s3 |
Use m_C to try to correct the reception window id and guess the request time id |
n_S = n' - \parallel m_C - m_S \parallel |
s4 |
Calculate the time hash h_{n_S} |
h_{n_S} = h(n_S) |
s5 |
Cancel h_{n_S} to extract T_{C'} |
T_{C'} = T_{req} \oplus h_{n_S} |
s6 |
Check if T_{C'} matches T_S |
T_{C'} = T_S ? |
If
T_{C'} = T_S
,S
can consider thatC
sent the request0
to(W + \frac{W}{2})
seconds ago.
Steps explanation
-
s1
- IfC
andS
have the same values forK
andn^1
,f(K,n^1)
must result in the same output; in other wordsT_C=T_S
. -
s3
/s4
-\| m_C - m_s\|
is the difference betweenm_C
andm_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, letk \in \N
n'= n_C+2+k \implies \parallel m_C - m_S\parallel \in \{0,1\}
- \parallel m_C - m_S\parallel \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 extractT_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 extractT_S
-
-
s6
- By the non-idempotency (i.e.a\oplus b \oplus a = b
) and associativity properties of the XOR operator, consideringh_{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 ofC
have successfully been extracted -
s7
- IfK
andn^1
are the same on both machines,T_S=T_C
. Furthermore, if the time ids are the same (c.f. steps6
) 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 - \parallel m_C - (\mid \frac{T_{now}}{W}\mid \mod 2) \parallel) \oplus f(K, n^1)