reorganize code
This commit is contained in:
194
core/nkode/customer.go
Normal file
194
core/nkode/customer.go
Normal file
@@ -0,0 +1,194 @@
|
||||
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
|
||||
}
|
||||
74
core/nkode/customer_attributes.go
Normal file
74
core/nkode/customer_attributes.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package nkode
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go-nkode/core/model"
|
||||
"go-nkode/util"
|
||||
)
|
||||
|
||||
// TODO: make generic
|
||||
type CustomerAttributes struct {
|
||||
AttrVals []uint64
|
||||
SetVals []uint64
|
||||
KeypadSize model.KeypadSize
|
||||
}
|
||||
|
||||
func NewCustomerAttributes(keypadSize model.KeypadSize) (*CustomerAttributes, error) {
|
||||
if keypadSize.IsDispersable() {
|
||||
return nil, errors.New("number of keys must be less than the number of attributes per key to be dispersion resistant")
|
||||
}
|
||||
|
||||
attrVals, errAttr := util.GenerateRandomNonRepeatingUint64(keypadSize.TotalAttrs())
|
||||
if errAttr != nil {
|
||||
return nil, errAttr
|
||||
}
|
||||
setVals, errSet := util.GenerateRandomNonRepeatingUint64(keypadSize.AttrsPerKey)
|
||||
if errSet != nil {
|
||||
return nil, errSet
|
||||
}
|
||||
|
||||
customerAttrs := CustomerAttributes{
|
||||
AttrVals: attrVals,
|
||||
SetVals: setVals,
|
||||
KeypadSize: keypadSize,
|
||||
}
|
||||
return &customerAttrs, nil
|
||||
}
|
||||
|
||||
func (c *CustomerAttributes) Renew() error {
|
||||
attrVals, errAttr := util.GenerateRandomNonRepeatingUint64(c.KeypadSize.TotalAttrs())
|
||||
if errAttr != nil {
|
||||
return errAttr
|
||||
}
|
||||
setVals, errSet := util.GenerateRandomNonRepeatingUint64(c.KeypadSize.AttrsPerKey)
|
||||
if errSet != nil {
|
||||
return errSet
|
||||
}
|
||||
c.AttrVals = attrVals
|
||||
c.SetVals = setVals
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CustomerAttributes) IndexOfAttr(attrVal uint64) int {
|
||||
// TODO: should this be mapped instead?
|
||||
return util.IndexOf[uint64](c.AttrVals, attrVal)
|
||||
}
|
||||
|
||||
func (c *CustomerAttributes) IndexOfSet(setVal uint64) (int, error) {
|
||||
// TODO: should this be mapped instead?
|
||||
idx := util.IndexOf[uint64](c.SetVals, setVal)
|
||||
if idx == -1 {
|
||||
return -1, errors.New(fmt.Sprintf("Set Val %d is invalid", setVal))
|
||||
}
|
||||
return idx, nil
|
||||
}
|
||||
|
||||
func (c *CustomerAttributes) GetAttrSetVal(attrVal uint64) (uint64, error) {
|
||||
indexOfAttr := c.IndexOfAttr(attrVal)
|
||||
if indexOfAttr == -1 {
|
||||
return 0, errors.New(fmt.Sprintf("No attribute %d", attrVal))
|
||||
}
|
||||
setIdx := indexOfAttr % c.KeypadSize.AttrsPerKey
|
||||
return c.SetVals[setIdx], nil
|
||||
}
|
||||
51
core/nkode/customer_test.go
Normal file
51
core/nkode/customer_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package nkode
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
model2 "go-nkode/core/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewCustomerAttributes(t *testing.T) {
|
||||
keypad := model2.KeypadSize{AttrsPerKey: 10, NumbOfKeys: 5}
|
||||
_, nil := NewCustomerAttributes(keypad)
|
||||
assert.NoError(t, nil)
|
||||
}
|
||||
|
||||
func TestCustomer_ValidKeyEntry(t *testing.T) {
|
||||
keypadSize := model2.KeypadSize{AttrsPerKey: 10, NumbOfKeys: 7}
|
||||
nkodePolicy := model2.NewDefaultNKodePolicy()
|
||||
customer, err := NewCustomer(keypadSize, nkodePolicy)
|
||||
assert.NoError(t, err)
|
||||
newUserInterface, err := NewUserInterface(customer.Attributes.KeypadSize)
|
||||
assert.NoError(t, err)
|
||||
username := "testing123"
|
||||
passcodeIdx := []int{0, 1, 2, 3}
|
||||
err = customer.AddNewUser(username, passcodeIdx, *newUserInterface)
|
||||
assert.NoError(t, err)
|
||||
userLoginInterface, err := customer.GetLoginInterface(username)
|
||||
assert.NoError(t, err)
|
||||
selectedKeys, err := SelectKeyByAttrIdx(userLoginInterface, passcodeIdx, keypadSize)
|
||||
assert.NoError(t, err)
|
||||
validatedPasscode, err := customer.ValidKeyEntry(username, selectedKeys)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(validatedPasscode), len(passcodeIdx))
|
||||
for idx := range validatedPasscode {
|
||||
assert.Equal(t, validatedPasscode[idx], passcodeIdx[idx])
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomer_IsValidNKode(t *testing.T) {
|
||||
keypadSize := model2.KeypadSize{AttrsPerKey: 10, NumbOfKeys: 7}
|
||||
nkodePolicy := model2.NewDefaultNKodePolicy()
|
||||
customer, err := NewCustomer(keypadSize, nkodePolicy)
|
||||
assert.NoError(t, err)
|
||||
newUserInterface, err := NewUserInterface(customer.Attributes.KeypadSize)
|
||||
assert.NoError(t, err)
|
||||
username := "testing123"
|
||||
passcodeIdx := []int{0, 1, 2, 3}
|
||||
err = customer.AddNewUser(username, passcodeIdx, *newUserInterface)
|
||||
assert.NoError(t, err)
|
||||
err = customer.IsValidNKode(passcodeIdx)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
140
core/nkode/nkode_api.go
Normal file
140
core/nkode/nkode_api.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package nkode
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"go-nkode/core/model"
|
||||
)
|
||||
|
||||
type NKodeAPI struct {
|
||||
Customers map[uuid.UUID]Customer
|
||||
SignupSessions map[uuid.UUID]UserSignSession
|
||||
}
|
||||
|
||||
func NewNKodeAPI() NKodeAPI {
|
||||
return NKodeAPI{
|
||||
Customers: make(map[uuid.UUID]Customer),
|
||||
SignupSessions: make(map[uuid.UUID]UserSignSession),
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NKodeAPI) CreateNewCustomer(keypadSize model.KeypadSize, nkodePolicy model.NKodePolicy) (*uuid.UUID, error) {
|
||||
newCustomer, err := NewCustomer(keypadSize, nkodePolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n.Customers[newCustomer.CustomerId] = *newCustomer
|
||||
return &newCustomer.CustomerId, nil
|
||||
}
|
||||
|
||||
func (n *NKodeAPI) GenerateSignupInterface(customerId uuid.UUID) (*uuid.UUID, []int, error) {
|
||||
customer, exists := n.Customers[customerId]
|
||||
if !exists {
|
||||
return nil, nil, errors.New(fmt.Sprintf("customer doesnt exists: %s", customerId.String()))
|
||||
}
|
||||
|
||||
signupSession, err := NewSignupSession(customer.Attributes.KeypadSize, customer.CustomerId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
n.SignupSessions[signupSession.SessionId] = *signupSession
|
||||
return &signupSession.SessionId, signupSession.SetInterface, nil
|
||||
}
|
||||
|
||||
func (n *NKodeAPI) SetNKode(username string, customerId uuid.UUID, keySelection []int, sessionId uuid.UUID) ([]int, error) {
|
||||
_, exists := n.Customers[customerId]
|
||||
if !exists {
|
||||
return nil, errors.New(fmt.Sprintf("set nkode customer id does not exist %s", customerId.String()))
|
||||
}
|
||||
|
||||
session, exists := n.SignupSessions[sessionId]
|
||||
if !exists {
|
||||
return nil, errors.New(fmt.Sprintf("session id does not exist %s", sessionId.String()))
|
||||
}
|
||||
confirmInterface, err := session.SetUserNKode(username, keySelection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n.SignupSessions[sessionId] = session
|
||||
return confirmInterface, nil
|
||||
}
|
||||
|
||||
func (n *NKodeAPI) ConfirmNKode(customerId uuid.UUID, keySelection []int, sessionId uuid.UUID) error {
|
||||
session, exists := n.SignupSessions[sessionId]
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("session id does not exist %s", sessionId.String()))
|
||||
}
|
||||
customer, exists := n.Customers[customerId]
|
||||
passcode, err := session.DeducePasscode(keySelection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = customer.IsValidNKode(passcode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = customer.AddNewUser(session.Username, passcode, session.LoginInterface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delete(n.SignupSessions, session.SessionId)
|
||||
n.Customers[customerId] = customer
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NKodeAPI) GetLoginInterface(username string, customerId uuid.UUID) ([]int, error) {
|
||||
err := n.customerUserExists(username, customerId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user := n.Customers[customerId].Users[username]
|
||||
err = user.Interface.PartialInterfaceShuffle()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n.Customers[customerId].Users[username] = user
|
||||
return user.Interface.IdxInterface, nil
|
||||
}
|
||||
|
||||
func (n *NKodeAPI) Login(customerId uuid.UUID, username string, keySelection []int) error {
|
||||
customer, exists := n.Customers[customerId]
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("customer %s does not exist", customerId.String()))
|
||||
}
|
||||
user, exists := customer.Users[username]
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("user dne %s", username))
|
||||
}
|
||||
passcode, err := customer.ValidKeyEntry(username, keySelection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if user.Renew {
|
||||
err = user.RefreshPasscode(passcode, customer.Attributes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NKodeAPI) customerUserExists(username string, customerId uuid.UUID) error {
|
||||
customer, exists := n.Customers[customerId]
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("customer %s does not exist", customerId.String()))
|
||||
}
|
||||
_, exists = customer.Users[username]
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("user dne %s", username))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NKodeAPI) RenewAttributes(customerId uuid.UUID) error {
|
||||
customer, exists := n.Customers[customerId]
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("customer %s does not exist", customerId.String()))
|
||||
}
|
||||
return customer.RenewKeys()
|
||||
}
|
||||
48
core/nkode/nkode_api_test.go
Normal file
48
core/nkode/nkode_api_test.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package nkode
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go-nkode/core/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNKodeAPI(t *testing.T) {
|
||||
for idx := 0; idx < 10; idx++ {
|
||||
username := "test_username"
|
||||
passcodeLen := 4
|
||||
nkodePolicy := model.NewDefaultNKodePolicy()
|
||||
keypadSize := model.KeypadSize{AttrsPerKey: 10, NumbOfKeys: 3}
|
||||
nkodeApi := NewNKodeAPI()
|
||||
customerId, err := nkodeApi.CreateNewCustomer(keypadSize, nkodePolicy)
|
||||
assert.NoError(t, err)
|
||||
sessionId, setInterface, err := nkodeApi.GenerateSignupInterface(*customerId)
|
||||
assert.NoError(t, err)
|
||||
keypadSize = model.KeypadSize{AttrsPerKey: 3, NumbOfKeys: 3}
|
||||
userPasscode := setInterface[:passcodeLen]
|
||||
setKeySelect, err := SelectKeyByAttrIdx(setInterface, userPasscode, keypadSize)
|
||||
assert.NoError(t, err)
|
||||
confirmInterface, err := nkodeApi.SetNKode(username, *customerId, setKeySelect, *sessionId)
|
||||
assert.NoError(t, err)
|
||||
confirmKeySelect, err := SelectKeyByAttrIdx(confirmInterface, userPasscode, keypadSize)
|
||||
err = nkodeApi.ConfirmNKode(*customerId, confirmKeySelect, *sessionId)
|
||||
assert.NoError(t, err)
|
||||
|
||||
keypadSize = model.KeypadSize{AttrsPerKey: 10, NumbOfKeys: 3}
|
||||
loginInterface, err := nkodeApi.GetLoginInterface(username, *customerId)
|
||||
assert.NoError(t, err)
|
||||
loginKeySelection, err := SelectKeyByAttrIdx(loginInterface, userPasscode, keypadSize)
|
||||
assert.NoError(t, err)
|
||||
err = nkodeApi.Login(*customerId, username, loginKeySelection)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = nkodeApi.RenewAttributes(*customerId)
|
||||
assert.NoError(t, err)
|
||||
|
||||
loginInterface, err = nkodeApi.GetLoginInterface(username, *customerId)
|
||||
assert.NoError(t, err)
|
||||
loginKeySelection, err = SelectKeyByAttrIdx(loginInterface, userPasscode, keypadSize)
|
||||
assert.NoError(t, err)
|
||||
err = nkodeApi.Login(*customerId, username, loginKeySelection)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
21
core/nkode/test_helper.go
Normal file
21
core/nkode/test_helper.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package nkode
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go-nkode/core/model"
|
||||
"go-nkode/util"
|
||||
)
|
||||
|
||||
func SelectKeyByAttrIdx(interfaceUser []int, passcodeIdxs []int, keypadSize model.KeypadSize) ([]int, error) {
|
||||
selectedKeys := make([]int, len(passcodeIdxs))
|
||||
for idx := range passcodeIdxs {
|
||||
attrIdx := util.IndexOf[int](interfaceUser, passcodeIdxs[idx])
|
||||
keyNumb := attrIdx / keypadSize.AttrsPerKey
|
||||
if keyNumb < 0 || keyNumb >= keypadSize.NumbOfKeys {
|
||||
return nil, errors.New(fmt.Sprintf("index key number: %d out of range 0-%d", keyNumb, keypadSize.NumbOfKeys-1))
|
||||
}
|
||||
selectedKeys[idx] = keyNumb
|
||||
}
|
||||
return selectedKeys, nil
|
||||
}
|
||||
46
core/nkode/user.go
Normal file
46
core/nkode/user.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package nkode
|
||||
|
||||
import (
|
||||
m "go-nkode/core/model"
|
||||
"go-nkode/util"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Username string
|
||||
EncipheredPasscode m.EncipheredNKode
|
||||
UserKeys UserCipherKeys
|
||||
Interface UserInterface
|
||||
Renew bool
|
||||
}
|
||||
|
||||
func (u *User) DecipherMask(setVals []uint64, passcodeLen int) ([]uint64, error) {
|
||||
return u.UserKeys.DecipherMask(u.EncipheredPasscode.Mask, setVals, passcodeLen)
|
||||
}
|
||||
|
||||
func (u *User) RenewKeys(setXor []uint64, attrXor []uint64) error {
|
||||
u.Renew = true
|
||||
var err error
|
||||
u.UserKeys.SetKey, err = util.XorLists(setXor, u.UserKeys.SetKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.UserKeys.AlphaKey, err = util.XorLists(attrXor, u.UserKeys.AlphaKey)
|
||||
return err
|
||||
}
|
||||
|
||||
func (u *User) RefreshPasscode(passcodeAttrIdx []int, customerAttributes CustomerAttributes) error {
|
||||
newKeys, err := NewUserCipherKeys(customerAttributes.KeypadSize, customerAttributes.SetVals, u.UserKeys.MaxNKodeLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
encipheredPasscode, err := newKeys.EncipherNKode(passcodeAttrIdx, customerAttributes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u.UserKeys = *newKeys
|
||||
u.EncipheredPasscode = *encipheredPasscode
|
||||
u.Renew = false
|
||||
return nil
|
||||
}
|
||||
188
core/nkode/user_cipher_keys.go
Normal file
188
core/nkode/user_cipher_keys.go
Normal file
@@ -0,0 +1,188 @@
|
||||
package nkode
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go-nkode/core/model"
|
||||
"go-nkode/util"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type UserCipherKeys struct {
|
||||
AlphaKey []uint64
|
||||
SetKey []uint64
|
||||
PassKey []uint64
|
||||
MaskKey []uint64
|
||||
Salt []byte
|
||||
MaxNKodeLen int
|
||||
}
|
||||
|
||||
func NewUserCipherKeys(keypadSize model.KeypadSize, setVals []uint64, maxNKodeLen int) (*UserCipherKeys, error) {
|
||||
if len(setVals) != keypadSize.AttrsPerKey {
|
||||
return nil, errors.New(fmt.Sprintf("setVals len != attrsPerKey, %d, %d", len(setVals), keypadSize.AttrsPerKey))
|
||||
}
|
||||
|
||||
setKey, err := util.GenerateRandomNonRepeatingUint64(keypadSize.AttrsPerKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setKey, err = util.XorLists(setKey, setVals)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
alphakey, _ := util.GenerateRandomNonRepeatingUint64(keypadSize.TotalAttrs())
|
||||
passKey, _ := util.GenerateRandomNonRepeatingUint64(maxNKodeLen)
|
||||
maskKey, _ := util.GenerateRandomNonRepeatingUint64(maxNKodeLen)
|
||||
salt, _ := util.RandomBytes(10)
|
||||
userCipherKeys := UserCipherKeys{
|
||||
AlphaKey: alphakey,
|
||||
PassKey: passKey,
|
||||
MaskKey: maskKey,
|
||||
SetKey: setKey,
|
||||
Salt: salt,
|
||||
MaxNKodeLen: maxNKodeLen,
|
||||
}
|
||||
return &userCipherKeys, nil
|
||||
}
|
||||
|
||||
func (u *UserCipherKeys) PadUserMask(userMask []uint64, setVals []uint64) ([]uint64, error) {
|
||||
if len(userMask) > u.MaxNKodeLen {
|
||||
return nil, errors.New("user mask length exceeds max nkode length")
|
||||
}
|
||||
paddedUserMask := make([]uint64, len(userMask))
|
||||
copy(paddedUserMask, userMask)
|
||||
for i := len(userMask); i < u.MaxNKodeLen; i++ {
|
||||
paddedUserMask = append(paddedUserMask, setVals[i%len(setVals)])
|
||||
}
|
||||
return paddedUserMask, nil
|
||||
}
|
||||
|
||||
func (u *UserCipherKeys) ValidPassword(hashedPassword string, passcodeAttrIdx []int, customerAttrs CustomerAttributes) error {
|
||||
hashBytes := []byte(hashedPassword)
|
||||
passcodeCipher := u.encipherCode(passcodeAttrIdx, customerAttrs)
|
||||
passwordDigest, err := u.saltAndDigest(passcodeCipher)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = bcrypt.CompareHashAndPassword(hashBytes, passwordDigest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *UserCipherKeys) EncipherSaltHashCode(passcodeAttrIdx []int, customerAttrs CustomerAttributes) (string, error) {
|
||||
passcodeCipher := u.encipherCode(passcodeAttrIdx, customerAttrs)
|
||||
|
||||
passcodeDigest, err := u.saltAndDigest(passcodeCipher)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
passcodeBytes, err := u.hashPasscode(passcodeDigest)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(passcodeBytes), nil
|
||||
}
|
||||
|
||||
func (u *UserCipherKeys) encipherCode(passcodeAttrIdx []int, customerAttrs CustomerAttributes) []uint64 {
|
||||
passcodeLen := len(passcodeAttrIdx)
|
||||
|
||||
passcodeCipher := make([]uint64, u.MaxNKodeLen)
|
||||
|
||||
for idx := 0; idx < passcodeLen; idx++ {
|
||||
attrIdx := passcodeAttrIdx[idx]
|
||||
alpha := u.AlphaKey[attrIdx]
|
||||
attrVal := customerAttrs.AttrVals[idx]
|
||||
pass := u.PassKey[idx]
|
||||
passcodeCipher[idx] = alpha ^ pass ^ attrVal
|
||||
}
|
||||
return passcodeCipher
|
||||
}
|
||||
|
||||
func (u *UserCipherKeys) saltAndDigest(passcode []uint64) ([]byte, error) {
|
||||
passcodeBytes := util.Uint64ArrToByteArr(passcode)
|
||||
passcodeBytes = append(passcodeBytes, u.Salt...)
|
||||
h := sha256.New()
|
||||
passcodeSha, err := h.Write(passcodeBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
passcodeDigest := uint64(passcodeSha)
|
||||
return util.Uint64ArrToByteArr([]uint64{passcodeDigest}), nil
|
||||
}
|
||||
|
||||
func (u *UserCipherKeys) hashPasscode(passcodeDigest []byte) ([]byte, error) {
|
||||
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword(passcodeDigest, bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return hashedPassword, nil
|
||||
}
|
||||
func (u *UserCipherKeys) EncipherMask(passcodeSet []uint64, customerAttrs CustomerAttributes) (string, error) {
|
||||
paddedPasscodeSets, err := u.PadUserMask(passcodeSet, customerAttrs.SetVals)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cipheredMask := make([]uint64, len(paddedPasscodeSets))
|
||||
for idx := range paddedPasscodeSets {
|
||||
setIdx, err := customerAttrs.IndexOfSet(paddedPasscodeSets[idx])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
setKeyVal := u.SetKey[setIdx]
|
||||
maskKeyVal := u.MaskKey[idx]
|
||||
setVal := paddedPasscodeSets[idx]
|
||||
cipheredMask[idx] = setKeyVal ^ maskKeyVal ^ setVal
|
||||
}
|
||||
mask := util.EncodeBase64Str(cipheredMask)
|
||||
return mask, nil
|
||||
}
|
||||
|
||||
func (u *UserCipherKeys) DecipherMask(mask string, setVals []uint64, passcodeLen int) ([]uint64, error) {
|
||||
decodedMask, err := util.DecodeBase64Str(mask)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decipheredMask, err := util.XorLists(decodedMask, u.MaskKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setKeyRandComponent, err := util.XorLists(setVals, u.SetKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
passcodeSet := make([]uint64, passcodeLen)
|
||||
for idx, setCipher := range decipheredMask[:passcodeLen] {
|
||||
setIdx := util.IndexOf(setKeyRandComponent, setCipher)
|
||||
passcodeSet[idx] = setVals[setIdx]
|
||||
}
|
||||
return passcodeSet, nil
|
||||
}
|
||||
|
||||
func (u *UserCipherKeys) EncipherNKode(passcodeAttrIdx []int, customerAttrs CustomerAttributes) (*model.EncipheredNKode, error) {
|
||||
code, err := u.EncipherSaltHashCode(passcodeAttrIdx, customerAttrs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
passcodeSet := make([]uint64, len(passcodeAttrIdx))
|
||||
|
||||
for idx := range passcodeSet {
|
||||
passcodeAttr := customerAttrs.AttrVals[passcodeAttrIdx[idx]]
|
||||
passcodeSet[idx], err = customerAttrs.GetAttrSetVal(passcodeAttr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
mask, err := u.EncipherMask(passcodeSet, customerAttrs)
|
||||
encipheredCode := model.EncipheredNKode{
|
||||
Code: code,
|
||||
Mask: mask,
|
||||
}
|
||||
return &encipheredCode, nil
|
||||
}
|
||||
192
core/nkode/user_interface.go
Normal file
192
core/nkode/user_interface.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package nkode
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go-nkode/core/model"
|
||||
"go-nkode/hashset"
|
||||
"go-nkode/util"
|
||||
)
|
||||
|
||||
type UserInterface struct {
|
||||
IdxInterface []int
|
||||
KeypadSize model.KeypadSize
|
||||
}
|
||||
|
||||
func NewUserInterface(keypadSize model.KeypadSize) (*UserInterface, error) {
|
||||
idxInterface := util.IdentityArray(keypadSize.TotalAttrs())
|
||||
userInterface := UserInterface{
|
||||
IdxInterface: idxInterface,
|
||||
KeypadSize: keypadSize,
|
||||
}
|
||||
err := userInterface.RandomShuffle()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &userInterface, nil
|
||||
}
|
||||
|
||||
func (u *UserInterface) RandomShuffle() error {
|
||||
err := u.shuffleKeys()
|
||||
|
||||
keypadView, err := u.InterfaceMatrix()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
setView, err := util.MatrixTranspose(keypadView)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for idx, set := range setView {
|
||||
err := util.FisherYatesShuffle(&set)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
setView[idx] = set
|
||||
}
|
||||
|
||||
keypadView, err = util.MatrixTranspose(setView)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.IdxInterface = util.MatrixToList(keypadView)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *UserInterface) InterfaceMatrix() ([][]int, error) {
|
||||
return util.ListToMatrix(u.IdxInterface, u.KeypadSize.AttrsPerKey)
|
||||
}
|
||||
|
||||
func (u *UserInterface) SetViewMatrix() ([][]int, error) {
|
||||
keypadView, err := u.InterfaceMatrix()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return util.MatrixTranspose(keypadView)
|
||||
}
|
||||
|
||||
func (u *UserInterface) DisperseInterface() error {
|
||||
if !u.KeypadSize.IsDispersable() {
|
||||
return errors.New("interface is not dispersable")
|
||||
}
|
||||
|
||||
err := u.shuffleKeys()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = u.randomAttributeRotation()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *UserInterface) shuffleKeys() error {
|
||||
userInterfaceMatrix, err := util.ListToMatrix(u.IdxInterface, u.KeypadSize.AttrsPerKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = util.FisherYatesShuffle[[]int](&userInterfaceMatrix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.IdxInterface = util.MatrixToList(userInterfaceMatrix)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *UserInterface) randomAttributeRotation() error {
|
||||
userInterface, err := u.InterfaceMatrix()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
transposeUserInterface, err := util.MatrixTranspose(userInterface)
|
||||
|
||||
attrRotation, err := util.RandomPermutation(len(transposeUserInterface))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for idx, attrSet := range transposeUserInterface {
|
||||
rotation := attrRotation[idx]
|
||||
transposeUserInterface[idx] = append(attrSet[rotation:], attrSet[:rotation]...)
|
||||
}
|
||||
userInterface, err = util.MatrixTranspose(transposeUserInterface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.IdxInterface = util.MatrixToList(userInterface)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *UserInterface) AttributeAdjacencyGraph() (map[int]hashset.Set[int], error) {
|
||||
interfaceKeypad, err := u.InterfaceMatrix()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
graph := make(map[int]hashset.Set[int])
|
||||
|
||||
for _, key := range interfaceKeypad {
|
||||
keySet := hashset.NewSetFromSlice(key)
|
||||
for _, attr := range key {
|
||||
attrAdjacency := keySet.Copy()
|
||||
attrAdjacency.Remove(attr)
|
||||
graph[attr] = attrAdjacency
|
||||
}
|
||||
}
|
||||
return graph, nil
|
||||
}
|
||||
|
||||
func (u *UserInterface) PartialInterfaceShuffle() error {
|
||||
err := u.shuffleKeys()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
numbOfSelectedSets := u.KeypadSize.AttrsPerKey / 2
|
||||
if u.KeypadSize.AttrsPerKey&1 == 1 {
|
||||
numbOfSelectedSets += util.Choice[int]([]int{0, 1})
|
||||
}
|
||||
setIdxs, err := util.RandomPermutation(u.KeypadSize.AttrsPerKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
selectedSets := hashset.NewSetFromSlice[int](setIdxs[:numbOfSelectedSets])
|
||||
|
||||
keypadSetView, err := u.SetViewMatrix()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
interfaceBySet := make([][]int, u.KeypadSize.AttrsPerKey)
|
||||
for idx, attrs := range keypadSetView {
|
||||
if selectedSets.Contains(idx) {
|
||||
err = util.FisherYatesShuffle[int](&attrs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
interfaceBySet[idx] = attrs
|
||||
}
|
||||
keypadView, err := util.MatrixTranspose(interfaceBySet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.IdxInterface = util.MatrixToList(keypadView)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *UserInterface) GetAttrIdxByKeyNumbSetIdx(setIdx int, keyNumb int) (int, error) {
|
||||
if keyNumb < 0 || u.KeypadSize.NumbOfKeys <= keyNumb {
|
||||
return -1, errors.New(fmt.Sprintf("keyNumb %d is out of range 0-%d", keyNumb, u.KeypadSize.NumbOfKeys))
|
||||
}
|
||||
|
||||
if setIdx < 0 || u.KeypadSize.AttrsPerKey <= setIdx {
|
||||
return -1, errors.New(fmt.Sprintf("setIdx %d is out of range 0-%d", setIdx, u.KeypadSize.AttrsPerKey))
|
||||
}
|
||||
keypadView, err := u.InterfaceMatrix()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return keypadView[keyNumb][setIdx], nil
|
||||
}
|
||||
164
core/nkode/user_signup_session.go
Normal file
164
core/nkode/user_signup_session.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package nkode
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"go-nkode/core/model"
|
||||
"go-nkode/hashset"
|
||||
py_builtin "go-nkode/py-builtin"
|
||||
"go-nkode/util"
|
||||
)
|
||||
|
||||
type UserSignSession struct {
|
||||
SessionId uuid.UUID
|
||||
CustomerId uuid.UUID
|
||||
LoginInterface UserInterface
|
||||
KeypadSize model.KeypadSize
|
||||
SetInterface []int
|
||||
ConfirmInterface []int
|
||||
SetKeyEntry []int
|
||||
Username string
|
||||
}
|
||||
|
||||
func NewSignupSession(keypadSize model.KeypadSize, customerId uuid.UUID) (*UserSignSession, error) {
|
||||
loginInterface, err := NewUserInterface(keypadSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signupInter, err := signupInterface(*loginInterface)
|
||||
session := UserSignSession{
|
||||
SessionId: uuid.New(),
|
||||
CustomerId: customerId,
|
||||
LoginInterface: *loginInterface,
|
||||
SetInterface: signupInter.IdxInterface,
|
||||
ConfirmInterface: nil,
|
||||
SetKeyEntry: nil,
|
||||
Username: "",
|
||||
KeypadSize: signupInter.KeypadSize,
|
||||
}
|
||||
|
||||
return &session, nil
|
||||
}
|
||||
|
||||
func (s *UserSignSession) DeducePasscode(confirmKeyEntry []int) ([]int, error) {
|
||||
validEntry := py_builtin.All[int](confirmKeyEntry, func(i int) bool {
|
||||
return 0 <= i && i < s.LoginInterface.KeypadSize.NumbOfKeys
|
||||
})
|
||||
|
||||
if !validEntry {
|
||||
return nil, errors.New(fmt.Sprintf("Invalid Key entry. One or more key index: %#v, not in range 0-%d", confirmKeyEntry, s.LoginInterface.KeypadSize.NumbOfKeys))
|
||||
}
|
||||
|
||||
if s.SetInterface == nil {
|
||||
return nil, errors.New("signup session set interface is nil")
|
||||
}
|
||||
|
||||
if s.ConfirmInterface == nil {
|
||||
return nil, errors.New("signup session confirm interface is nil")
|
||||
}
|
||||
|
||||
if s.SetKeyEntry == nil {
|
||||
return nil, errors.New("signup session set key entry is nil")
|
||||
}
|
||||
|
||||
if s.Username == "" {
|
||||
return nil, errors.New("signup session username is nil")
|
||||
}
|
||||
|
||||
if len(confirmKeyEntry) != len(s.SetKeyEntry) {
|
||||
return nil, errors.New(fmt.Sprintf("confirm and set key entry lenght mismatch %d != %d", len(confirmKeyEntry), len(s.SetKeyEntry)))
|
||||
}
|
||||
|
||||
passcodeLen := len(confirmKeyEntry)
|
||||
setKeyVals, err := s.getSelectedKeyVals(s.SetKeyEntry, s.SetInterface)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
confirmKeyVals, err := s.getSelectedKeyVals(confirmKeyEntry, s.ConfirmInterface)
|
||||
passcode := make([]int, passcodeLen)
|
||||
|
||||
for idx := 0; idx < passcodeLen; idx++ {
|
||||
setKey := hashset.NewSetFromSlice[int](setKeyVals[idx])
|
||||
confirmKey := hashset.NewSetFromSlice[int](confirmKeyVals[idx])
|
||||
intersection := setKey.Intersect(confirmKey)
|
||||
if intersection.Size() < 1 {
|
||||
return nil, errors.New(fmt.Sprintf("set and confirm do not intersect at index %d", idx))
|
||||
}
|
||||
if intersection.Size() > 1 {
|
||||
return nil, errors.New(fmt.Sprintf("set and confirm intersect at more than one point at index %d", idx))
|
||||
}
|
||||
intersectionSlice := intersection.ToSlice()
|
||||
passcode[idx] = intersectionSlice[0]
|
||||
}
|
||||
return passcode, nil
|
||||
}
|
||||
|
||||
func (s *UserSignSession) SetUserNKode(username string, keySelection []int) ([]int, error) {
|
||||
validKeySelection := py_builtin.All[int](keySelection, func(i int) bool {
|
||||
return 0 <= i && i < s.KeypadSize.NumbOfKeys
|
||||
})
|
||||
if !validKeySelection {
|
||||
return nil, errors.New(fmt.Sprintf("one or key selection is out of range 0-%d", s.KeypadSize.NumbOfKeys-1))
|
||||
}
|
||||
|
||||
s.SetKeyEntry = keySelection
|
||||
s.Username = username
|
||||
|
||||
setInterface := UserInterface{IdxInterface: s.SetInterface, KeypadSize: s.KeypadSize}
|
||||
err := setInterface.DisperseInterface()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.ConfirmInterface = setInterface.IdxInterface
|
||||
return s.ConfirmInterface, nil
|
||||
}
|
||||
|
||||
func (s *UserSignSession) getSelectedKeyVals(keySelections []int, userInterface []int) ([][]int, error) {
|
||||
keypadInterface, err := util.ListToMatrix(userInterface, s.KeypadSize.AttrsPerKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyVals := make([][]int, len(keySelections))
|
||||
|
||||
for idx, keyIdx := range keySelections {
|
||||
keyVals[idx] = keypadInterface[keyIdx]
|
||||
}
|
||||
return keyVals, nil
|
||||
}
|
||||
|
||||
func signupInterface(baseUserInterface UserInterface) (*UserInterface, error) {
|
||||
if baseUserInterface.KeypadSize.IsDispersable() {
|
||||
return nil, errors.New("keypad is dispersable, can't use signupInterface")
|
||||
}
|
||||
err := baseUserInterface.RandomShuffle()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
interfaceMatrix, err := baseUserInterface.InterfaceMatrix()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attrSetView, err := util.MatrixTranspose(interfaceMatrix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = util.FisherYatesShuffle[[]int](&attrSetView)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
numbOfKeys := baseUserInterface.KeypadSize.NumbOfKeys
|
||||
attrSetView = attrSetView[:numbOfKeys]
|
||||
attrSetView, err = util.MatrixTranspose(attrSetView)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signupUserInterface := UserInterface{
|
||||
IdxInterface: util.MatrixToList(attrSetView),
|
||||
KeypadSize: model.KeypadSize{
|
||||
AttrsPerKey: numbOfKeys,
|
||||
NumbOfKeys: numbOfKeys,
|
||||
},
|
||||
}
|
||||
return &signupUserInterface, nil
|
||||
}
|
||||
119
core/nkode/user_test.go
Normal file
119
core/nkode/user_test.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package nkode
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go-nkode/core/model"
|
||||
py_builtin "go-nkode/py-builtin"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUserCipherKeys_EncipherSaltHashCode(t *testing.T) {
|
||||
keypadSize := model.KeypadSize{AttrsPerKey: 10, NumbOfKeys: 5}
|
||||
maxNKodeLen := 10
|
||||
customerAttrs, err := NewCustomerAttributes(keypadSize)
|
||||
assert.NoError(t, err)
|
||||
newUser, err := NewUserCipherKeys(keypadSize, customerAttrs.SetVals, maxNKodeLen)
|
||||
assert.NoError(t, err)
|
||||
passcodeIdx := []int{0, 1, 2, 3}
|
||||
encipher0, err := newUser.EncipherSaltHashCode(passcodeIdx, *customerAttrs)
|
||||
assert.NoError(t, err)
|
||||
err = newUser.ValidPassword(encipher0, passcodeIdx, *customerAttrs)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestUserCipherKeys_EncipherDecipherMask(t *testing.T) {
|
||||
keypadSize := model.KeypadSize{AttrsPerKey: 10, NumbOfKeys: 5}
|
||||
maxNKodeLen := 10
|
||||
|
||||
customerAttrs, err := NewCustomerAttributes(keypadSize)
|
||||
assert.NoError(t, err)
|
||||
newUser, err := NewUserCipherKeys(keypadSize, customerAttrs.SetVals, maxNKodeLen)
|
||||
assert.NoError(t, err)
|
||||
passcodeIdx := []int{0, 1, 2, 3}
|
||||
originalSetVals := make([]uint64, len(passcodeIdx))
|
||||
|
||||
for idx, val := range passcodeIdx {
|
||||
attr := customerAttrs.AttrVals[val]
|
||||
originalSetVals[idx], err = customerAttrs.GetAttrSetVal(attr)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
encipheredCode, err := newUser.EncipherNKode(passcodeIdx, *customerAttrs)
|
||||
assert.NoError(t, err)
|
||||
passcodeSetVals, err := newUser.DecipherMask(encipheredCode.Mask, customerAttrs.SetVals, len(passcodeIdx))
|
||||
assert.NoError(t, err)
|
||||
|
||||
for idx, setVal := range passcodeSetVals {
|
||||
assert.Equal(t, setVal, originalSetVals[idx])
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserInterface_RandomShuffle(t *testing.T) {
|
||||
keypadSize := model.KeypadSize{
|
||||
AttrsPerKey: 10,
|
||||
NumbOfKeys: 5,
|
||||
}
|
||||
userInterface, err := NewUserInterface(keypadSize)
|
||||
assert.NoError(t, err)
|
||||
userInterfaceCopy := make([]int, len(userInterface.IdxInterface))
|
||||
copy(userInterfaceCopy, userInterface.IdxInterface)
|
||||
|
||||
err = userInterface.RandomShuffle()
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, len(userInterface.IdxInterface), len(userInterfaceCopy))
|
||||
equalCount := 0
|
||||
for idx, val := range userInterface.IdxInterface {
|
||||
if val == userInterfaceCopy[idx] {
|
||||
equalCount++
|
||||
}
|
||||
}
|
||||
assert.NotEqual(t, equalCount, len(userInterface.IdxInterface))
|
||||
}
|
||||
|
||||
func TestUserInterface_DisperseInterface(t *testing.T) {
|
||||
|
||||
for idx := 0; idx < 10000; idx++ {
|
||||
keypadSize := model.KeypadSize{AttrsPerKey: 7, NumbOfKeys: 10}
|
||||
|
||||
userInterface, err := NewUserInterface(keypadSize)
|
||||
assert.NoError(t, err)
|
||||
preDispersion, err := userInterface.AttributeAdjacencyGraph()
|
||||
assert.NoError(t, err)
|
||||
err = userInterface.DisperseInterface()
|
||||
assert.NoError(t, err)
|
||||
postDispersion, err := userInterface.AttributeAdjacencyGraph()
|
||||
assert.Equal(t, len(postDispersion), len(preDispersion))
|
||||
for attr, adjAttrs := range preDispersion {
|
||||
postAdjAttrs := postDispersion[attr]
|
||||
assert.Equal(t, adjAttrs.Size(), postAdjAttrs.Size())
|
||||
assert.True(t, adjAttrs.IsDisjoint(postAdjAttrs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserInterface_PartialInterfaceShuffle(t *testing.T) {
|
||||
keypadSize := model.KeypadSize{AttrsPerKey: 7, NumbOfKeys: 10}
|
||||
userInterface, err := NewUserInterface(keypadSize)
|
||||
assert.NoError(t, err)
|
||||
preShuffle := userInterface.IdxInterface
|
||||
err = userInterface.PartialInterfaceShuffle()
|
||||
assert.NoError(t, err)
|
||||
postShuffle := userInterface.IdxInterface
|
||||
|
||||
shuffleCompare := make([]bool, len(postShuffle))
|
||||
for idx, val := range preShuffle {
|
||||
shuffleCompare[idx] = val == postShuffle[idx]
|
||||
}
|
||||
|
||||
allTrue := py_builtin.All[bool](shuffleCompare, func(n bool) bool {
|
||||
return n == true
|
||||
})
|
||||
assert.False(t, allTrue)
|
||||
|
||||
allFalse := py_builtin.All[bool](shuffleCompare, func(n bool) bool {
|
||||
return n == false
|
||||
})
|
||||
|
||||
assert.False(t, allFalse)
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user