package nkode import ( "errors" "fmt" "github.com/google/uuid" m "go-nkode/core/model" "go-nkode/hashset" py "go-nkode/py-builtin" "go-nkode/util" ) type Customer struct { CustomerId m.CustomerId NKodePolicy m.NKodePolicy Attributes CustomerAttributes Users map[m.Username]User } func NewCustomer(nkodePolicy m.NKodePolicy) (*Customer, error) { customerAttrs, err := NewCustomerAttributes() if err != nil { return nil, err } customer := Customer{ CustomerId: m.CustomerId(uuid.New()), NKodePolicy: nkodePolicy, Attributes: *customerAttrs, Users: make(map[m.Username]User), } return &customer, nil } func (c *Customer) AddNewUser(username m.Username, passcodeIdx []int, ui UserInterface, kp m.KeypadDimension) error { _, exists := c.Users[username] if exists { return errors.New(fmt.Sprintf("User %s already exists for customer %+v exists", username, c.CustomerId)) } setVals, err := c.Attributes.SetVals(kp) if err != nil { return err } newKeys, err := NewUserCipherKeys(&kp, setVals, c.NKodePolicy.MaxNkodeLen) if err != nil { return err } encipheredNKode, err := newKeys.EncipherNKode(passcodeIdx, c.Attributes) if err != nil { return err } newUser := User{ Username: username, EncipheredPasscode: *encipheredNKode, UserKeys: *newKeys, Interface: ui, Kp: kp, } c.Users[username] = newUser return nil } func (c *Customer) ValidKeyEntry(username m.Username, selectedKeys []int) ([]int, error) { user, exists := c.Users[username] if !exists { return nil, errors.New(fmt.Sprintf("user %s does not exist for customer %+v", username, c.CustomerId)) } validKeys := py.All[int](selectedKeys, func(idx int) bool { return 0 <= idx && idx < user.Kp.NumbOfKeys }) if !validKeys { return nil, errors.New(fmt.Sprintf("one or more keys not in range 0-%d", user.Kp.NumbOfKeys-1)) } presumedAttrIdxVals, err := c.getPresumedAttributeIdxVals(user, selectedKeys) if err != nil { return nil, err } err = c.IsValidNKode(user.Kp, presumedAttrIdxVals) if err != nil { return nil, err } attrVals, err := c.Attributes.AttrVals(user.Kp) if err != nil { return nil, err } err = user.UserKeys.ValidPassword(user.EncipheredPasscode.Code, presumedAttrIdxVals, attrVals) if err != nil { return nil, err } return presumedAttrIdxVals, nil } func (c *Customer) getPresumedAttributeIdxVals(user User, selectedKeys []int) ([]int, error) { passcodeLen := len(selectedKeys) if passcodeLen < c.NKodePolicy.MinNkodeLen || passcodeLen > c.NKodePolicy.MaxNkodeLen { return nil, errors.New(fmt.Sprintf("Invalid passcode length of %d. Passcode length must be in range %d-%d", passcodeLen, c.NKodePolicy.MinNkodeLen, c.NKodePolicy.MaxNkodeLen)) } setVals, err := c.Attributes.SetVals(user.Kp) if err != nil { return nil, err } passcodeSetVals, err := user.DecipherMask(setVals, passcodeLen) if err != nil { return nil, err } presumedAttrIdxVals := make([]int, passcodeLen) for idx := range presumedAttrIdxVals { keyNumb := selectedKeys[idx] setIdx, err := c.Attributes.IndexOfSet(passcodeSetVals[idx]) if err != nil { return nil, err } selectedAttrIdx, err := user.Interface.GetAttrIdxByKeyNumbSetIdx(setIdx, keyNumb) if err != nil { return nil, err } presumedAttrIdxVals[idx] = selectedAttrIdx } return presumedAttrIdxVals, nil } func (c *Customer) IsValidNKode(kp m.KeypadDimension, passcodeAttrIdx []int) error { nkodeLen := len(passcodeAttrIdx) if nkodeLen < c.NKodePolicy.MinNkodeLen { return errors.New(fmt.Sprintf("NKode length %d is too short. Minimum nKode length is %d", nkodeLen, c.NKodePolicy.MinNkodeLen)) } validIdx := py.All[int](passcodeAttrIdx, func(i int) bool { return i >= 0 && i < kp.TotalAttrs() }) if !validIdx { return errors.New(fmt.Sprintf("One or more idx out of range 0-%d in IsValidNKode", kp.TotalAttrs()-1)) } passcodeSetVals := make(hashset.Set[uint64]) passcodeAttrVals := make(hashset.Set[uint64]) attrVals, err := c.Attributes.AttrVals(kp) if err != nil { return err } for idx := 0; idx < nkodeLen; idx++ { attrVal := attrVals[passcodeAttrIdx[idx]] setVal, err := c.Attributes.GetAttrSetVal(attrVal, kp) if err != nil { return err } passcodeSetVals.Add(setVal) passcodeAttrVals.Add(attrVal) } if passcodeSetVals.Size() < c.NKodePolicy.DistinctSets { return errors.New(fmt.Sprintf("passcode has two few distinct sets min %d, has %d", c.NKodePolicy.DistinctSets, passcodeSetVals.Size())) } if passcodeAttrVals.Size() < c.NKodePolicy.DistinctAttributes { return errors.New(fmt.Sprintf("passcode has two few distinct attributes min %d, has %d", c.NKodePolicy.DistinctAttributes, passcodeAttrVals.Size())) } return nil } func (c *Customer) GetLoginInterface(username m.Username) ([]int, error) { user, exists := c.Users[username] if !exists { return nil, errors.New(fmt.Sprintf("can't get login interface for non-existant user %s in customer %s", username, c.CustomerId)) } err := user.Interface.PartialInterfaceShuffle() if err != nil { return nil, err } c.Users[username] = user return user.Interface.IdxInterface, nil } func (c *Customer) RenewKeys() error { oldAttrs := make([]uint64, m.KeypadMax.TotalAttrs()) oldSets := make([]uint64, m.KeypadMax.AttrsPerKey) allAttrVals, err := c.Attributes.AttrVals(m.KeypadMax) if err != nil { return nil } allSetVals, err := c.Attributes.AttrVals(m.KeypadMax) if err != nil { return nil } copy(oldAttrs, allAttrVals) copy(oldSets, allSetVals) err = c.Attributes.Renew() if err != nil { return nil } allAttrVals, err = c.Attributes.AttrVals(m.KeypadMax) if err != nil { return nil } allSetVals, err = c.Attributes.AttrVals(m.KeypadMax) if err != nil { return nil } attrsXor, err := util.XorLists(oldAttrs, allAttrVals) if err != nil { return nil } setXor, err := util.XorLists(oldSets, allSetVals) if err != nil { return nil } for _, user := range c.Users { err = user.RenewKeys(setXor, attrsXor) if err != nil { return nil } } return nil }