195 lines
5.9 KiB
Go
195 lines
5.9 KiB
Go
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
|
|
}
|