package client import ( "errors" "fmt" "git.xdrm.io/logauth/schastsp/internal/keyset" "git.xdrm.io/logauth/schastsp/context" "git.xdrm.io/logauth/schastsp/internal/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 Shared context * @saveDir 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 Writer to send into * * @return err 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 Reader to receive from * * @return err 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 * ---------------------------------------------------------*/ 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; }