schastsp/internal/client/client.internal.go

320 lines
8.3 KiB
Go

package client
import (
"encoding/binary"
"fmt"
"git.xdrm.io/schastsp/client/keyset"
"git.xdrm.io/schastsp/util/scha"
"git.xdrm.io/schastsp/util/timeid"
"git.xdrm.io/schastsp/util/xor"
)
/* (1) Updates 'key' and 'sync' with files
*
---------------------------------------------------------*/
func (c *T) updateConfig() {
var err error
/* (1) Restore if both are NIL
---------------------------------------------------------*/
if c.key == nil && c.sync == nil {
/* (1) Create default key */
c.key, err = keyset.Create(c.ctx)
/* (2) Fetch key */
err = c.fkey.Fetch(c.key)
/* (3) On error -> set key to NIL */
if err != nil { c.key = nil }
/* (4) Create default sync */
c.sync, err = keyset.Create(c.ctx)
/* (5) Fetch sync */
err = c.fsync.Fetch(c.sync)
/* (6) On error -> set sync to NIL */
if err != nil { c.sync = nil }
/* (7) Exit if all keysets have been fetched */
if c.key != nil && c.sync != nil {
return
}
}
/* (2) If cannot fetch -> create new keysets
---------------------------------------------------------*/
if c.key == nil {
c.key, _ = keyset.Create(c.ctx)
}
if c.sync == nil {
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") }
/* (2) Store sync */
err = c.fsync.Store(c.sync)
if err != nil { panic("Cannot store sync") }
}
/* (2) Migrate current key
*
---------------------------------------------------------*/
func (c *T) migrateKey() {
var err error
/* (1) Copy sync into key */
c.key = c.sync
/* (2) Regenerate sync */
c.sync, err = keyset.Create(c.ctx)
if err != nil { panic(err) }
/* (3) Store keysets to files */
c.updateConfig()
}
/* (3) Generate a new 'sync' keyset respecting mod constraints (timeid + migration)
*
---------------------------------------------------------*/
func (c *T) generateKeyWithConstraints() {
/* Get current hash */
keyHash, err := c.key.CurrentHash()
if err != nil { panic(err) }
/* Search key one is respects contraints */
for true {
/* (1) Get current time id
---------------------------------------------------------*/
/* (1) Get time id */
timeID, timeMod := timeid.Generate(c.ctx.Window())
/* (2) Convert timeId to byte array */
timeIDBytes := make([]byte, 4)
binary.BigEndian.PutUint32(timeIDBytes, timeID)
/* (3) Hash time id */
hashedTimeID, err := scha.Hash(timeIDBytes, 1)
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 }
/* (2) Get x1 */
x1 := make([]byte, scha.HSIZE)
copy(x1, xor.ByteArray(keyHash, hashedTimeID))
/* (3) Get x2 */
x2 := make([]byte, scha.HSIZE)
copy(x2, xor.ByteArray(syncHash, hashedTimeID))
/* (4) Get x1 xor x2 */
x := make([]byte, scha.HSIZE)
copy(x, xor.ByteArray(x1, x2))
if DEBUG {
fmt.Printf("+ x1 : '%x'\n", x1)
fmt.Printf("+ x2 : '%x'\n", x2)
}
/* (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) }
/* (4) Retry if invalid time constraint */
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) }
/* (6) Retry if invalid time constraint */
if !migrationConstraintValue { continue }
/* (7) Store new sync */
c.sync = newKey
/* (8) Store keysets to files */
c.updateConfig()
break
}
}
/* (4) Generate the client request
*
* @x1<[]byte> Byte array to write into
* @x2<[]byte> Byte array to write into
*
* @return err<error> The error or NIL if not
*
---------------------------------------------------------*/
func (c *T) generateRequest(x1 []byte, x2 []byte) error {
/* (1) Migrate if validated migration
---------------------------------------------------------*/
if c.key.MigrationCode() == 3 {
c.migrateKey()
}
/* (2) Decrement
---------------------------------------------------------*/
/* (1) Decrement hash */
remainingHashes := c.key.Decrement()
c.updateConfig()
if DEBUG {
fmt.Printf("Remaining %x[%d] hashes\n", remainingHashes, remainingHashes)
fmt.Printf("Migration code is %d\n", c.key.MigrationCode())
}
/* (3) New sync hash if key consumed
---------------------------------------------------------*/
if c.key.MigrationCode() > 0 {
/* (1) Generate sync with constraints */
c.generateKeyWithConstraints()
/* (2) Notify key needs renewal */
c.key.MigrationCode(2)
/* (3) Store config */
c.updateConfig()
}
/* (4) Get useful hashes
---------------------------------------------------------*/
/* (1) Store current hash */
h0, err := c.key.CurrentHash()
if err != nil { return err }
/* (2) Copy into next hash (same value) */
h1 := make([]byte, scha.HSIZE)
// 1. If migration code = 0 -> use same hash
copy(h1, h0)
// 2. Else -> use 'sync'
if c.key.MigrationCode() > 0 {
h1, err = c.sync.CurrentHash()
if err != nil { return err }
}
/* (5) Manage time id
---------------------------------------------------------*/
/* (1) Get current time id */
timeID, timeMod := timeid.Generate(c.ctx.Window())
/* (2) Convert time id to byte array */
timeIDBytes := make([]byte, 4)
binary.BigEndian.PutUint32(timeIDBytes, timeID)
/* (2) Get digest of time id */
hashedTimeID, err := scha.Hash(timeIDBytes, 1)
/* (6) Calculate x1 and x2
---------------------------------------------------------*/
/* (1) Calculate x1 = h ^ h(timeId) */
copy(x1, xor.ByteArray(h0, hashedTimeID))
if DEBUG {
fmt.Printf("\n=== x1 ===\n")
fmt.Printf(" hash is h0 = %x\n", h0)
fmt.Printf(" time id is n = %x[%d]\n", timeIDBytes, timeID)
fmt.Printf(" h(t) = %x\n", hashedTimeID)
fmt.Printf(" x1 is h0+h(t) = %X\n", x1)
fmt.Printf(" check x1+h(t) eq h0 = %x\n", xor.ByteArray(x1, hashedTimeID))
fmt.Printf(" check x1+h0 eq h(t) = %x\n", xor.ByteArray(x1, h0))
}
/* (2) Calculate x2 = h ^ h(timeId) ^ timeMod */
copy(x2, xor.ByteArray(h1, hashedTimeID))
// only add time mod if code = 0
if c.key.MigrationCode() == 0 {
x2[0] = xor.Byte(x2[0], byte(timeMod))
}
if DEBUG {
fmt.Printf("\n=== x2 ===\n")
fmt.Printf(" next is h1 = %x\n", h1)
fmt.Printf(" time mod is m = %x[%d]\n", timeMod, timeMod)
fmt.Printf(" h(t) = %x\n", hashedTimeID)
if c.key.MigrationCode() == 0 {
fmt.Printf(" x2 is h1+h(t)+m = %X\n", x2)
fmt.Printf(" check x2+x1 %% 2 eq m = %d (%t)\n", uint8(xor.ByteArray(x1, x2)[0]%2), xor.ByteArray(x1, x2)[0]%2 == byte(timeMod))
} else {
fmt.Printf(" x2 is h1+h(t) = %X\n", x2)
fmt.Printf(" check x2+x1 %% 2 eq m = %d (%t)\n", uint8(xor.ByteArray(x1, x2)[0]%2), xor.ByteArray(x1, x2)[0]%2 == byte(timeMod))
}
fmt.Printf(" check x2+x1 %% 3 eq o = %d (%t)\n", uint8(xor.ByteArray(x1, x2)[1]%3), uint8(xor.ByteArray(x1, x2)[1]%3) == c.key.MigrationCode())
}
return nil
}
/* (5) Rescue management
*
* @Y1<[]byte> First rescue parameter
* @Y2<[]byte> Second rescue parameter
*
* @return err<error> The error or NIL if not
*
---------------------------------------------------------*/
func (c *T) rescue(y1 []byte, y2 []byte) error {
/* (1) Extract time mod */
timeMod := uint32(xor.ByteArray(y1, y2)[0] % 2)
/* (2) Try to guess time id from timeM */
timeID := timeid.Guess(c.ctx.Window(), timeMod)
timeIDBytes := make([]byte, 4)
binary.BigEndian.PutUint32(timeIDBytes, timeID)
/* (3) Hash timeId */
hashedTimeID, err := scha.Hash(timeIDBytes, 1)
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 }
/* (6) Store updated key */
c.updateConfig()
return nil
}