package nkode import ( "errors" "fmt" "github.com/google/uuid" "go-nkode/core/model" "go-nkode/hashset" py "go-nkode/py-builtin" "go-nkode/util" ) type Customer struct { CustomerId uuid.UUID NKodePolicy model.NKodePolicy Attributes CustomerAttributes Users map[string]User } func NewCustomer(keypadSize model.KeypadSize, nkodePolicy model.NKodePolicy) (*Customer, error) { if keypadSize.TotalAttrs() < nkodePolicy.DistinctAttributes { return nil, errors.New(fmt.Sprintf("incompadible nkode policy and keypad size TotalAttrs: %d < DistinctAttributes: %d", keypadSize.TotalAttrs(), nkodePolicy.DistinctAttributes)) } if keypadSize.AttrsPerKey < nkodePolicy.DistinctSets { return nil, errors.New(fmt.Sprintf("incompadible nkode policy and keypad size AttrPerKey: %d < DistinctSets: %d", keypadSize.AttrsPerKey, nkodePolicy.DistinctSets)) } customerAttrs, err := NewCustomerAttributes(keypadSize) if err != nil { return nil, err } customer := Customer{ CustomerId: uuid.New(), NKodePolicy: nkodePolicy, Attributes: *customerAttrs, Users: make(map[string]User), } return &customer, nil } func (c *Customer) AddNewUser(username string, passcodeIdx []int, userInterface UserInterface) error { _, exists := c.Users[username] if exists { return errors.New(fmt.Sprintf("User %s already exists for customer %s exists", username, c.CustomerId.String())) } newKeys, err := NewUserCipherKeys(c.Attributes.KeypadSize, c.Attributes.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: userInterface, } c.Users[username] = newUser return nil } func (c *Customer) ValidKeyEntry(username string, selectedKeys []int) ([]int, error) { user, exists := c.Users[username] if !exists { return nil, errors.New(fmt.Sprintf("user %s does not exist for customer %s", username, c.CustomerId.String())) } validKeys := py.All[int](selectedKeys, func(idx int) bool { return 0 <= idx && idx < c.Attributes.KeypadSize.NumbOfKeys }) if !validKeys { return nil, errors.New(fmt.Sprintf("one or more keys not in range 0-%d", c.Attributes.KeypadSize.NumbOfKeys-1)) } presumedAttrIdxVals, err := c.getPresumedAttributeIdxVals(user, selectedKeys) if err != nil { return nil, err } err = c.IsValidNKode(presumedAttrIdxVals) if err != nil { return nil, err } err = user.UserKeys.ValidPassword(user.EncipheredPasscode.Code, presumedAttrIdxVals, c.Attributes) 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)) } passcodeSetVals, err := user.DecipherMask(c.Attributes.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(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 < c.Attributes.KeypadSize.TotalAttrs() }) if !validIdx { return errors.New(fmt.Sprintf("One or more idx out of range 0-%d in IsValidNKode", c.Attributes.KeypadSize.TotalAttrs()-1)) } passcodeSetVals := make(hashset.Set[uint64]) passcodeAttrVals := make(hashset.Set[uint64]) for idx := 0; idx < nkodeLen; idx++ { attrVal := c.Attributes.AttrVals[passcodeAttrIdx[idx]] setVal, err := c.Attributes.GetAttrSetVal(attrVal) 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 string) ([]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.String())) } 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, c.Attributes.KeypadSize.TotalAttrs()) oldSets := make([]uint64, c.Attributes.KeypadSize.AttrsPerKey) copy(oldAttrs, c.Attributes.AttrVals) copy(oldSets, c.Attributes.SetVals) err := c.Attributes.Renew() if err != nil { return nil } attrsXor, err := util.XorLists(oldAttrs, c.Attributes.AttrVals) if err != nil { return nil } setXor, err := util.XorLists(oldSets, c.Attributes.SetVals) if err != nil { return nil } for _, user := range c.Users { err = user.RenewKeys(setXor, attrsXor) if err != nil { return nil } } return nil }