package server import ( "errors" "fmt" "git.xdrm.io/logauth/schastsp/context" "git.xdrm.io/logauth/schastsp/internal/scha" "io" "os" "path/filepath" ) const DEBUG = false /* (1) Structure ---------------------------------------------------------*/ type T struct { ctx *context.T // shared context hash []byte // current key conf string // configuration file path } /* (2) Constructor * * @ctx Shared context * @savePath Configuration file path * ---------------------------------------------------------*/ 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 /* (2) Get absolute file path */ absolutePath, err := filepath.Abs(savePath) if err != nil { return nil, err } /* (3) Check file */ info, err := os.Stat(absolutePath) if err != nil { // Unknown error 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 } // fail if exists but not regular file } else if !info.Mode().IsRegular() { return nil, errors.New("Configuration file is not a regular file") } /* (4) Store file path */ inst.conf = absolutePath /* (5) Try to fetch hash from conf */ inst.hash = make([]byte, scha.HSIZE) err = inst.fetch() // if err != nil { return nil, err } return inst, nil } /* (3) Handle request and send response * * @req Request reader * @res Response Writer * * @return err Error or NIL if not * ---------------------------------------------------------*/ 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) } /* (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) } /* (3) Manage request */ errCode, err := s.manageRequest(x1, x2) 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 } return nil } /* (2) If not authenticated -> build resynchronisation response ---------------------------------------------------------*/ /* (1) Init y1 + y2 */ y1 := make([]byte, scha.HSIZE) y2 := make([]byte, scha.HSIZE) /* (2) Generate response */ err = s.generateResynchronisationResponse(y1, y2) 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") } /* (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") } /* (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") } return nil } /* (4) Apply a synchronisation key * * @syncKey<[]byte> Synchronisation key from client * * @return err * ---------------------------------------------------------*/ func (s *T) SynchronisationKey(syncKey []byte) error { /* (1) Check size */ if uint16(len(syncKey)) != scha.HSIZE { return fmt.Errorf("Invalid synchronisation key size (%d), expected %d bytes", len(syncKey), scha.HSIZE) } /* (2) Copy synchronisation key */ copy(s.hash, syncKey) /* (3) Store new config */ s.store() return nil }