schastsp/internal/client/client.go

165 lines
3.8 KiB
Go

package client
import (
"errors"
"fmt"
"git.xdrm.io/schastsp/client/keyset"
"git.xdrm.io/schastsp/context"
"git.xdrm.io/schastsp/util/scha"
"io"
)
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
}
/* (2) Constructor
*
* @ctx<context.T> Shared context
* @saveDir<string> Configuration path
*
---------------------------------------------------------*/
func New(ctx *context.T, saveDir string) (*T, error) {
var err error
inst := new(T)
/* (1) Store context */
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 }
/* (3) Get file management for SYNC */
inst.fsync, err = Config(saveDir, "sync")
if err != nil { return nil, err }
/* (4) Restore from config */
inst.updateConfig()
return inst, nil
}
/* (3) Processes and sends a new request
*
* @w<io.Writer> Writer to send into
*
* @return err<error> Error
*
---------------------------------------------------------*/
func (c *T) Send(w io.Writer) error {
/* (1) Generate the request */
x1 := make([]byte, scha.HSIZE)
x2 := make([]byte, scha.HSIZE)
err := c.generateRequest(x1, x2)
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") }
written, err = w.Write(x2)
if err != nil { return err }
if written != len(x1) { return errors.New("Cannot write x2") }
return nil
}
/* (4) Receives and processes a response
*
* @w<io.Reader> Reader to receive from
*
* @return err<error> Error
*
---------------------------------------------------------*/
func (c *T) Receive(r io.Reader) error {
/* (1) Read error code
---------------------------------------------------------*/
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 DEBUG { fmt.Printf("ERROR CODE : %d\n", errCode[0]) }
/* (2) Manage success
---------------------------------------------------------*/
if errCode[0] == 0 {
/* (1) If pending migration -> migrate */
if c.key.MigrationCode() == 2 {
c.migrateKey()
if DEBUG { fmt.Printf("*** VALIDATED MIGRATION\n") }
}
/* (2) No error anyway */
return nil
}
/* (3) Manage rescue
---------------------------------------------------------*/
/* (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") }
/* (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") }
/* (3) Manage rescue mode */
err = c.rescue(y1, y2)
if err != nil { return err }
if DEBUG { fmt.Printf("*** MIGRATION PREPARED\n") }
return nil
}
/* (5) Returns a synchronisation key (first server connection)
*
* @return key<[]byte> Synchronisation key
* @return err<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)
/* (2) Get current hash */
hash, err := c.key.CurrentHash()
if err != nil { return nil, err }
/* (3) Decrement key so 'hash' is valid */
c.key.Decrement()
/* (4) Return key */
return hash, nil;
}