Files
go-nkode/customer.go
2024-08-19 19:13:17 -05:00

157 lines
4.8 KiB
Go

package main
import (
"errors"
"fmt"
"github.com/google/uuid"
m "go-nkode/models"
py "go-nkode/py-builtin"
)
type Customer struct {
CustomerId uuid.UUID
NKodePolicy m.NKodePolicy
Attributes CustomerAttributes
Users map[string]User
}
func NewCustomer(keypadSize m.KeypadSize, nkodePolicy m.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([]uint64, nkodeLen)
var err error
for idx := range passcodeSetVals {
attrVal := c.Attributes.AttrVals[passcodeAttrIdx[idx]]
passcodeSetVals[idx], err = c.Attributes.GetAttrSetVal(attrVal)
if err != nil {
return err
}
}
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
}