implement and test sql db accessor

This commit is contained in:
2024-08-27 19:27:52 -05:00
parent e6947e714d
commit fe06a95c98
24 changed files with 745 additions and 403 deletions

View File

@@ -9,7 +9,7 @@ import (
var KeyIndexOutOfRange error = errors.New("one or more keys is out of range")
func ValidKeyEntry(user User, customer Customer, selectedKeys []int) ([]int, error) {
func ValidKeyEntry(user m.User, customer m.Customer, selectedKeys []int) ([]int, error) {
validKeys := py.All[int](selectedKeys, func(idx int) bool {
return 0 <= idx && idx < user.Kp.NumbOfKeys
})
@@ -24,7 +24,7 @@ func ValidKeyEntry(user User, customer Customer, selectedKeys []int) ([]int, err
return nil, err
}
setVals, err := customer.Attributes.SetVals(user.Kp)
setVals, err := customer.Attributes.SetValsForKp(user.Kp)
if err != nil {
return nil, err
}
@@ -51,7 +51,7 @@ func ValidKeyEntry(user User, customer Customer, selectedKeys []int) ([]int, err
if err != nil {
panic(err)
}
attrVals, err := customer.Attributes.AttrVals(user.Kp)
attrVals, err := customer.Attributes.AttrValsForKp(user.Kp)
if err != nil {
panic(err)
}
@@ -63,12 +63,12 @@ func ValidKeyEntry(user User, customer Customer, selectedKeys []int) ([]int, err
return presumedAttrIdxVals, nil
}
func NewUser(customer Customer, username m.Username, passcodeIdx []int, ui UserInterface, kp m.KeypadDimension) (*User, error) {
setVals, err := customer.Attributes.SetVals(kp)
func NewUser(customer m.Customer, username m.Username, passcodeIdx []int, ui m.UserInterface, kp m.KeypadDimension) (*m.User, error) {
setVals, err := customer.Attributes.SetValsForKp(kp)
if err != nil {
return nil, err
}
newKeys, err := NewUserCipherKeys(&kp, setVals, customer.NKodePolicy.MaxNkodeLen)
newKeys, err := m.NewUserCipherKeys(&kp, setVals, customer.NKodePolicy.MaxNkodeLen)
if err != nil {
return nil, err
}
@@ -76,7 +76,7 @@ func NewUser(customer Customer, username m.Username, passcodeIdx []int, ui UserI
if err != nil {
return nil, err
}
newUser := User{
newUser := m.User{
Id: m.UserId(uuid.New()),
Username: username,
EncipheredPasscode: *encipheredNKode,

View File

@@ -1,200 +0,0 @@
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 {
Id m.CustomerId
NKodePolicy m.NKodePolicy
Attributes CustomerAttributes
}
func NewCustomer(nkodePolicy m.NKodePolicy) (*Customer, error) {
customerAttrs, err := NewCustomerAttributes()
if err != nil {
return nil, err
}
customer := Customer{
Id: m.CustomerId(uuid.New()),
NKodePolicy: nkodePolicy,
Attributes: *customerAttrs,
}
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.Id))
// }
// 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{
// Id: m.UserId(uuid.New()),
// 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.Id))
// }
//
// 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) RenewKeys() ([]uint64, []uint64) {
oldAttrs := make([]uint64, m.KeypadMax.TotalAttrs())
oldSets := make([]uint64, m.KeypadMax.AttrsPerKey)
allAttrVals, err := c.Attributes.AttrVals(m.KeypadMax)
if err != nil {
panic(err)
}
allSetVals, err := c.Attributes.AttrVals(m.KeypadMax)
if err != nil {
panic(err)
}
copy(oldAttrs, allAttrVals)
copy(oldSets, allSetVals)
err = c.Attributes.Renew()
if err != nil {
panic(err)
}
allAttrVals, err = c.Attributes.AttrVals(m.KeypadMax)
if err != nil {
panic(err)
}
allSetVals, err = c.Attributes.SetVals(m.KeypadMax)
if err != nil {
panic(err)
}
attrsXor, err := util.XorLists(oldAttrs, allAttrVals)
if err != nil {
panic(err)
}
setXor, err := util.XorLists(oldSets, allSetVals)
if err != nil {
panic(err)
}
return setXor, attrsXor
}

View File

@@ -1,83 +0,0 @@
package nkode
import (
"errors"
"fmt"
"go-nkode/core/model"
"go-nkode/util"
)
type CustomerAttributes struct {
attrVals []uint64
setVals []uint64
}
func NewCustomerAttributes() (*CustomerAttributes, error) {
attrVals, errAttr := util.GenerateRandomNonRepeatingUint64(m.KeypadMax.TotalAttrs())
if errAttr != nil {
return nil, errAttr
}
setVals, errSet := util.GenerateRandomNonRepeatingUint64(m.KeypadMax.AttrsPerKey)
if errSet != nil {
return nil, errSet
}
customerAttrs := CustomerAttributes{
attrVals: attrVals,
setVals: setVals,
}
return &customerAttrs, nil
}
func (c *CustomerAttributes) Renew() error {
attrVals, errAttr := util.GenerateRandomNonRepeatingUint64(m.KeypadMax.TotalAttrs())
if errAttr != nil {
return errAttr
}
setVals, errSet := util.GenerateRandomNonRepeatingUint64(m.KeypadMax.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, userKeypad m.KeypadDimension) (uint64, error) {
indexOfAttr := c.IndexOfAttr(attrVal)
if indexOfAttr == -1 {
return 0, errors.New(fmt.Sprintf("No attribute %d", attrVal))
}
setIdx := indexOfAttr % userKeypad.AttrsPerKey
return c.setVals[setIdx], nil
}
func (c *CustomerAttributes) AttrVals(userKp m.KeypadDimension) ([]uint64, error) {
err := userKp.IsValidKeypadDimension()
if err != nil {
return nil, err
}
return c.attrVals[:userKp.TotalAttrs()], nil
}
func (c *CustomerAttributes) SetVals(userKp m.KeypadDimension) ([]uint64, error) {
err := userKp.IsValidKeypadDimension()
if err != nil {
return nil, err
}
return c.setVals[:userKp.AttrsPerKey], nil
}

View File

@@ -2,7 +2,7 @@ package nkode
import (
"github.com/stretchr/testify/assert"
m "go-nkode/core/model"
"go-nkode/core/model"
"testing"
)
@@ -14,16 +14,16 @@ func TestCustomer(t *testing.T) {
func testNewCustomerAttributes(t *testing.T) {
// keypad := m.KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 5}
_, nil := NewCustomerAttributes()
_, nil := m.NewCustomerAttributes()
assert.NoError(t, nil)
}
func testCustomerValidKeyEntry(t *testing.T) {
kp := m.KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 9}
nkodePolicy := m.NewDefaultNKodePolicy()
customer, err := NewCustomer(nkodePolicy)
customer, err := m.NewCustomer(nkodePolicy)
assert.NoError(t, err)
newUserInterface, err := NewUserInterface(&kp)
newUserInterface, err := m.NewUserInterface(&kp)
assert.NoError(t, err)
username := m.Username("testing123")
passcodeIdx := []int{0, 1, 2, 3}
@@ -31,7 +31,7 @@ func testCustomerValidKeyEntry(t *testing.T) {
assert.NoError(t, err)
userLoginInterface, err := user.GetLoginInterface()
assert.NoError(t, err)
selectedKeys, err := SelectKeyByAttrIdx(userLoginInterface, passcodeIdx, kp)
selectedKeys, err := m.SelectKeyByAttrIdx(userLoginInterface, passcodeIdx, kp)
assert.NoError(t, err)
validatedPasscode, err := ValidKeyEntry(*user, *customer, selectedKeys)
assert.NoError(t, err)
@@ -44,9 +44,9 @@ func testCustomerValidKeyEntry(t *testing.T) {
func testCustomerIsValidNKode(t *testing.T) {
kp := m.KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 7}
nkodePolicy := m.NewDefaultNKodePolicy()
customer, err := NewCustomer(nkodePolicy)
customer, err := m.NewCustomer(nkodePolicy)
assert.NoError(t, err)
newUserInterface, err := NewUserInterface(&kp)
newUserInterface, err := m.NewUserInterface(&kp)
assert.NoError(t, err)
username := m.Username("testing123")
passcodeIdx := []int{0, 1, 2, 3}

View File

@@ -5,10 +5,11 @@ import (
)
type DbAccessor interface {
GetCustomer(m.CustomerId) (*Customer, error)
GetUser(m.Username, m.CustomerId) (*User, error)
WriteNewCustomer(Customer) error
WriteNewUser(User) error
UpdateUserInterface(m.UserId, UserInterface) error
GetCustomer(m.CustomerId) (*m.Customer, error)
GetUser(m.Username, m.CustomerId) (*m.User, error)
WriteNewCustomer(m.Customer) error
WriteNewUser(m.User) error
UpdateUserInterface(m.UserId, m.UserInterface) error
Renew(m.CustomerId) error
RefreshUser(m.User, []int, m.CustomerAttributes) error
}

View File

@@ -7,20 +7,20 @@ import (
)
type InMemoryDb struct {
Customers map[m.CustomerId]Customer
Users map[m.UserId]User
Customers map[m.CustomerId]m.Customer
Users map[m.UserId]m.User
userIdMap map[string]m.UserId
}
func NewInMemoryDb() InMemoryDb {
return InMemoryDb{
Customers: make(map[m.CustomerId]Customer),
Users: make(map[m.UserId]User),
Customers: make(map[m.CustomerId]m.Customer),
Users: make(map[m.UserId]m.User),
userIdMap: make(map[string]m.UserId),
}
}
func (db *InMemoryDb) GetCustomer(id m.CustomerId) (*Customer, error) {
func (db *InMemoryDb) GetCustomer(id m.CustomerId) (*m.Customer, error) {
customer, exists := db.Customers[id]
if !exists {
return nil, errors.New(fmt.Sprintf("customer %s dne", customer.Id))
@@ -28,7 +28,7 @@ func (db *InMemoryDb) GetCustomer(id m.CustomerId) (*Customer, error) {
return &customer, nil
}
func (db *InMemoryDb) GetUser(username m.Username, customerId m.CustomerId) (*User, error) {
func (db *InMemoryDb) GetUser(username m.Username, customerId m.CustomerId) (*m.User, error) {
key := userIdKey(customerId, username)
userId, exists := db.userIdMap[key]
if !exists {
@@ -41,7 +41,7 @@ func (db *InMemoryDb) GetUser(username m.Username, customerId m.CustomerId) (*Us
return &user, nil
}
func (db *InMemoryDb) WriteNewCustomer(customer Customer) error {
func (db *InMemoryDb) WriteNewCustomer(customer m.Customer) error {
_, exists := db.Customers[customer.Id]
if exists {
@@ -51,7 +51,7 @@ func (db *InMemoryDb) WriteNewCustomer(customer Customer) error {
return nil
}
func (db *InMemoryDb) WriteNewUser(user User) error {
func (db *InMemoryDb) WriteNewUser(user m.User) error {
_, exists := db.Customers[user.CustomerId]
if !exists {
return errors.New(fmt.Sprintf("can't add user %s to customer %s: customer dne", user.Username, user.CustomerId))
@@ -67,7 +67,7 @@ func (db *InMemoryDb) WriteNewUser(user User) error {
return nil
}
func (db *InMemoryDb) UpdateUserInterface(userId m.UserId, ui UserInterface) error {
func (db *InMemoryDb) UpdateUserInterface(userId m.UserId, ui m.UserInterface) error {
user, exists := db.Users[userId]
if !exists {
return errors.New(fmt.Sprintf("can't update user %s, dne", user.Id))
@@ -82,18 +82,29 @@ func (db *InMemoryDb) Renew(id m.CustomerId) error {
return errors.New(fmt.Sprintf("customer %s does not exist", id))
}
setXor, attrsXor := customer.RenewKeys()
db.Customers[id] = customer
var err error
for _, user := range db.Users {
if user.CustomerId == id {
err = user.RenewKeys(setXor[:user.Kp.AttrsPerKey], attrsXor[:user.Kp.TotalAttrs()])
err = user.RenewKeys(setXor, attrsXor)
if err != nil {
panic(err)
}
db.Users[user.Id] = user
}
}
return nil
}
func (db *InMemoryDb) RefreshUser(user m.User, passocode []int, customerAttr m.CustomerAttributes) error {
err := user.RefreshPasscode(passocode, customerAttr)
if err != nil {
return err
}
db.Users[user.Id] = user
return nil
}
func userIdKey(customerId m.CustomerId, username m.Username) string {
key := fmt.Sprintf("%s:%s", customerId, username)
return key

View File

@@ -19,7 +19,7 @@ func NewNKodeAPI(db DbAccessor) NKodeAPI {
}
func (n *NKodeAPI) CreateNewCustomer(nkodePolicy m.NKodePolicy) (*m.CustomerId, error) {
newCustomer, err := NewCustomer(nkodePolicy)
newCustomer, err := m.NewCustomer(nkodePolicy)
if err != nil {
return nil, err
}
@@ -118,7 +118,7 @@ func (n *NKodeAPI) Login(customerId m.CustomerId, username m.Username, keySelect
return err
}
if user.Renew {
err = user.RefreshPasscode(passcode, customer.Attributes)
err = n.Db.RefreshUser(*user, passcode, customer.Attributes)
if err != nil {
return err
}

View File

@@ -3,17 +3,34 @@ package nkode
import (
"github.com/stretchr/testify/assert"
m "go-nkode/core/model"
"os"
"testing"
)
func TestNKodeAPI(t *testing.T) {
//db1 := NewInMemoryDb()
//1testNKodeAPI(t, &db1)
dbFile := "test.db"
db2, err := NewSqliteDB(dbFile)
assert.NoError(t, err)
testNKodeAPI(t, db2)
if _, err := os.Stat(dbFile); err == nil {
err = os.Remove(dbFile)
assert.NoError(t, err)
} else {
assert.NoError(t, err)
}
}
func testNKodeAPI(t *testing.T, db DbAccessor) {
for idx := 0; idx < 10; idx++ {
db := NewInMemoryDb()
username := m.Username("test_username")
passcodeLen := 4
nkodePolicy := m.NewDefaultNKodePolicy()
keypadSize := m.KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 8}
nkodeApi := NewNKodeAPI(&db)
nkodeApi := NewNKodeAPI(db)
customerId, err := nkodeApi.CreateNewCustomer(nkodePolicy)
assert.NoError(t, err)
signupResponse, err := nkodeApi.GenerateSignupInterface(*customerId, keypadSize)
@@ -22,18 +39,18 @@ func TestNKodeAPI(t *testing.T) {
sessionId := signupResponse.SessionId
keypadSize = m.KeypadDimension{AttrsPerKey: 8, NumbOfKeys: 8}
userPasscode := setInterface[:passcodeLen]
setKeySelect, err := SelectKeyByAttrIdx(setInterface, userPasscode, keypadSize)
setKeySelect, err := m.SelectKeyByAttrIdx(setInterface, userPasscode, keypadSize)
assert.NoError(t, err)
confirmInterface, err := nkodeApi.SetNKode(username, *customerId, sessionId, setKeySelect)
assert.NoError(t, err)
confirmKeySelect, err := SelectKeyByAttrIdx(confirmInterface, userPasscode, keypadSize)
confirmKeySelect, err := m.SelectKeyByAttrIdx(confirmInterface, userPasscode, keypadSize)
err = nkodeApi.ConfirmNKode(*customerId, sessionId, confirmKeySelect)
assert.NoError(t, err)
keypadSize = m.KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 8}
loginInterface, err := nkodeApi.GetLoginInterface(username, *customerId)
assert.NoError(t, err)
loginKeySelection, err := SelectKeyByAttrIdx(loginInterface, userPasscode, keypadSize)
loginKeySelection, err := m.SelectKeyByAttrIdx(loginInterface, userPasscode, keypadSize)
assert.NoError(t, err)
err = nkodeApi.Login(*customerId, username, loginKeySelection)
assert.NoError(t, err)
@@ -43,9 +60,10 @@ func TestNKodeAPI(t *testing.T) {
loginInterface, err = nkodeApi.GetLoginInterface(username, *customerId)
assert.NoError(t, err)
loginKeySelection, err = SelectKeyByAttrIdx(loginInterface, userPasscode, keypadSize)
loginKeySelection, err = m.SelectKeyByAttrIdx(loginInterface, userPasscode, keypadSize)
assert.NoError(t, err)
err = nkodeApi.Login(*customerId, username, loginKeySelection)
assert.NoError(t, err)
}
}

View File

@@ -1 +1,339 @@
package nkode
import (
"database/sql"
"errors"
"fmt"
"github.com/google/uuid"
_ "github.com/mattn/go-sqlite3" // Import the SQLite3 driver
m "go-nkode/core/model"
"go-nkode/util"
"log"
)
type SqliteDB struct {
path string
}
func NewSqliteDB(path string) (*SqliteDB, error) {
sqldb := SqliteDB{path: path}
err := sqldb.NewTables()
return &sqldb, err
}
func (d *SqliteDB) NewTables() error {
db, err := sql.Open("sqlite3", d.path)
if err != nil {
log.Fatal(err)
}
defer db.Close()
createTables := `
PRAGMA foreign_keys = ON;
CREATE TABLE IF NOT EXISTS customer (
id TEXT NOT NULL PRIMARY KEY,
max_nkode_len INTEGER NOT NULL,
min_nkode_len INTEGER NOT NULL,
distinct_sets INTEGER NOT NULL,
distinct_attributes INTEGER NOT NULL,
lock_out INTEGER NOT NULL,
expiration INTEGER NOT NULL,
attribute_values BLOB NOT NULL,
set_values BLOB NOT NULL
);
CREATE TABLE IF NOT EXISTS user (
id TEXT NOT NULL PRIMARY KEY,
username TEXT NOT NULL,
renew INT NOT NULL,
customer_id TEXT NOT NULL,
-- Enciphered Passcode
code TEXT NOT NULL,
mask TEXT NOT NULL,
-- Keypad Dimensions
attributes_per_key INT NOT NULL,
number_of_keys INT NOT NULL,
-- User Keys
alpha_key BLOB NOT NULL,
set_key BLOB NOT NULL,
pass_key BLOB NOT NULL,
mask_key BLOB NOT NULL,
salt BLOB NOT NULL,
max_nkode_len INT NOT NULL,
-- User Interface
idx_interface BLOB NOT NULL,
FOREIGN KEY (customer_id) REFERENCES customers(id),
UNIQUE(customer_id, username)
);
`
_, err = db.Exec(createTables)
if err != nil {
return err
}
return nil
}
func (d *SqliteDB) WriteNewCustomer(c m.Customer) error {
db, err := sql.Open("sqlite3", d.path)
if err != nil {
log.Fatal(err)
}
defer db.Close()
insertCustomer := `
INSERT INTO customer (id, max_nkode_len, min_nkode_len, distinct_sets, distinct_attributes, lock_out, expiration, attribute_values, set_values)
VALUES (?,?,?,?,?,?,?,?,?)
`
_, err = db.Exec(insertCustomer, uuid.UUID(c.Id), c.NKodePolicy.MaxNkodeLen, c.NKodePolicy.MinNkodeLen, c.NKodePolicy.DistinctSets, c.NKodePolicy.DistinctAttributes, c.NKodePolicy.LockOut, c.NKodePolicy.Expiration, c.Attributes.AttrBytes(), c.Attributes.SetBytes())
return err
}
func (d *SqliteDB) WriteNewUser(u m.User) error {
db, err := sql.Open("sqlite3", d.path)
if err != nil {
log.Fatal(err)
}
defer db.Close()
insertUser := `
INSERT INTO user (id, username, renew, customer_id, code, mask, attributes_per_key, number_of_keys, alpha_key, set_key, pass_key, mask_key, salt, max_nkode_len, idx_interface)
VALUES (?,?, ?,?,?,?,?,?,?,?,?,?,?,?,?)
`
var renew int
if u.Renew {
renew = 1
} else {
renew = 0
}
_, err = db.Exec(insertUser, uuid.UUID(u.Id), u.Username, renew, uuid.UUID(u.CustomerId), u.EncipheredPasscode.Code, u.EncipheredPasscode.Mask, u.Kp.AttrsPerKey, u.Kp.NumbOfKeys, util.Uint64ArrToByteArr(u.UserKeys.AlphaKey), util.Uint64ArrToByteArr(u.UserKeys.SetKey), util.Uint64ArrToByteArr(u.UserKeys.PassKey), util.Uint64ArrToByteArr(u.UserKeys.MaskKey), u.UserKeys.Salt, u.UserKeys.MaxNKodeLen, util.IntArrToByteArr(u.Interface.IdxInterface))
return err
}
func (d *SqliteDB) GetCustomer(id m.CustomerId) (*m.Customer, error) {
db, err := sql.Open("sqlite3", d.path)
if err != nil {
return nil, err
}
defer db.Close()
selectCustomer := `SELECT max_nkode_len, min_nkode_len, distinct_sets, distinct_attributes, lock_out, expiration, attribute_values, set_values FROM customer WHERE id = ?`
rows, err := db.Query(selectCustomer, uuid.UUID(id))
if !rows.Next() {
return nil, errors.New(fmt.Sprintf("no new row for customer %s with err %s", id, rows.Err()))
}
var maxNKodeLen int
var minNKodeLen int
var distinctSets int
var distinctAttributes int
var lockOut int
var expiration int
var attributeValues []byte
var setValues []byte
err = rows.Scan(&maxNKodeLen, &minNKodeLen, &distinctSets, &distinctAttributes, &lockOut, &expiration, &attributeValues, &setValues)
if err != nil {
return nil, err
}
if rows.Next() {
return nil, errors.New(fmt.Sprintf("too many rows for customer %s", id))
}
customer := m.Customer{
Id: id,
NKodePolicy: m.NKodePolicy{
MaxNkodeLen: maxNKodeLen,
MinNkodeLen: minNKodeLen,
DistinctSets: distinctSets,
DistinctAttributes: distinctAttributes,
LockOut: lockOut,
Expiration: expiration,
},
Attributes: m.NewCustomerAttributesFromBytes(attributeValues, setValues),
}
return &customer, nil
}
func (d *SqliteDB) GetUser(username m.Username, customerId m.CustomerId) (*m.User, error) {
db, err := sql.Open("sqlite3", d.path)
if err != nil {
return nil, err
}
defer db.Close()
userSelect := `
SELECT id, renew, code, mask, attributes_per_key, number_of_keys, alpha_key, set_key, pass_key, mask_key, salt, max_nkode_len, idx_interface FROM user
WHERE user.username = ? AND user.customer_id = ?
`
rows, err := db.Query(userSelect, string(username), uuid.UUID(customerId).String())
if !rows.Next() {
return nil, errors.New(fmt.Sprintf("no new rows for user %s of customer %s", string(username), uuid.UUID(customerId).String()))
}
var id string
var renewVal int
var code string
var mask string
var attrsPerKey int
var numbOfKeys int
var alphaKey []byte
var setKey []byte
var passKey []byte
var maskKey []byte
var salt []byte
var maxNKodeLen int
var idxInterface []byte
err = rows.Scan(&id, &renewVal, &code, &mask, &attrsPerKey, &numbOfKeys, &alphaKey, &setKey, &passKey, &maskKey, &salt, &maxNKodeLen, &idxInterface)
if rows.Next() {
return nil, errors.New(fmt.Sprintf("too many rows for user %s of customer %s", username, customerId))
}
userId, err := uuid.Parse(id)
if err != nil {
return nil, err
}
var renew bool
if renewVal == 0 {
renew = false
} else {
renew = true
}
user := m.User{
Id: m.UserId(userId),
CustomerId: customerId,
Username: username,
EncipheredPasscode: m.EncipheredNKode{
Code: code,
Mask: mask,
},
Kp: m.KeypadDimension{
AttrsPerKey: attrsPerKey,
NumbOfKeys: numbOfKeys,
},
UserKeys: m.UserCipherKeys{
AlphaKey: util.ByteArrToUint64Arr(alphaKey),
SetKey: util.ByteArrToUint64Arr(setKey),
PassKey: util.ByteArrToUint64Arr(passKey),
MaskKey: util.ByteArrToUint64Arr(maskKey),
Salt: salt,
MaxNKodeLen: maxNKodeLen,
Kp: nil,
},
Interface: m.UserInterface{
IdxInterface: util.ByteArrToIntArr(idxInterface),
Kp: nil,
},
Renew: renew,
}
user.Interface.Kp = &user.Kp
user.UserKeys.Kp = &user.Kp
return &user, nil
}
func (d *SqliteDB) UpdateUserInterface(id m.UserId, ui m.UserInterface) error {
db, err := sql.Open("sqlite3", d.path)
if err != nil {
return err
}
defer db.Close()
updateUserInterface := `
UPDATE user SET idx_interface = ? WHERE id = ?
`
_, err = db.Exec(updateUserInterface, util.IntArrToByteArr(ui.IdxInterface), uuid.UUID(id).String())
return err
}
func (d *SqliteDB) Renew(id m.CustomerId) error {
customer, err := d.GetCustomer(id)
if err != nil {
return err
}
setXor, attrXor := customer.RenewKeys()
renewArgs := []any{util.Uint64ArrToByteArr(customer.Attributes.AttrVals), util.Uint64ArrToByteArr(customer.Attributes.SetVals), uuid.UUID(customer.Id).String()}
renewExec := `
BEGIN TRANSACTION;
UPDATE customer SET attribute_values = ?, set_values = ? WHERE id = ?;
`
db, err := sql.Open("sqlite3", d.path)
if err != nil {
return err
}
defer db.Close()
userQuery := `
SELECT id, alpha_key, set_key, attributes_per_key, number_of_keys FROM user WHERE customer_id = ?
`
rows, err := db.Query(userQuery, uuid.UUID(id).String())
for rows.Next() {
var userId string
var alphaBytes []byte
var setBytes []byte
var attrsPerKey int
var numbOfKeys int
err = rows.Scan(&userId, &alphaBytes, &setBytes, &attrsPerKey, &numbOfKeys)
if err != nil {
return err
}
user := m.User{
Id: m.UserId{},
CustomerId: m.CustomerId{},
Username: "",
EncipheredPasscode: m.EncipheredNKode{},
Kp: m.KeypadDimension{
AttrsPerKey: attrsPerKey,
NumbOfKeys: numbOfKeys,
},
UserKeys: m.UserCipherKeys{
AlphaKey: util.ByteArrToUint64Arr(alphaBytes),
SetKey: util.ByteArrToUint64Arr(setBytes),
},
Interface: m.UserInterface{},
Renew: false,
}
err = user.RenewKeys(setXor, attrXor)
if err != nil {
return err
}
renewExec += "\nUPDATE user SET alpha_key = ?, set_key = ?, renew = ? WHERE id = ?;"
renewArgs = append(renewArgs, util.Uint64ArrToByteArr(user.UserKeys.AlphaKey), util.Uint64ArrToByteArr(user.UserKeys.SetKey), 1, userId)
}
renewExec += `
COMMIT;
`
_, err = db.Exec(renewExec, renewArgs...)
return err
}
func (d *SqliteDB) RefreshUser(user m.User, passcodeIdx []int, customerAttr m.CustomerAttributes) error {
db, err := sql.Open("sqlite3", d.path)
if err != nil {
return err
}
defer db.Close()
err = user.RefreshPasscode(passcodeIdx, customerAttr)
if err != nil {
return err
}
updateUser := `
UPDATE user SET renew = ?, code = ?, mask = ?, alpha_key = ?, set_key = ?, pass_key = ?, mask_key = ?, salt = ? WHERE id = ?;
`
_, err = db.Exec(updateUser, 0, user.EncipheredPasscode.Code, user.EncipheredPasscode.Mask, util.Uint64ArrToByteArr(user.UserKeys.AlphaKey), util.Uint64ArrToByteArr(user.UserKeys.SetKey), util.Uint64ArrToByteArr(user.UserKeys.PassKey), util.Uint64ArrToByteArr(user.UserKeys.MaskKey), user.UserKeys.Salt, uuid.UUID(user.Id).String())
return err
}

View File

@@ -0,0 +1,44 @@
package nkode
import (
"github.com/stretchr/testify/assert"
m "go-nkode/core/model"
"os"
"testing"
)
func TestNewSqliteDB(t *testing.T) {
dbFile := "test.db"
db, err := NewSqliteDB(dbFile)
assert.NoError(t, err)
nkode_policy := m.NewDefaultNKodePolicy()
customerOrig, err := m.NewCustomer(nkode_policy)
assert.NoError(t, err)
err = db.WriteNewCustomer(*customerOrig)
assert.NoError(t, err)
customer, err := db.GetCustomer(customerOrig.Id)
assert.NoError(t, err)
assert.Equal(t, customerOrig, customer)
username := m.Username("test_user")
kp := m.KeypadDefault
passcodeIdx := []int{0, 1, 2, 3}
ui, err := m.NewUserInterface(&kp)
assert.NoError(t, err)
userOrig, err := NewUser(*customer, username, passcodeIdx, *ui, kp)
assert.NoError(t, err)
err = db.WriteNewUser(*userOrig)
assert.NoError(t, err)
user, err := db.GetUser(username, customer.Id)
assert.NoError(t, err)
assert.Equal(t, userOrig, user)
err = db.Renew(customer.Id)
assert.NoError(t, err)
if _, err := os.Stat(dbFile); err == nil {
err = os.Remove(dbFile)
assert.NoError(t, err)
} else {
assert.NoError(t, err)
}
}

View File

@@ -1,21 +0,0 @@
package nkode
import (
"errors"
"fmt"
m "go-nkode/core/model"
"go-nkode/util"
)
func SelectKeyByAttrIdx(interfaceUser []int, passcodeIdxs []int, keypadSize m.KeypadDimension) ([]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
}

View File

@@ -1,62 +0,0 @@
package nkode
import (
m "go-nkode/core/model"
"go-nkode/util"
)
type User struct {
Id m.UserId
CustomerId m.CustomerId
Username m.Username
EncipheredPasscode m.EncipheredNKode
Kp m.KeypadDimension
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 {
panic(err)
}
u.UserKeys.AlphaKey, err = util.XorLists(attrXor, u.UserKeys.AlphaKey)
if err != nil {
panic(err)
}
return nil
}
func (u *User) RefreshPasscode(passcodeAttrIdx []int, customerAttributes CustomerAttributes) error {
setVals, err := customerAttributes.SetVals(u.Kp)
newKeys, err := NewUserCipherKeys(&u.Kp, 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
}
func (u *User) GetLoginInterface() ([]int, error) {
err := u.Interface.PartialInterfaceShuffle()
if err != nil {
return nil, err
}
return u.Interface.IdxInterface, nil
}

View File

@@ -1,198 +0,0 @@
package nkode
import (
"crypto/sha256"
"errors"
"github.com/google/uuid"
m "go-nkode/core/model"
"go-nkode/util"
"golang.org/x/crypto/bcrypt"
)
type UserCipherKeys struct {
Id uuid.UUID
UserId m.UserId
AlphaKey []uint64
SetKey []uint64
PassKey []uint64
MaskKey []uint64
Salt []byte
MaxNKodeLen int
kp *m.KeypadDimension
}
func NewUserCipherKeys(kp *m.KeypadDimension, setVals []uint64, maxNKodeLen int) (*UserCipherKeys, error) {
err := kp.IsValidKeypadDimension()
if err != nil {
return nil, err
}
setKey, err := util.GenerateRandomNonRepeatingUint64(kp.AttrsPerKey)
if err != nil {
return nil, err
}
setKey, err = util.XorLists(setKey, setVals)
if err != nil {
return nil, err
}
alphakey, _ := util.GenerateRandomNonRepeatingUint64(kp.TotalAttrs())
passKey, _ := util.GenerateRandomNonRepeatingUint64(maxNKodeLen)
maskKey, _ := util.GenerateRandomNonRepeatingUint64(maxNKodeLen)
salt, _ := util.RandomBytes(10)
userCipherKeys := UserCipherKeys{
Id: uuid.New(),
AlphaKey: alphakey,
PassKey: passKey,
MaskKey: maskKey,
SetKey: setKey,
Salt: salt,
MaxNKodeLen: maxNKodeLen,
kp: kp,
}
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, attrVals []uint64) error {
hashBytes := []byte(hashedPassword)
passcodeCipher := u.encipherCode(passcodeAttrIdx, attrVals)
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, attrVals []uint64) (string, error) {
passcodeCipher := u.encipherCode(passcodeAttrIdx, attrVals)
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, attrVals []uint64) []uint64 {
passcodeLen := len(passcodeAttrIdx)
passcodeCipher := make([]uint64, u.MaxNKodeLen)
for idx := 0; idx < passcodeLen; idx++ {
attrIdx := passcodeAttrIdx[idx]
alpha := u.AlphaKey[attrIdx]
attrVal := 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, userKp m.KeypadDimension) (string, error) {
setVals, err := customerAttrs.SetVals(userKp)
if err != nil {
return "", err
}
paddedPasscodeSets, err := u.PadUserMask(passcodeSet, 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) (*m.EncipheredNKode, error) {
attrVals, err := customerAttrs.AttrVals(*u.kp)
code, err := u.EncipherSaltHashCode(passcodeAttrIdx, attrVals)
if err != nil {
return nil, err
}
passcodeSet := make([]uint64, len(passcodeAttrIdx))
for idx := range passcodeSet {
passcodeAttr := attrVals[passcodeAttrIdx[idx]]
passcodeSet[idx], err = customerAttrs.GetAttrSetVal(passcodeAttr, *u.kp)
if err != nil {
return nil, err
}
}
mask, err := u.EncipherMask(passcodeSet, customerAttrs, *u.kp)
encipheredCode := m.EncipheredNKode{
Code: code,
Mask: mask,
}
return &encipheredCode, nil
}

View File

@@ -1,192 +0,0 @@
package nkode
import (
"errors"
"fmt"
m "go-nkode/core/model"
"go-nkode/hashset"
"go-nkode/util"
)
type UserInterface struct {
IdxInterface m.IdxInterface
Kp *m.KeypadDimension
}
func NewUserInterface(kp *m.KeypadDimension) (*UserInterface, error) {
idxInterface := util.IdentityArray(kp.TotalAttrs())
userInterface := UserInterface{
IdxInterface: idxInterface,
Kp: kp,
}
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.Kp.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.Kp.IsDispersable() {
panic("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.Kp.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.Kp.AttrsPerKey / 2
if u.Kp.AttrsPerKey&1 == 1 {
numbOfSelectedSets += util.Choice[int]([]int{0, 1})
}
setIdxs, err := util.RandomPermutation(u.Kp.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.Kp.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.Kp.NumbOfKeys <= keyNumb {
return -1, errors.New(fmt.Sprintf("keyNumb %d is out of range 0-%d", keyNumb, u.Kp.NumbOfKeys))
}
if setIdx < 0 || u.Kp.AttrsPerKey <= setIdx {
return -1, errors.New(fmt.Sprintf("setIdx %d is out of range 0-%d", setIdx, u.Kp.AttrsPerKey))
}
keypadView, err := u.InterfaceMatrix()
if err != nil {
return -1, err
}
return keypadView[keyNumb][setIdx], nil
}

View File

@@ -13,7 +13,7 @@ import (
type UserSignSession struct {
Id m.SessionId
CustomerId m.CustomerId
LoginUserInterface UserInterface
LoginUserInterface m.UserInterface
Kp m.KeypadDimension
SetIdxInterface m.IdxInterface
ConfirmIdxInterface m.IdxInterface
@@ -23,7 +23,7 @@ type UserSignSession struct {
}
func NewSignupSession(kp m.KeypadDimension, customerId m.CustomerId) (*UserSignSession, error) {
loginInterface, err := NewUserInterface(&kp)
loginInterface, err := m.NewUserInterface(&kp)
if err != nil {
return nil, err
}
@@ -109,7 +109,7 @@ func (s *UserSignSession) SetUserNKode(username m.Username, keySelection m.KeySe
s.SetKeySelection = keySelection
s.Username = username
setKp := s.SignupKeypad()
setInterface := UserInterface{IdxInterface: s.SetIdxInterface, Kp: &setKp}
setInterface := m.UserInterface{IdxInterface: s.SetIdxInterface, Kp: &setKp}
err := setInterface.DisperseInterface()
if err != nil {
return nil, err
@@ -132,7 +132,7 @@ func (s *UserSignSession) getSelectedKeyVals(keySelections m.KeySelection, userI
return keyVals, nil
}
func signupInterface(baseUserInterface UserInterface, kp m.KeypadDimension) (*UserInterface, error) {
func signupInterface(baseUserInterface m.UserInterface, kp m.KeypadDimension) (*m.UserInterface, error) {
if kp.IsDispersable() {
return nil, errors.New("keypad is dispersable, can't use signupInterface")
}
@@ -158,7 +158,7 @@ func signupInterface(baseUserInterface UserInterface, kp m.KeypadDimension) (*Us
if err != nil {
return nil, err
}
signupUserInterface := UserInterface{
signupUserInterface := m.UserInterface{
IdxInterface: util.MatrixToList(attrSetView),
Kp: &m.KeypadDimension{
AttrsPerKey: numbOfKeys,

View File

@@ -1,127 +0,0 @@
package nkode
import (
"github.com/stretchr/testify/assert"
m "go-nkode/core/model"
py "go-nkode/py-builtin"
"testing"
)
func TestUserCipherKeys_EncipherSaltHashCode(t *testing.T) {
kp := m.KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 8}
maxNKodeLen := 10
customerAttrs, err := NewCustomerAttributes()
assert.NoError(t, err)
setVals, err := customerAttrs.SetVals(kp)
assert.NoError(t, err)
attrVals, err := customerAttrs.AttrVals(kp)
assert.NoError(t, err)
newUser, err := NewUserCipherKeys(&kp, setVals, maxNKodeLen)
assert.NoError(t, err)
passcodeIdx := []int{0, 1, 2, 3}
encipher0, err := newUser.EncipherSaltHashCode(passcodeIdx, attrVals)
assert.NoError(t, err)
err = newUser.ValidPassword(encipher0, passcodeIdx, attrVals)
assert.NoError(t, err)
}
func TestUserCipherKeys_EncipherDecipherMask(t *testing.T) {
kp := m.KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 8}
maxNKodeLen := 10
customerAttrs, err := NewCustomerAttributes()
assert.NoError(t, err)
setVals, err := customerAttrs.SetVals(kp)
assert.NoError(t, err)
attrVals, err := customerAttrs.AttrVals(kp)
assert.NoError(t, err)
newUser, err := NewUserCipherKeys(&kp, setVals, maxNKodeLen)
assert.NoError(t, err)
passcodeIdx := []int{0, 1, 2, 3}
originalSetVals := make([]uint64, len(passcodeIdx))
for idx, val := range passcodeIdx {
attr := attrVals[val]
originalSetVals[idx], err = customerAttrs.GetAttrSetVal(attr, kp)
assert.NoError(t, err)
}
encipheredCode, err := newUser.EncipherNKode(passcodeIdx, *customerAttrs)
assert.NoError(t, err)
passcodeSetVals, err := newUser.DecipherMask(encipheredCode.Mask, setVals, len(passcodeIdx))
assert.NoError(t, err)
for idx, setVal := range passcodeSetVals {
assert.Equal(t, setVal, originalSetVals[idx])
}
}
func TestUserInterface_RandomShuffle(t *testing.T) {
kp := m.KeypadDimension{
AttrsPerKey: 10,
NumbOfKeys: 8,
}
userInterface, err := NewUserInterface(&kp)
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++ {
kp := m.KeypadDimension{AttrsPerKey: 7, NumbOfKeys: 10}
userInterface, err := NewUserInterface(&kp)
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) {
kp := m.KeypadDimension{AttrsPerKey: 7, NumbOfKeys: 10}
userInterface, err := NewUserInterface(&kp)
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.All[bool](shuffleCompare, func(n bool) bool {
return n == true
})
assert.False(t, allTrue)
allFalse := py.All[bool](shuffleCompare, func(n bool) bool {
return n == false
})
assert.False(t, allFalse)
}