Compare commits

..

No commits in common. "master" and "fix-structure" have entirely different histories.

20 changed files with 432 additions and 893 deletions

View File

@ -1,194 +0,0 @@
# 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$.

113
README.md
View File

@ -1,113 +0,0 @@
# SCHA/STSP
**Copyright (C) 2017 xdrm-brackets**
[![Go version](https://img.shields.io/badge/go_version-1.10.3-blue.svg)](https://golang.org/doc/go1.10) [![Go Report Card](https://goreportcard.com/badge/git.xdrm.io/logauth/schastsp)](https://goreportcard.com/report/git.xdrm.io/logauth/schastsp) [![Go doc](https://godoc.org/git.xdrm.io/logauth/schastsp?status.svg)](https://godoc.org/git.xdrm.io/logauth/schastsp)
## Overview
----
This software defines and uses its own protocol which bundles 2 technologies :
- Stateless Cyclic Hash Algorithm
- Stateless Time Scrambling Protocol
It is meant to be used over request/response stateless networking and has been designed with HTTP in mind. The protocol only covers the generation and management of a pair of *tokens* which are hexadecimal strings. These are sent inside each request and a pair also has to be sent back to the sender. These tokens are mainly sent in the HTTP `Authorization` header for HTTP requests and responses.
**Features**
Beyond security issues, this protocol has some additional features :
- **Trust Chain** - Each exchange between the server and the client is bound to the previous one. Each request is unique and can only give information about previous ones, not future ones. This principle ensures the server that no-one can be faking the client (unless someone has access to its key).
> If an attacker can guess (*e.g. bruteforce*) a successful request and gain access to the server - if he hasn't the client's key - it is fairly impossible that he also guesses the next request.
> A resynchronization protocol is featured in this package in order for a client to regain ownership on the trust chain if lost (by network issue or by an attack).
- **Time-awareness** - A request is only valid a short amount of time after its generation. This amount is usually the maximum transmission time.
> Any MITM that catches the client's request to change its content only has a minimum time to forward it. As a result - if the amount is well chosen - any request modification is blocked by the time it takes.
- [TODO]
**Security**
The aim of this package is to provide a **secure** authentication system between a server and its clients. Secure is defined as follows :
- No *man-in-the-middle* (MITM) can gather enough information to fake any client. Every data sent over the network is the result of one or more <u>one-way algorithms</u>.
- Neither a protocol understanding nor the source code can help an attacker fake a client or find useful information among requests.
- The server has no secret key other than the <u>synchronization key</u> (used once to bind the client). The server has no clue what each client's key is, in fact it knows as much as a *MITM*.
## Requirements
----
You need a recent machine with `go` installed.
> This package has not been tested under the version **1.10**.
## Installation
----
Download the package with `go get` :
```bash
go get git.xdrm.io/logauth/schastsp
```
Build the executables :
```bash
go install git.xdrm.io/logauth/schastsp/cmd/client;
go install git.xdrm.io/logauth/schastsp/cmd/server;
```
The executables are now available in your `$GOBIN` (*i.e. `$GOPATH/bin`*) folder.
## Client
----
### 1. Usage
```bash
client <command> [options]
```
Available commands are :
- `-req` generates a new request into standard output
- `-res` reads and manages a response from standard input
- `-sync` generates a synchronization key into standard output (*i.e. resets the system*)
Available options are :
- `-file` sets the folder where the keys are (or will be) stored.
- `-win` sets the window value (in milliseconds). Typically the maximum transmission time.
- `-min` sets the minimum cyclic-hash depth.
- `-max` sets the maximum cyclic-hash depth.
- `-thr` sets the cyclic-hash threshold. This value represents at which offset of the key's TTL the key will be generated. It also corresponds to the number of time the client can desynchronize before becoming impossible to synchronize again.

View File

@ -3,9 +3,9 @@ package client
import (
"errors"
"fmt"
"git.xdrm.io/logauth/schastsp/context"
"git.xdrm.io/logauth/schastsp/internal/keyset"
"git.xdrm.io/logauth/schastsp/internal/scha"
"git.xdrm.io/schastsp/internal/keyset"
"git.xdrm.io/schastsp/context"
"git.xdrm.io/schastsp/internal/scha"
"io"
)
@ -14,11 +14,11 @@ const DEBUG = false
/* (1) Structure
---------------------------------------------------------*/
type T struct {
ctx *context.T // shared context
key *keyset.T // current key
sync *keyset.T // next bufferised key
fkey *config // key file management
fsync *config // sync file management
ctx *context.T // shared context
key *keyset.T // current key
sync *keyset.T // next bufferised key
fkey *config // key file management
fsync *config // sync file management
}
/* (2) Constructor
@ -34,22 +34,16 @@ func New(ctx *context.T, saveDir string) (*T, error) {
inst := new(T)
/* (1) Store context */
if ctx == nil {
return nil, errors.New("Context must not be nil")
}
if ctx == nil { return nil, errors.New("Context must not be nil"); }
inst.ctx = ctx
/* (2) Get file management for KEY */
inst.fkey, err = Config(saveDir, "key")
if err != nil {
return nil, err
}
if err != nil { return nil, err }
/* (3) Get file management for SYNC */
inst.fsync, err = Config(saveDir, "sync")
if err != nil {
return nil, err
}
if err != nil { return nil, err }
/* (4) Restore from config */
inst.updateConfig()
@ -72,26 +66,16 @@ func (c *T) Send(w io.Writer) error {
x2 := make([]byte, scha.HSIZE)
err := c.generateRequest(x1, x2)
if err != nil {
return err
}
if err != nil { return err }
/* (2) Write request into writer */
written, err := w.Write(x1)
if err != nil {
return err
}
if written != len(x1) {
return errors.New("Cannot write x1")
}
if err != nil { return err }
if written != len(x1) { return errors.New("Cannot write x1") }
written, err = w.Write(x2)
if err != nil {
return err
}
if written != len(x1) {
return errors.New("Cannot write x2")
}
if err != nil { return err }
if written != len(x1) { return errors.New("Cannot write x2") }
return nil
}
@ -109,16 +93,10 @@ func (c *T) Receive(r io.Reader) error {
---------------------------------------------------------*/
errCode := make([]byte, 1)
read, err := r.Read(errCode)
if err != nil {
return err
}
if uint16(read) != 1 {
return errors.New("Cannot read error code")
}
if err != nil { return err }
if uint16(read) != 1 { return errors.New("Cannot read error code") }
if DEBUG {
fmt.Printf("ERROR CODE : %d\n", errCode[0])
}
if DEBUG { fmt.Printf("ERROR CODE : %d\n", errCode[0]) }
/* (2) Manage success
---------------------------------------------------------*/
@ -127,9 +105,7 @@ func (c *T) Receive(r io.Reader) error {
/* (1) If pending migration -> migrate */
if c.key.MigrationCode() == 2 {
c.migrateKey()
if DEBUG {
fmt.Printf("*** VALIDATED MIGRATION\n")
}
if DEBUG { fmt.Printf("*** VALIDATED MIGRATION\n") }
}
/* (2) No error anyway */
@ -142,37 +118,28 @@ func (c *T) Receive(r io.Reader) error {
/* (1) Read y1 */
y1 := make([]byte, scha.HSIZE)
read, err = r.Read(y1)
if err != nil {
return err
}
if uint16(read) != scha.HSIZE {
return errors.New("Cannot read y1")
}
if err != nil { return err }
if uint16(read) != scha.HSIZE { return errors.New("Cannot read y1") }
/* (2) Read y2 */
y2 := make([]byte, scha.HSIZE)
read, err = r.Read(y2)
if err != nil {
return err
}
if uint16(read) != scha.HSIZE {
return errors.New("Cannot read enough y2")
}
if err != nil { return err }
if uint16(read) != scha.HSIZE { return errors.New("Cannot read enough y2") }
/* (3) Manage rescue mode */
err = c.rescue(y1, y2)
if err != nil {
return err
}
if err != nil { return err }
if DEBUG {
fmt.Printf("*** MIGRATION PREPARED\n")
}
if DEBUG { fmt.Printf("*** MIGRATION PREPARED\n") }
return nil
}
/* (5) Returns a synchronisation key (first server connection)
*
* @return key<[]byte> Synchronisation key
@ -181,19 +148,17 @@ func (c *T) Receive(r io.Reader) error {
---------------------------------------------------------*/
func (c *T) SynchronisationKey() ([]byte, error) {
/* (1) Reset keys so no value can be guessed */
c.migrateKey() // 1: copies 'sync' into 'key'
c.migrateKey() // 2: copies random new 'sync' into 'key' (old 'sync)
/* (1) Reset keys so no value can be guessed*/
c.migrateKey(); // 1: copies 'sync' into 'key'
c.migrateKey(); // 2: copies random new 'sync' into 'key' (old 'sync)
/* (2) Get current hash */
hash, err := c.key.CurrentHash()
if err != nil {
return nil, err
}
if err != nil { return nil, err }
/* (3) Decrement key so 'hash' is valid */
c.key.Decrement()
/* (4) Return key */
return hash, nil
return hash, nil;
}

View File

@ -3,10 +3,10 @@ package client
import (
"encoding/binary"
"fmt"
"git.xdrm.io/logauth/schastsp/internal/keyset"
"git.xdrm.io/logauth/schastsp/internal/scha"
"git.xdrm.io/logauth/schastsp/internal/timeid"
"git.xdrm.io/logauth/schastsp/internal/xor"
"git.xdrm.io/schastsp/internal/keyset"
"git.xdrm.io/schastsp/internal/scha"
"git.xdrm.io/schastsp/internal/timeid"
"git.xdrm.io/schastsp/internal/xor"
)
/* (1) Updates 'key' and 'sync' with files
@ -27,9 +27,7 @@ func (c *T) updateConfig() {
err = c.fkey.Fetch(c.key)
/* (3) On error -> set key to NIL */
if err != nil {
c.key = nil
}
if err != nil { c.key = nil }
/* (4) Create default sync */
c.sync, err = keyset.Create(c.ctx)
@ -38,9 +36,7 @@ func (c *T) updateConfig() {
err = c.fsync.Fetch(c.sync)
/* (6) On error -> set sync to NIL */
if err != nil {
c.sync = nil
}
if err != nil { c.sync = nil }
/* (7) Exit if all keysets have been fetched */
if c.key != nil && c.sync != nil {
@ -49,6 +45,7 @@ func (c *T) updateConfig() {
}
/* (2) If cannot fetch -> create new keysets
---------------------------------------------------------*/
if c.key == nil {
@ -59,19 +56,16 @@ func (c *T) updateConfig() {
c.sync, _ = keyset.Create(c.ctx)
}
/* (3) Store current value
---------------------------------------------------------*/
/* (1) Store key */
err = c.fkey.Store(c.key)
if err != nil {
panic("Cannot store key")
}
if err != nil { panic("Cannot store key") }
/* (2) Store sync */
err = c.fsync.Store(c.sync)
if err != nil {
panic("Cannot store sync")
}
if err != nil { panic("Cannot store sync") }
}
@ -87,9 +81,7 @@ func (c *T) migrateKey() {
/* (2) Regenerate sync */
c.sync, err = keyset.Create(c.ctx)
if err != nil {
panic(err)
}
if err != nil { panic(err) }
/* (3) Store keysets to files */
c.updateConfig()
@ -103,9 +95,7 @@ func (c *T) generateKeyWithConstraints() {
/* Get current hash */
keyHash, err := c.key.CurrentHash()
if err != nil {
panic(err)
}
if err != nil { panic(err) }
/* Search key one is respects contraints */
for true {
@ -121,21 +111,19 @@ func (c *T) generateKeyWithConstraints() {
/* (3) Hash time id */
hashedTimeID, err := scha.Hash(timeIDBytes, 1)
if err != nil {
continue
}
if err != nil { continue }
/* (2) Generate a new sync
---------------------------------------------------------*/
newKey, _ := keyset.Create(c.ctx)
/* (3) Check constraints
---------------------------------------------------------*/
/* (1) Get next hash */
syncHash, err := newKey.CurrentHash()
if err != nil {
continue
}
if err != nil { continue }
/* (2) Get x1 */
x1 := make([]byte, scha.HSIZE)
@ -157,26 +145,18 @@ func (c *T) generateKeyWithConstraints() {
/* (2) Get time mod difference (first byte) */
timeConstraintValue := x[0]%2 == byte(timeMod)
if DEBUG {
fmt.Printf(" %.2x ^ %.2x = %.2x[%d] %% 2 = %d == %d ? %t\n", x1[0], x2[0], x[0], x[0], x[0]%2, timeMod, timeConstraintValue)
}
if DEBUG { fmt.Printf(" %.2x ^ %.2x = %.2x[%d] %% 2 = %d == %d ? %t\n", x1[0], x2[0], x[0], x[0], x[0]%2, timeMod, timeConstraintValue) }
/* (4) Retry if invalid time constraint */
if !timeConstraintValue {
continue
}
if !timeConstraintValue { continue }
/* (5) Get migration mod difference (second byte) */
migrationConstraintValue := x[1]%3 == byte(c.key.MigrationCode())
if DEBUG {
fmt.Printf(" %.2x ^ %.2x = %.2x[%d] %% 3 = %d == %d ? %t\n", x1[1], x2[1], x[1], x[1], x[1]%3, c.key.MigrationCode(), migrationConstraintValue)
}
if DEBUG { fmt.Printf(" %.2x ^ %.2x = %.2x[%d] %% 3 = %d == %d ? %t\n", x1[1], x2[1], x[1], x[1], x[1]%3, c.key.MigrationCode(), migrationConstraintValue) }
/* (6) Retry if invalid time constraint */
if !migrationConstraintValue {
continue
}
if !migrationConstraintValue { continue }
/* (7) Store new sync */
c.sync = newKey
@ -235,9 +215,7 @@ func (c *T) generateRequest(x1 []byte, x2 []byte) error {
---------------------------------------------------------*/
/* (1) Store current hash */
h0, err := c.key.CurrentHash()
if err != nil {
return err
}
if err != nil { return err }
/* (2) Copy into next hash (same value) */
h1 := make([]byte, scha.HSIZE)
@ -248,9 +226,7 @@ func (c *T) generateRequest(x1 []byte, x2 []byte) error {
// 2. Else -> use 'sync'
if c.key.MigrationCode() > 0 {
h1, err = c.sync.CurrentHash()
if err != nil {
return err
}
if err != nil { return err }
}
/* (5) Manage time id
@ -326,18 +302,14 @@ func (c *T) rescue(y1 []byte, y2 []byte) error {
/* (3) Hash timeId */
hashedTimeID, err := scha.Hash(timeIDBytes, 1)
if err != nil {
return err
}
if err != nil { return err }
/* (4) Get the received hash */
receivedHash := xor.ByteArray(y1, hashedTimeID)
/* (5) Try to rescue the key */
err = c.key.Rescue(receivedHash)
if err != nil {
return err
}
if err != nil { return err }
/* (6) Store updated key */
c.updateConfig()

View File

@ -1,13 +1,14 @@
package client
package client;
import (
"errors"
"git.xdrm.io/schastsp/internal/keyset"
"fmt"
"git.xdrm.io/logauth/schastsp/internal/keyset"
"os"
"path/filepath"
"errors"
"os"
)
type config struct {
path string // absolute configuration file path
}
@ -20,54 +21,47 @@ type config struct {
---------------------------------------------------------*/
func Config(dir string, name string) (*config, error) {
inst := new(config)
inst := new(config);
/* (1) Build full path */
absoluteDir, err := filepath.Abs(dir)
if err != nil {
return nil, err
}
absoluteDir, err := filepath.Abs(dir);
if err != nil { return nil, err; }
basename := filepath.Base(name);
fullpath := fmt.Sprintf("%s/%s", absoluteDir, basename);
basename := filepath.Base(name)
fullpath := fmt.Sprintf("%s/%s", absoluteDir, basename)
/* (2) Check directory */
if info, err := os.Stat(absoluteDir); err != nil {
// Unknown error
if !os.IsNotExist(err) {
return nil, err
}
if !os.IsNotExist(err) { return nil, err }
// not exists -> try to create dir
err2 := os.MkdirAll(absoluteDir, 0755)
if err2 != nil {
return nil, err2
}
err2 := os.MkdirAll(absoluteDir, 0755);
if err2 != nil { return nil, err2 }
// fail if not a directory
// fail if not a directory
} else if !info.Mode().IsDir() {
return nil, errors.New("Configuration dir is not a directory")
return nil, errors.New("Configuration dir is not a directory");
}
/* (3) Check file */
info, err := os.Stat(fullpath)
info, err := os.Stat(fullpath);
if err != nil {
// Unknown error
if !os.IsNotExist(err) {
return nil, err
}
if !os.IsNotExist(err) { return nil, err }
// File does not exist -> try to create file
_, err2 := os.Create(fullpath)
if err2 != nil {
return nil, err2
}
_, err2 := os.Create(fullpath);
if err2 != nil { return nil, err2 }
// fail if exists but not regular file
// fail if exists but not regular file
} else if !info.Mode().IsRegular() {
return nil, errors.New("Configuration file is not a regular file")
return nil, errors.New("Configuration file is not a regular file");
}
inst.path = fullpath
@ -76,6 +70,7 @@ func Config(dir string, name string) (*config, error) {
}
/* (2) Fetches a keyset from a file
*
* @ks<*keyset.T> Set to fetch into
@ -88,23 +83,20 @@ func (c config) Fetch(ks *keyset.T) (err error) {
/* (1) Open file */
file, err := os.Open(c.path)
if err != nil {
return err
}
if err != nil { return err }
/* (2) Defer close */
defer file.Close()
/* (3) Fetch from file */
err = ks.Fetch(file)
if err != nil {
return err
}
err = ks.Fetch(file);
if err != nil { return err }
return nil
}
/* (3) Stores a keyset into file
*
* @ks<*keyset.T> Set to store
@ -116,20 +108,16 @@ func (c config) Fetch(ks *keyset.T) (err error) {
func (c config) Store(ks *keyset.T) error {
/* (1) Open file */
file, err := os.OpenFile(c.path, os.O_RDWR|os.O_CREATE, 0755)
if err != nil {
return err
}
file, err := os.OpenFile(c.path, os.O_RDWR|os.O_CREATE, 0755);
if err != nil { return err }
/* (2) Defer close */
defer file.Close()
/* (3) Store into file */
err = ks.Store(file)
if err != nil {
return err
}
if err != nil { return err }
return nil
}
}

View File

@ -1,38 +1,38 @@
package main
import (
"time"
"os"
"flag"
"fmt"
"git.xdrm.io/logauth/schastsp/client"
"git.xdrm.io/logauth/schastsp/context"
"os"
"time"
"git.xdrm.io/schastsp/context"
"git.xdrm.io/schastsp/client"
)
func main() {
func main(){
executionStart := time.Now().UnixNano()
/* (1) Flag management (cli arguments)
---------------------------------------------------------*/
/* (1) Secret folder */
clientConfigPath := flag.String("file", "/tmp/schastsp_keys", "Configuration folder, it will contain sensitive data (keys), make sure to control it properly.")
clientConfigPath := flag.String("file", "/tmp/schastsp_keys", "Configuration folder, it will contain sensitive data (keys), make sure to control it properly. (default: /tmp/schastsp_keys)")
/* (2) request | response */
isRequest := flag.Bool("req", false, "Will generate a request into standard output.")
isResponse := flag.Bool("res", false, "Will proceed a response management from standard input.")
/* (3) Context window size */
winSize := flag.Uint("win", 2000, "Time window value in ms.")
winSize := flag.Uint("win", 2000, "Time window value in ms. (default: 2000)")
/* (4) Context minimum depth value */
minDepth := flag.Uint("min", 0x0f0, "Minimum depth value.")
minDepth := flag.Uint("min", 0x0f0, "Minimum depth value. (default: 240)")
/* (5) Context maximum depth value */
maxDepth := flag.Uint("max", 0xfff, "Maximum depth value.")
maxDepth := flag.Uint("max", 0xfff, "Maximum depth value. (default: 4095)")
/* (6) Context depth threshold */
thrDepth := flag.Uint("thr", 0x00a, "Depth threshold protecting minimum depth to be reached.")
thrDepth := flag.Uint("thr", 0x00a, "Depth threshold protecting minimum depth to be reached. (default: 10)")
/* (7) Synchronisation request (special order) */
syncRequest := flag.Bool("sync", false, "If set, proceeds a synchronisation request and outputs the synchronisation key.")
@ -40,81 +40,72 @@ func main() {
/* (8) Parse flags */
flag.Parse()
/* (2) Create context + client
---------------------------------------------------------*/
/* (1) Create context */
ctx, err := context.Create(float64(*winSize)/1e3, uint16(*minDepth), uint16(*thrDepth), uint16(*maxDepth))
if err != nil {
os.Stderr.WriteString(fmt.Sprintf("[CLIENT_ERROR:context] %s\n", err))
return
}
ctx, err := context.Create( float64(*winSize) / 1e3, uint16(*minDepth), uint16(*thrDepth), uint16(*maxDepth) );
if err != nil { os.Stderr.WriteString( fmt.Sprintf("[CLIENT_ERROR:context] %s\n", err) ); return }
/* (2) Create client */
cli, err := client.New(ctx, *clientConfigPath)
if err != nil {
os.Stderr.WriteString(fmt.Sprintf("[CLIENT_ERROR:client] %s\n", err))
return
}
if err != nil { os.Stderr.WriteString( fmt.Sprintf("[CLIENT_ERROR:client] %s\n", err) ); return }
/* (3) Dispatch execution
---------------------------------------------------------*/
/* (1) If synchronisation request */
if *syncRequest {
synchronisationRequest(cli)
synchronisationRequest(cli);
/* (2) If request */
/* (2) If request */
} else if *isRequest {
err = cli.Send(os.Stdout)
if err != nil {
os.Stderr.WriteString(fmt.Sprintf("[CLIENT_ERROR:request] %s\n", err))
}
if err != nil { os.Stderr.WriteString( fmt.Sprintf("[CLIENT_ERROR:request] %s\n", err) ) }
/* (3) If response */
/* (3) If response */
} else if *isResponse {
err = cli.Receive(os.Stdin)
if err != nil {
os.Stderr.WriteString(fmt.Sprintf("[CLIENT_ERROR:response] %s\n", err))
return
}
if err != nil { os.Stderr.WriteString( fmt.Sprintf("[CLIENT_ERROR:response] %s\n", err) ); return }
/* (4) Else -> nothing */
} else {
os.Stderr.WriteString(fmt.Sprintf("Missing argument.\n\nYou must give one of the 3 available actions :\n -req to manage a request\n -res to manage a response\n -sync to get a synchronisation key\n"))
/* (4) Else -> nothing */
}else {
os.Stderr.WriteString( fmt.Sprintf("Missing argument.\n\nYou must give one of the 3 available actions :\n -req to manage a request\n -res to manage a response\n -sync to get a synchronisation key\n") )
}
executionStop := time.Now().UnixNano()
nsElapsed := float64(executionStop - executionStart)
usElapsed := float64(nsElapsed / 1e3)
msElapsed := float64(usElapsed / 1e3)
sElapsed := float64(msElapsed / 1e3)
nsElapsed := float64(executionStop - executionStart)
usElapsed := float64(nsElapsed / 1e3)
msElapsed := float64(usElapsed / 1e3)
sElapsed := float64(msElapsed / 1e3)
if sElapsed >= 1 {
os.Stderr.WriteString(fmt.Sprintf("executed in %.3f s\n", sElapsed))
} else if msElapsed >= 1 {
os.Stderr.WriteString(fmt.Sprintf("executed in %.3f ms\n", msElapsed))
} else if usElapsed >= 1 {
os.Stderr.WriteString(fmt.Sprintf("executed in %.3f us\n", usElapsed))
} else if nsElapsed >= 1 {
os.Stderr.WriteString(fmt.Sprintf("executed in %.3f ns\n", nsElapsed))
}
if sElapsed >= 1 { os.Stderr.WriteString( fmt.Sprintf("executed in %.3f s\n", sElapsed) )
} else if msElapsed >= 1 { os.Stderr.WriteString( fmt.Sprintf("executed in %.3f ms\n", msElapsed) )
} else if usElapsed >= 1 { os.Stderr.WriteString( fmt.Sprintf("executed in %.3f us\n", usElapsed) )
} else if nsElapsed >= 1 { os.Stderr.WriteString( fmt.Sprintf("executed in %.3f ns\n", nsElapsed) ) }
return
}
func synchronisationRequest(cli *client.T) {
func synchronisationRequest(cli *client.T){
/* (1) Get synchronisation key */
syncKey, err := cli.SynchronisationKey()
if err != nil {
fmt.Errorf("[CLIENT_ERROR:syncKey] %s\n", err)
return
}
if err != nil { fmt.Errorf("[CLIENT_ERROR:syncKey] %s\n", err); return }
/* (2) Print synchronisation key */
os.Stdout.Write(syncKey)
return
return;
}
}

View File

@ -1,16 +1,16 @@
package main
import (
"git.xdrm.io/schastsp/internal/scha"
"time"
"os"
"flag"
"fmt"
"git.xdrm.io/logauth/schastsp/context"
"git.xdrm.io/logauth/schastsp/internal/scha"
"git.xdrm.io/logauth/schastsp/server"
"os"
"time"
"git.xdrm.io/schastsp/context"
"git.xdrm.io/schastsp/server"
)
func main() {
func main(){
executionStart := time.Now().UnixNano()
@ -37,81 +37,69 @@ func main() {
/* (7) Parse flags */
flag.Parse()
/* (2) Create context + client
---------------------------------------------------------*/
/* (1) Create context */
ctx, err := context.Create(float64(*winSize)/1e3, uint16(*minDepth), uint16(*thrDepth), uint16(*maxDepth))
if err != nil {
os.Stderr.WriteString(fmt.Sprintf("[SERVER_ERROR:context] %s\n", err))
return
}
ctx, err := context.Create( float64(*winSize) / 1e3, uint16(*minDepth), uint16(*thrDepth), uint16(*maxDepth) );
if err != nil { os.Stderr.WriteString( fmt.Sprintf("[SERVER_ERROR:context] %s\n", err) ); return }
/* (2) Create server */
ser, err := server.New(ctx, *serverConfigPath)
if err != nil {
os.Stderr.WriteString(fmt.Sprintf("[SERVER_ERROR:server] %s\n", err))
return
}
if err != nil { os.Stderr.WriteString( fmt.Sprintf("[SERVER_ERROR:server] %s\n", err) ); return }
/* (3) Dispatch execution
---------------------------------------------------------*/
/* (1) If synchronisation request */
if *syncRequest {
synchronisationCommit(ser)
synchronisationCommit(ser);
/* (2) If request handling */
/* (2) If request handling */
} else {
err = ser.HandleRequest(os.Stdin, os.Stdout)
if err != nil {
os.Stderr.WriteString(fmt.Sprintf("[SERVER_ERROR:request] %s\n", err))
}
if err != nil { os.Stderr.WriteString( fmt.Sprintf("[SERVER_ERROR:request] %s\n", err) ) }
}
executionStop := time.Now().UnixNano()
nsElapsed := float64(executionStop - executionStart)
usElapsed := float64(nsElapsed / 1e3)
msElapsed := float64(usElapsed / 1e3)
sElapsed := float64(msElapsed / 1e3)
nsElapsed := float64(executionStop - executionStart)
usElapsed := float64(nsElapsed / 1e3)
msElapsed := float64(usElapsed / 1e3)
sElapsed := float64(msElapsed / 1e3)
if sElapsed >= 1 {
os.Stderr.WriteString(fmt.Sprintf("executed in %.3f s\n", sElapsed))
} else if msElapsed >= 1 {
os.Stderr.WriteString(fmt.Sprintf("executed in %.3f ms\n", msElapsed))
} else if usElapsed >= 1 {
os.Stderr.WriteString(fmt.Sprintf("executed in %.3f us\n", usElapsed))
} else if nsElapsed >= 1 {
os.Stderr.WriteString(fmt.Sprintf("executed in %.3f ns\n", nsElapsed))
}
if sElapsed >= 1 { os.Stderr.WriteString( fmt.Sprintf("executed in %.3f s\n", sElapsed) )
} else if msElapsed >= 1 { os.Stderr.WriteString( fmt.Sprintf("executed in %.3f ms\n", msElapsed) )
} else if usElapsed >= 1 { os.Stderr.WriteString( fmt.Sprintf("executed in %.3f us\n", usElapsed) )
} else if nsElapsed >= 1 { os.Stderr.WriteString( fmt.Sprintf("executed in %.3f ns\n", nsElapsed) ) }
return
}
func synchronisationCommit(ser *server.T) {
func synchronisationCommit(ser *server.T){
/* (1) Try to read key from standard input */
syncKey := make([]byte, scha.HSIZE)
read, err := os.Stdin.Read(syncKey)
if err != nil {
os.Stderr.WriteString(fmt.Sprintf("[SERVER_ERROR:syncKey] %s\n", err))
return
}
if uint16(read) != scha.HSIZE {
os.Stderr.WriteString(fmt.Sprintf("[SERVER_ERROR:syncKey] Cannot read proper key size\n"))
return
}
if err != nil { os.Stderr.WriteString( fmt.Sprintf("[SERVER_ERROR:syncKey] %s\n", err) ); return }
if uint16(read) != scha.HSIZE { os.Stderr.WriteString( fmt.Sprintf("[SERVER_ERROR:syncKey] Cannot read proper key size\n") ); return }
/* (2) Store synchronisation key */
err = ser.SynchronisationKey(syncKey)
if err != nil {
os.Stderr.WriteString(fmt.Sprintf("[SERVER_ERROR:syncKey] %s\n", err))
return
}
if err != nil { os.Stderr.WriteString( fmt.Sprintf("[SERVER_ERROR:syncKey] %s\n", err) ); return }
/* (3) Debug */
os.Stdout.WriteString("Synchronisation key successfully written\n")
return
return;
}
}

View File

@ -1,41 +1,41 @@
package main
import (
"time"
"bytes"
"os"
"flag"
"fmt"
"git.xdrm.io/logauth/schastsp/client"
"git.xdrm.io/logauth/schastsp/context"
"git.xdrm.io/logauth/schastsp/server"
"os"
"time"
"git.xdrm.io/schastsp/context"
"git.xdrm.io/schastsp/client"
"git.xdrm.io/schastsp/server"
)
/* Store target config paths */
var clientConfigPath = "/tmp/cyclichash/client/"
var serverConfigPath = "/tmp/cyclichash/server/lastkey"
var clientConfigPath = "/tmp/cyclichash/client/";
var serverConfigPath = "/tmp/cyclichash/server/lastkey";
func main() {
func main(){
executionStart := time.Now().UnixNano()
/* (1) Create context
---------------------------------------------------------*/
/* (1) Create it */
ctx, err := context.Create(2, 0x0f0, 0x0a, 0xfff)
if err != nil {
fmt.Printf("[ERROR:context] %s\n", err)
return
}
ctx, err := context.Create(2, 0x0f0, 0x0a, 0xfff);
if err != nil { fmt.Printf("[ERROR:context] %s\n", err); return }
/* (2) Manage flags (cli arguments)
---------------------------------------------------------*/
/* (1) Define 'sync' mode */
var sync bool
var sync bool;
flag.BoolVar(&sync, "sync", false, "If set, a new synchronisation key will be outputed\nNote: it will break existing systems")
/* (2) Define connection 'latency' */
var latency int64
var latency int64;
flag.Int64Var(&latency, "t", 0, "Connection latency in ms")
/* (3) Parse cli arguments */
@ -48,106 +48,86 @@ func main() {
testCommunication(ctx, latency)
}
executionStop := time.Now().UnixNano()
nsElapsed := float64(executionStop - executionStart)
usElapsed := float64(nsElapsed / 1e3)
msElapsed := float64(usElapsed / 1e3)
sElapsed := float64(msElapsed / 1e3)
if sElapsed >= 1 {
fmt.Printf("executed in %.3f s\n", sElapsed)
} else if msElapsed >= 1 {
fmt.Printf("executed in %.3f ms\n", msElapsed)
} else if usElapsed >= 1 {
fmt.Printf("executed in %.3f us\n", usElapsed)
} else if nsElapsed >= 1 {
fmt.Printf("executed in %.3f ns\n", nsElapsed)
}
executionStop := time.Now().UnixNano()
nsElapsed := float64(executionStop - executionStart)
usElapsed := float64(nsElapsed / 1e3)
msElapsed := float64(usElapsed / 1e3)
sElapsed := float64(msElapsed / 1e3)
if sElapsed >= 1 { fmt.Printf("executed in %.3f s\n", sElapsed)
} else if msElapsed >= 1 { fmt.Printf("executed in %.3f ms\n", msElapsed)
} else if usElapsed >= 1 { fmt.Printf("executed in %.3f us\n", usElapsed)
} else if nsElapsed >= 1 { fmt.Printf("executed in %.3f ns\n", nsElapsed) }
return
}
func synchronise(ctx *context.T) {
func synchronise(ctx *context.T){
/* (1) Create client */
client, err := client.New(ctx, clientConfigPath)
if err != nil {
fmt.Printf("[ERROR:client] %s\n", err)
return
}
client, err := client.New(ctx, clientConfigPath);
if err != nil { fmt.Printf("[ERROR:client] %s\n", err); return }
/* (2) Get synchronisation key */
syncKey, err := client.SynchronisationKey()
if err != nil {
fmt.Printf("[ERROR:syncKey] %s\n", err)
return
}
if err != nil { fmt.Printf("[ERROR:syncKey] %s\n", err); return }
/* (3) Store synchronisation key */
conf, err := os.OpenFile(serverConfigPath, os.O_RDWR|os.O_CREATE, 0775)
if err != nil {
fmt.Printf("[ERROR:syncKey:storage] %s\n", err)
return
}
if err != nil { fmt.Printf("[ERROR:syncKey:storage] %s\n", err); return }
defer conf.Close()
conf.Write(syncKey)
fmt.Printf("Synchronisation Key stored\n")
fmt.Printf("Synchronisation Key stored\n");
return
return;
}
func testCommunication(ctx *context.T, latency int64) {
func testCommunication(ctx *context.T, latency int64){
/* (1) Setup
---------------------------------------------------------*/
/* (1) Create client */
client, err := client.New(ctx, clientConfigPath)
if err != nil {
fmt.Printf("[ERROR:client] %s\n", err)
return
}
client, err := client.New(ctx, clientConfigPath);
if err != nil { fmt.Printf("[ERROR:client] %s\n", err); return }
/* (2) Create server */
server, err := server.New(ctx, serverConfigPath)
if err != nil {
fmt.Printf("[ERROR:server] %s\n", err)
return
}
if err != nil { fmt.Printf("[ERROR:server] %s\n", err); return }
/* (3) Create emulation buffers */
requestSocket, responseSocket := new(bytes.Buffer), new(bytes.Buffer)
/* (2) REAL STUFF HAPPENS HERE
---------------------------------------------------------*/
/* (1) Request | client send */
err = client.Send(requestSocket)
if err != nil {
fmt.Printf("[ERROR:request:send] %s\n", err)
return
}
if err != nil { fmt.Printf("[ERROR:request:send] %s\n", err); return }
/* (2) Emulate latency */
time.Sleep(time.Duration(latency) * time.Second / 1e3)
time.Sleep( time.Duration(latency) * time.Second / 1e3)
/* (3) Request | server receive */
/* Response | server send */
err = server.HandleRequest(requestSocket, responseSocket)
if err != nil {
fmt.Printf("[ERROR:request:receive] %s\n", err)
return
}
if err != nil { fmt.Printf("[ERROR:request:receive] %s\n", err); return }
/* (4) Response | client receive */
err = client.Receive(responseSocket)
if err != nil {
fmt.Printf("[ERROR:response:receive] %s\n", err)
return
}
if err != nil { fmt.Printf("[ERROR:response:receive] %s\n", err); return }
return
return;
}
}

View File

@ -11,12 +11,13 @@ const DefaultMin = 0x00f0
const DefaultMax = 0x0fff
const DefaultThr = 0x000a
/* (2) Struct attributes */
type T struct {
win float64 // 'timeid' window size
min uint16 // minimum scha depth
max uint16 // maximum scha depth
thr uint16 // scha depth threshold
win float64; // 'timeid' window size
min uint16; // minimum scha depth
max uint16; // maximum scha depth
thr uint16; // scha depth threshold
}
/* (3) Constructor
@ -29,15 +30,13 @@ type T struct {
* @return outName<outType> outDesc
*
---------------------------------------------------------*/
func Create(win float64, optional ...uint16) (*T, error) {
func Create(win float64, optional... uint16) (*T, error) {
var inst = new(T)
var inst = new(T);
/* (1) Window size error */
if win < 0 {
return nil, errors.New("Window size must be positive and is negative")
}
inst.win = win
if win < 0 { return nil, errors.New("Window size must be positive and is negative") }
inst.win = win;
/* (2) Default values */
inst.min = DefaultMin
@ -50,13 +49,13 @@ func Create(win float64, optional ...uint16) (*T, error) {
if optional[0] < 0x0f {
return nil, errors.New("Minimum depth must be greater than 0x0f (decimal 15) for consistency issues")
}
inst.min = optional[0]
inst.min = optional[0];
}
/* (4) Optional 'thr' */
if len(optional) > 1 {
inst.thr = optional[1]
inst.thr = optional[1];
}
/* (5) Optional 'max' */
@ -65,7 +64,7 @@ func Create(win float64, optional ...uint16) (*T, error) {
if optional[2] <= inst.min+inst.thr {
return nil, errors.New("Minimum depth must be greater than 0x0f (decimal 15) for consistency issues")
}
inst.max = optional[2]
inst.max = optional[2];
}
@ -73,7 +72,7 @@ func Create(win float64, optional ...uint16) (*T, error) {
}
/* (4) Getters */
func (c T) Window() float64 { return c.win }
func (c T) MinDepth() uint16 { return c.min }
func (c T) MaxDepth() uint16 { return c.max }
func (c T) DepthThreshold() uint16 { return c.thr }
func (c T) Window() float64 { return c.win }
func (c T) MinDepth() uint16 { return c.min }
func (c T) MaxDepth() uint16 { return c.max }
func (c T) DepthThreshold() uint16 { return c.thr }

View File

@ -4,13 +4,14 @@ import (
"testing"
)
func TestDefaultArguments(t *testing.T) {
ctx, err := Create(2.2)
ctx, err := Create(2.2);
if err != nil {
t.Errorf("[Unexpected error]: %s", err)
return
t.Errorf("[Unexpected error]: %s", err);
return;
}
// check all optional arguments
if ctx.Window() != 2.2 {
@ -37,34 +38,34 @@ func TestDefaultArguments(t *testing.T) {
func TestOptionalMinConstraint(t *testing.T) {
ctx, err := Create(2.2, 0x0f)
ctx, err := Create(2.2, 0x0f);
if err != nil {
t.Errorf("[Unexpected error]: %s", err)
return
t.Errorf("[Unexpected error]: %s", err);
return;
}
ctx, err = Create(2.2, 0x0f-1)
ctx, err = Create(2.2, 0x0f-1);
if err == nil {
t.Errorf("Expected an error with 'min' < %d ; got min=%d", 0x0f, ctx.MinDepth())
return
t.Errorf("Expected an error with 'min' < %d ; got min=%d", 0x0f, ctx.MinDepth());
return;
}
}
func TestOptionalMaxConstraint(t *testing.T) {
ctx, err := Create(2.2, 0xf1, 0x02, 0xf1+0x02+1)
ctx, err := Create(2.2, 0xf1, 0x02, 0xf1+0x02+1);
if err != nil {
t.Errorf("[Unexpected error]: %s", err)
return
t.Errorf("[Unexpected error]: %s", err);
return;
}
ctx, err = Create(2.2, 0xf1, 0x02, 0xf1+0x02)
ctx, err = Create(2.2, 0xf1, 0x02, 0xf1+0x02);
if err == nil {
t.Errorf("Expected an error with 'max' > 'min'+'thr' ; got max=%d, min=%d, thr=%d", ctx.MinDepth(), ctx.MaxDepth(), ctx.DepthThreshold())
return
t.Errorf("Expected an error with 'max' > 'min'+'thr' ; got max=%d, min=%d, thr=%d", ctx.MinDepth(), ctx.MaxDepth(), ctx.DepthThreshold());
return;
}
}
}

View File

@ -3,8 +3,8 @@ package keyset
import (
"encoding/binary"
"errors"
"git.xdrm.io/logauth/schastsp/context"
"git.xdrm.io/logauth/schastsp/internal/scha"
"git.xdrm.io/schastsp/context"
"git.xdrm.io/schastsp/internal/scha"
"io"
)
@ -230,9 +230,7 @@ func (s *T) Rescue(lastHash []byte) error {
/* (1) Process hash */
currentHash, err := scha.Hash(s.sec, i)
if err != nil {
return err
}
if err != nil { return err }
/* (2) If not found -> try again */
if string(currentHash) != string(lastHash) {

View File

@ -5,6 +5,7 @@ import (
"time"
)
/* (2) Generates a pseudo-random KeySet
*
---------------------------------------------------------*/
@ -26,7 +27,7 @@ func (s *T) generate() {
/* (2) Manage other attributes
---------------------------------------------------------*/
/* (1) Random depth pick init */
var randMin, randMax = s.ctx.MinDepth() + (s.ctx.MaxDepth()-s.ctx.MinDepth())/2, s.ctx.MaxDepth()
var randMin, randMax = s.ctx.MinDepth() + (s.ctx.MaxDepth()-s.ctx.MinDepth()) / 2, s.ctx.MaxDepth()
/* (2) Select "random" depth */
s.depth = randMin + uint16(rand.Intn(int(randMax+1-randMin)))
@ -34,4 +35,4 @@ func (s *T) generate() {
/* (3) Reset comsumption level */
s.mcode = 0
}
}

View File

@ -2,8 +2,8 @@ package keyset
import (
"bytes"
"git.xdrm.io/logauth/schastsp/context"
"git.xdrm.io/logauth/schastsp/internal/scha"
"git.xdrm.io/schastsp/context"
"git.xdrm.io/schastsp/lib/scha"
"testing"
)

View File

@ -1,16 +1,19 @@
package scha
import (
"crypto/sha512"
"errors"
"git.xdrm.io/logauth/schastsp/internal/xor"
"crypto/sha512"
"git.xdrm.io/schastsp/internal/xor"
)
/* (0) Static
---------------------------------------------------------*/
/* (1) Constants */
const HSIZE uint16 = sha512.Size
const HBITSIZE uint32 = uint32(HSIZE) * 8
const HSIZE uint16 = sha512.Size;
const HBITSIZE uint32 = uint32(HSIZE) * 8;
/* (1) Basic hash function
*
@ -19,22 +22,23 @@ const HBITSIZE uint32 = uint32(HSIZE) * 8
* @return digest<[]byte]> Byte array digest
*
---------------------------------------------------------*/
func hash(input []byte) []byte {
func hash(input []byte) []byte{
/* (1) Create sha512 hasher */
hasher := sha512.New()
hasher := sha512.New();
/* (2) Defer memory cleanup */
defer hasher.Reset()
defer hasher.Reset();
/* (3) Set input to be hashed */
hasher.Write(input)
hasher.Write(input);
/* (4) Extract digest */
return hasher.Sum(nil)
return hasher.Sum(nil);
}
/* (2) Public hashing interface
*
* @input<[]byte> Byte array input
@ -46,33 +50,32 @@ func hash(input []byte) []byte {
* @return err<error> If consistence error
*
---------------------------------------------------------*/
func Hash(input []byte, depth uint16, options ...[]byte) ([]byte, error) {
func Hash(input []byte, depth uint16, options... []byte) ([]byte, error) {
/* (1) Manage errors errors
---------------------------------------------------------*/
/* (1) Avoid no depth (no hash at all) */
if depth < 1 {
return nil, errors.New("Cannot use a 'depth' of zero. This is inconsistent and means that no hash will be processed")
if( depth < 1 ){
return nil, errors.New("Cannot use a 'depth' of zero. This is inconsistent and means that no hash will be processed");
}
/* (2) Avoir empty input */
if len(input) < 1 {
return nil, errors.New("Cannot use an empty 'input'. This is inconsistent")
if( len(input) < 1 ){
return nil, errors.New("Cannot use an empty 'input'. This is inconsistent");
}
/* (2) Extract optional arguments
---------------------------------------------------------*/
/* (1) Optional salt */
salt := make([]byte, 0)
if len(options) > 0 {
salt = options[0]
}
if len(options) > 0 { salt = options[0] }
/* (2) Optional pepper */
pepper := make([]byte, 0)
if len(options) > 0 {
pepper = options[0]
}
if len(options) > 0 { pepper = options[0] }
/* (3) Process cyclic hash
---------------------------------------------------------*/
@ -80,14 +83,14 @@ func Hash(input []byte, depth uint16, options ...[]byte) ([]byte, error) {
digest := make([]byte, 0, HSIZE)
/* (2) Process first hash + salt */
digest = hash(xor.ByteArray(input, salt))
digest = hash( xor.ByteArray(input, salt) )
/* (3) Iterate @depth times */
for depth--; depth > 0; depth-- {
// Add Pepper only for last time
if depth == 1 {
digest = hash(xor.ByteArray(digest, pepper))
if( depth == 1 ){
digest = hash( xor.ByteArray(digest, pepper) )
} else {
digest = hash(digest)
}
@ -95,4 +98,4 @@ func Hash(input []byte, depth uint16, options ...[]byte) ([]byte, error) {
}
return digest, nil
}
}

View File

@ -2,147 +2,155 @@ package scha
import "testing"
func TestSimpleHash(t *testing.T) {
func TestSimpleHash(t *testing.T){
input := []byte("somePlainText")
input := []byte("somePlainText");
expected := []byte{
0x4c, 0xcb, 0x0e, 0xf6, 0x81, 0x99, 0x2e, 0xd6, 0xb8, 0x17, 0x52, 0x1d, 0x09,
0x6e, 0x99, 0x19, 0xe7, 0xda, 0x50, 0xc8, 0xbf, 0x64, 0xae, 0xc1, 0x4f, 0xaa,
0x47, 0x06, 0xf3, 0x49, 0x30, 0x8a, 0x90, 0x8e, 0xd2, 0xff, 0xc2, 0x6d, 0xee,
0xaa, 0xd6, 0x45, 0xd8, 0xb3, 0x17, 0xe3, 0xb9, 0x45, 0x29, 0x26, 0xe2, 0x8e,
0x99, 0x50, 0x94, 0x49, 0x90, 0x02, 0xa5, 0x61, 0x4a, 0x3f, 0x5e, 0xfa}
got, err := Hash(input, 1)
digestLength := uint(len(got))
0x99, 0x50, 0x94, 0x49, 0x90, 0x02, 0xa5, 0x61, 0x4a, 0x3f, 0x5e, 0xfa};
got, err := Hash(input, 1);
digestLength := uint(len(got));
/* (2) Fail on errors */
if err != nil {
t.Errorf("Expected no errors, got: %s", err)
t.Errorf("Expected no errors, got: %s", err);
}
/* (2) Fail on wrong size */
if uint16(digestLength) != HSIZE {
t.Errorf("Expected hash digest of %d bytes ; %d bytes received", HSIZE, digestLength)
if uint16(digestLength) != HSIZE {
t.Errorf("Expected hash digest of %d bytes ; %d bytes received", HSIZE, digestLength);
}
/* (3) Check each byte */
for k, v := range got {
for k, v := range got{
if v != expected[k] {
t.Errorf("Expected sha[%d] of '%x' to be '%x' ; received '%x'", HSIZE, input, expected, got)
return
t.Errorf("Expected sha[%d] of '%x' to be '%x' ; received '%x'", HSIZE, input, expected, got);
return;
}
}
}
func TestDepth1Plus1Equals2(t *testing.T) {
input := []byte("someOtherPlainText")
func TestDepth1Plus1Equals2(t *testing.T){
input := []byte("someOtherPlainText");
/* (1) Calculate H1 */
h1, err := Hash(input, 1)
if err != nil {
t.Errorf("Expected no error")
return
t.Errorf("Expected no error");
return;
}
/* (2) Calculate H1(H1) */
h11, err := Hash(h1, 1)
h11, err := Hash(h1, 1);
if err != nil {
t.Errorf("Expected no error")
return
t.Errorf("Expected no error");
return;
}
/* (3) Calculate H2 */
h2, err := Hash(input, 1+1)
h2, err := Hash(input, 1+1);
if err != nil {
t.Errorf("Expected no error")
return
t.Errorf("Expected no error");
return;
}
/* (4) Manage different length */
if len(h11) != len(h2) || len(h11) != int(HSIZE) {
t.Errorf("Expected digest lengths to be %d, got %d and %d", HSIZE, len(h11), len(h2))
return
t.Errorf("Expected digest lengths to be %d, got %d and %d", HSIZE, len(h11), len(h2));
return;
}
/* (5) Compare the 2 strings */
for k, v := range h11 {
if v != h2[k] {
t.Errorf("Expected h2() to be equal to h1(h1())\n got '%x'\n expected '%x'", h2, h11)
return
return;
}
}
}
func TestDepth52Plus64Equals116(t *testing.T) {
input := []byte("someOtherPlainText")
func TestDepth52Plus64Equals116(t *testing.T){
input := []byte("someOtherPlainText");
/* (1) Calculate H52 */
h52, err := Hash(input, 52)
if err != nil {
t.Errorf("Expected no error")
return
t.Errorf("Expected no error");
return;
}
/* (2) Calculate H52(H64) */
h5264, err := Hash(h52, 64)
h5264, err := Hash(h52, 64);
if err != nil {
t.Errorf("Expected no error")
return
t.Errorf("Expected no error");
return;
}
/* (3) Calculate H116 */
h116, err := Hash(input, 52+64)
h116, err := Hash(input, 52+64);
if err != nil {
t.Errorf("Expected no error")
return
t.Errorf("Expected no error");
return;
}
/* (4) Manage different length */
if len(h5264) != len(h116) || len(h5264) != int(HSIZE) {
t.Errorf("Expected digest lengths to be %d, got %d and %d", HSIZE, len(h5264), len(h116))
return
t.Errorf("Expected digest lengths to be %d, got %d and %d", HSIZE, len(h5264), len(h116));
return;
}
/* (5) Compare the 2 strings */
for k, v := range h5264 {
if v != h116[k] {
t.Errorf("Expected h116() to be equal to h52(h64())\n got '%x'\n expected '%x'", h116, h5264)
return
return;
}
}
}
func TestDepthError(t *testing.T) {
input := []byte("somePlainText")
_, err := Hash(input, 0)
func TestDepthError(t *testing.T){
input := []byte("somePlainText");
_, err := Hash(input, 0);
/* (2) Fail on errors */
if err == nil {
t.Errorf("Expected an error for depth of 0")
t.Errorf("Expected an error for depth of 0");
}
}
func TestEmptyInputError(t *testing.T) {
_, err := Hash(nil, 1)
func TestEmptyInputError(t *testing.T){
_, err := Hash(nil, 1);
/* (2) Fail on errors */
if err == nil {
t.Errorf("Expected an error for empty input")
t.Errorf("Expected an error for empty input");
}
}
}

View File

@ -5,6 +5,7 @@ import (
"time"
)
/* (1) Generates the current time id
*
* @wsize<float64> Window Size in seconds
@ -13,25 +14,24 @@ import (
* @return parity<uint32> Current time parity
*
---------------------------------------------------------*/
func Generate(wsize float64) (uint32, uint32) {
func Generate(wsize float64) (uint32, uint32){
/* (1) If wsize is 0 (div by zero possible error) */
if wsize == 0 {
return 0, 0
}
if wsize == 0 { return 0, 0 }
/* (2) Get current timestamp */
timestamp := float64(time.Now().Unix())
timestamp := float64( time.Now().Unix() );
/* (3) Calculate the time id */
var id = uint32(timestamp / wsize)
var id = uint32( timestamp / wsize );
/* (4) Calculate parity */
var parity = id % 2
var parity = id % 2;
return id, parity
return id, parity;
}
/* (2) Try to guess a previous time id from its parity
*
* @wsize<float64> Window Size in seconds
@ -40,12 +40,12 @@ func Generate(wsize float64) (uint32, uint32) {
* @return id<uint32> The guessed time id
*
---------------------------------------------------------*/
func Guess(wsize float64, parity uint32) uint32 {
func Guess(wsize float64, parity uint32) uint32{
/* (1) Get current time id */
var idNow, parityNow = Generate(wsize)
var idNow, parityNow = Generate(wsize);
/* (2) Update ID with tidNow parity difference */
return idNow - uint32(math.Abs(float64(parityNow)-float64(parity)))
return idNow - uint32(math.Abs( float64(parityNow) - float64(parity) ));
}
}

View File

@ -1,22 +1,23 @@
package timeid
import (
"testing"
"time"
"testing"
)
func TestGuessing(t *testing.T) {
var windowSize float64 = .5
func TestGuessing(t *testing.T){
id, parity := Generate(windowSize)
var windowSize float64 = .5;
time.Sleep(time.Duration(windowSize) * time.Second)
id, parity := Generate(windowSize);
var guessedId = Guess(windowSize, parity)
time.Sleep( time.Duration(windowSize) * time.Second);
var guessedId = Guess(windowSize, parity);
if id != guessedId {
t.Errorf("Wrong guessed id, expected '%d' ; got '%d'", id, guessedId)
t.Errorf("Wrong guessed id, expected '%d' ; got '%d'", id, guessedId);
}
}
}

View File

@ -12,10 +12,11 @@ import "math"
---------------------------------------------------------*/
func Byte(_left byte, _right byte) byte {
return _left ^ _right
return _left ^ _right;
}
/* (2) bitwise XOR between Byte arrays
*
* @_left<byte[]> Left operand
@ -42,16 +43,16 @@ func ByteArray(_left []byte, _right []byte) []byte {
out := make([]byte, l, l)
/* (2) Process bitwise XOR */
for i := 0; i < l; i++ {
for i := 0 ; i < l ; i++ {
// 1. Out of range for _left
if i >= ll {
out[i] = _right[i]
out[i] = _right[i];
} else if i >= lr {
out[i] = _left[i]
out[i] = _left[i];
} else {

View File

@ -1,13 +1,13 @@
package server
import (
"errors"
"fmt"
"git.xdrm.io/logauth/schastsp/context"
"git.xdrm.io/logauth/schastsp/internal/scha"
"git.xdrm.io/schastsp/internal/scha"
"io"
"os"
"path/filepath"
"errors"
"git.xdrm.io/schastsp/context"
)
const DEBUG = false
@ -15,11 +15,12 @@ const DEBUG = false
/* (1) Structure
---------------------------------------------------------*/
type T struct {
ctx *context.T // shared context
hash []byte // current key
conf string // configuration file path
ctx *context.T // shared context
hash []byte // current key
conf string // configuration file path
}
/* (2) Constructor
*
* @ctx<context.T> Shared context
@ -31,49 +32,43 @@ func New(ctx *context.T, savePath string) (*T, error) {
inst := new(T)
/* (1) Store context */
if ctx == nil {
return nil, errors.New("Context must not be nil")
}
inst.ctx = ctx
if ctx == nil { return nil, errors.New("Context must not be nil"); }
inst.ctx = ctx;
/* (2) Get absolute file path */
absolutePath, err := filepath.Abs(savePath)
if err != nil {
return nil, err
}
absolutePath, err := filepath.Abs(savePath);
if err != nil { return nil, err; }
/* (3) Check file */
info, err := os.Stat(absolutePath)
info, err := os.Stat(absolutePath);
if err != nil {
// Unknown error
if !os.IsNotExist(err) {
return nil, err
}
if !os.IsNotExist(err) { return nil, err }
// File does not exist -> try to create file
_, err2 := os.Create(absolutePath)
if err2 != nil {
return nil, err2
}
_, err2 := os.Create(absolutePath);
if err2 != nil { return nil, err2 }
// fail if exists but not regular file
// fail if exists but not regular file
} else if !info.Mode().IsRegular() {
return nil, errors.New("Configuration file is not a regular file")
return nil, errors.New("Configuration file is not a regular file");
}
/* (4) Store file path */
inst.conf = absolutePath
inst.conf = absolutePath;
/* (5) Try to fetch hash from conf */
inst.hash = make([]byte, scha.HSIZE)
err = inst.fetch()
err = inst.fetch();
// if err != nil { return nil, err }
return inst, nil
return inst, nil;
}
/* (3) Handle request and send response
*
* @req<io.Reader> Request reader
@ -87,44 +82,33 @@ func (s *T) HandleRequest(req io.Reader, res io.Writer) error {
/* (1) Manage request
---------------------------------------------------------*/
/* (1) Read x1 */
x1 := make([]byte, scha.HSIZE)
read, err := req.Read(x1)
if err != nil {
return fmt.Errorf("Reading x1 : %s", err)
}
if uint16(read) != scha.HSIZE {
return fmt.Errorf("Cannot read x1 : %d / %d bytes available", read, scha.HSIZE)
}
x1 := make([]byte, scha.HSIZE);
read, err := req.Read(x1);
if err != nil { return fmt.Errorf("Reading x1 : %s", err) }
if uint16(read) != scha.HSIZE { return fmt.Errorf("Cannot read x1 : %d / %d bytes available", read, scha.HSIZE) }
/* (2) Read x2 */
x2 := make([]byte, scha.HSIZE)
read, err = req.Read(x2)
if err != nil {
return fmt.Errorf("Reading x2 : %s", err)
}
if uint16(read) != scha.HSIZE {
return fmt.Errorf("Cannot read x2 : %d / %d bytes available", read, scha.HSIZE)
}
x2 := make([]byte, scha.HSIZE);
read, err = req.Read(x2);
if err != nil { return fmt.Errorf("Reading x2 : %s", err) }
if uint16(read) != scha.HSIZE { return fmt.Errorf("Cannot read x2 : %d / %d bytes available", read, scha.HSIZE) }
/* (3) Manage request */
errCode, err := s.manageRequest(x1, x2)
if err != nil {
return err
}
if err != nil { return err }
/* (4) Valid authentication */
if errCode == 0 {
response := []byte{0x00}
written, err := res.Write(response)
if written != 1 || err != nil {
return err
}
if written != 1 || err != nil { return err }
return nil
return nil;
}
/* (2) If not authenticated -> build resynchronisation response
---------------------------------------------------------*/
/* (1) Init y1 + y2 */
@ -133,41 +117,29 @@ func (s *T) HandleRequest(req io.Reader, res io.Writer) error {
/* (2) Generate response */
err = s.generateResynchronisationResponse(y1, y2)
if err != nil {
return err
}
if err != nil { return err }
/* (3) Write error code */
responseCode := []byte{errCode}
written, err := res.Write(responseCode)
if err != nil {
return err
}
if written != len(responseCode) {
return errors.New("Cannot write response code")
}
written, err := res.Write(responseCode);
if err != nil { return err }
if written != len(responseCode) { return errors.New("Cannot write response code") }
/* (4) Write y1 into response */
written, err = res.Write(y1)
if err != nil {
return err
}
if written != len(y1) {
return errors.New("Cannot write y1")
}
if err != nil { return err }
if written != len(y1) { return errors.New("Cannot write y1") }
/* (5) Write y2 into response */
written, err = res.Write(y2)
if err != nil {
return err
}
if written != len(y1) {
return errors.New("Cannot write y2")
}
if err != nil { return err }
if written != len(y1) { return errors.New("Cannot write y2") }
return nil
}
/* (4) Apply a synchronisation key
*
* @syncKey<[]byte> Synchronisation key from client
@ -190,4 +162,4 @@ func (s *T) SynchronisationKey(syncKey []byte) error {
return nil
}
}

View File

@ -4,9 +4,9 @@ import (
"encoding/binary"
"errors"
"fmt"
"git.xdrm.io/logauth/schastsp/internal/scha"
"git.xdrm.io/logauth/schastsp/internal/timeid"
"git.xdrm.io/logauth/schastsp/internal/xor"
"git.xdrm.io/schastsp/internal/scha"
"git.xdrm.io/schastsp/internal/timeid"
"git.xdrm.io/schastsp/internal/xor"
"os"
)
@ -19,18 +19,14 @@ func (s T) store() error {
/* (1) Try to open file for writing */
file, err := os.OpenFile(s.conf, os.O_RDWR|os.O_CREATE, 0755)
if err != nil {
return err
}
if err != nil { return err }
/* (2) Defer close */
defer file.Close()
/* (3) Write hash into file */
err = binary.Write(file, binary.BigEndian, s.hash)
if err != nil {
return err
}
if err != nil { return err }
return nil
@ -45,9 +41,7 @@ func (s *T) fetch() error {
/* (1) Try to open file for reading */
file, err := os.Open(s.conf)
if err != nil {
return err
}
if err != nil { return err }
/* (2) Defer close */
defer file.Close()
@ -55,9 +49,7 @@ func (s *T) fetch() error {
/* (3) Try to fetch hash from file */
fetchedHash := make([]byte, scha.HSIZE)
err = binary.Read(file, binary.BigEndian, fetchedHash)
if err != nil {
return err
}
if err != nil { return err }
/* (4) Fail if wrong size */
if uint16(len(fetchedHash)) != scha.HSIZE {
@ -85,9 +77,7 @@ func (s *T) fetch() error {
---------------------------------------------------------*/
func (s *T) manageRequest(x1 []byte, x2 []byte) (byte, error) {
if DEBUG {
fmt.Printf(" stored hash is H = %x\n", s.hash)
}
if DEBUG { fmt.Printf(" stored hash is H = %x\n", s.hash) }
/* (1) Extract meta data
---------------------------------------------------------*/
@ -96,22 +86,19 @@ func (s *T) manageRequest(x1 []byte, x2 []byte) (byte, error) {
/* (2) Extract migration code */
mcode := uint8(x[1]) % 3
if DEBUG {
fmt.Printf(" extracted code is o = %d\n", mcode)
}
if DEBUG { fmt.Printf(" extracted code is o = %d\n", mcode) }
/* (3) Fail if no migration but different hashes */
if mcode == 0 && string(x1[2:]) != string(x2[2:]) {
return 1, nil
}
/* (2) TimeID management
---------------------------------------------------------*/
/* (1) Extract time mod */
timeMod := uint32(x[0]) % 2
if DEBUG {
fmt.Printf(" extracted time mod m = %d\n", timeMod)
}
if DEBUG { fmt.Printf(" extracted time mod m = %d\n", timeMod) }
/* (2) Try to guess time id */
timeID := timeid.Guess(s.ctx.Window(), timeMod)
@ -120,43 +107,35 @@ func (s *T) manageRequest(x1 []byte, x2 []byte) (byte, error) {
/* (3) Hash guessed time id */
hashedTimeID, err := scha.Hash(timeIDBytes, 1)
if err != nil {
return 2, err
}
if err != nil { return 2, err }
/* (3) Extract hashes
---------------------------------------------------------*/
/* (1) Extract hash from x0 */
h0 := xor.ByteArray(x1, hashedTimeID)
if DEBUG {
fmt.Printf(" supposing hash is h0 = %x\n", h0)
}
if DEBUG { fmt.Printf(" supposing hash is h0 = %x\n", h0) }
/* (2) Extract next hash from x1 */
h1 := xor.ByteArray(x2, hashedTimeID)
if DEBUG {
fmt.Printf(" supposing next is h1 = %x\n", h1)
}
if DEBUG { fmt.Printf(" supposing next is h1 = %x\n", h1) }
/* (3) Only remove timeMod if migration code = 0 */
if mcode == 0 {
h1[0] = xor.Byte(h1[0], byte(timeMod))
}
/* (4) Hash h0 to compare with stored hash 's.hash'
---------------------------------------------------------*/
/* (1) Hash 1 time to check if matches */
hashedH0, err := scha.Hash(h0, 1)
if err != nil {
return 2, err
}
if err != nil { return 2, err }
/* (2) If migration code = 2 (Rescue mode) -> hash MIN times */
if mcode == 2 {
hashedH0, err = scha.Hash(h0, s.ctx.MinDepth())
if err != nil {
return 2, err
}
if err != nil { return 2, err }
if DEBUG {
fmt.Printf(" hashed is h(min) = %x\n", hashedH0)
@ -170,6 +149,7 @@ func (s *T) manageRequest(x1 []byte, x2 []byte) (byte, error) {
return 1, nil
}
/* (5) Store next hash
---------------------------------------------------------*/
copy(s.hash, h1)
@ -197,9 +177,7 @@ func (s *T) generateResynchronisationResponse(y1 []byte, y2 []byte) error {
binary.BigEndian.PutUint32(timeIDBytes, timeID)
hashedTimeID, err := scha.Hash(timeIDBytes, 1)
if err != nil {
return err
}
if err != nil { return err }
/* (4) Process y1 = H ^ h(timeId) */
copy(y1, xor.ByteArray(s.hash, hashedTimeID))