diff --git a/core/api/endpoints.go b/core/api/endpoints.go deleted file mode 100644 index 08b1a94..0000000 --- a/core/api/endpoints.go +++ /dev/null @@ -1,12 +0,0 @@ -package api - -const ( - CreateNewCustomer = "/create-new-customer" - GenerateSignupInterface = "/generate-signup-interface" - SetNKode = "/set-nkode" - ConfirmNKode = "/confirm-nkode" - GetLoginInterface = "/get-login-interface" - Login = "/login" - RenewAttributes = "/renew-attributes" - RandomSvgInterface = "/random-svg-interface" -) diff --git a/core/model/customer.go b/core/customer.go similarity index 99% rename from core/model/customer.go rename to core/customer.go index df919d6..005f63d 100644 --- a/core/model/customer.go +++ b/core/customer.go @@ -1,4 +1,4 @@ -package model +package core import ( "errors" diff --git a/core/model/customer_attributes.go b/core/customer_attributes.go similarity index 99% rename from core/model/customer_attributes.go rename to core/customer_attributes.go index 586abe8..518e2ce 100644 --- a/core/model/customer_attributes.go +++ b/core/customer_attributes.go @@ -1,4 +1,4 @@ -package model +package core import ( "errors" diff --git a/core/nkode/customer_test.go b/core/customer_test.go similarity index 56% rename from core/nkode/customer_test.go rename to core/customer_test.go index cb04a1c..a38d887 100644 --- a/core/nkode/customer_test.go +++ b/core/customer_test.go @@ -1,8 +1,7 @@ -package nkode +package core import ( "github.com/stretchr/testify/assert" - "go-nkode/core/model" "testing" ) @@ -13,26 +12,25 @@ func TestCustomer(t *testing.T) { } func testNewCustomerAttributes(t *testing.T) { - // keypad := m.KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 5} - _, nil := model.NewCustomerAttributes() + _, nil := NewCustomerAttributes() assert.NoError(t, nil) } func testCustomerValidKeyEntry(t *testing.T) { - kp := model.KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 9} - nkodePolicy := model.NewDefaultNKodePolicy() - customer, err := model.NewCustomer(nkodePolicy) + kp := KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 9} + nkodePolicy := NewDefaultNKodePolicy() + customer, err := NewCustomer(nkodePolicy) assert.NoError(t, err) - mockSvgInterface := make(model.SvgIdInterface, kp.TotalAttrs()) - userInterface, err := model.NewUserInterface(&kp, mockSvgInterface) + mockSvgInterface := make(SvgIdInterface, kp.TotalAttrs()) + userInterface, err := NewUserInterface(&kp, mockSvgInterface) assert.NoError(t, err) - username := model.Username("testing123") + username := Username("testing123") passcodeIdx := []int{0, 1, 2, 3} user, err := NewUser(*customer, username, passcodeIdx, *userInterface, kp) assert.NoError(t, err) userLoginInterface, err := user.GetLoginInterface() assert.NoError(t, err) - selectedKeys, err := model.SelectKeyByAttrIdx(userLoginInterface, passcodeIdx, kp) + selectedKeys, err := SelectKeyByAttrIdx(userLoginInterface, passcodeIdx, kp) assert.NoError(t, err) validatedPasscode, err := ValidKeyEntry(*user, *customer, selectedKeys) assert.NoError(t, err) @@ -43,14 +41,14 @@ func testCustomerValidKeyEntry(t *testing.T) { } func testCustomerIsValidNKode(t *testing.T) { - kp := model.KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 7} - nkodePolicy := model.NewDefaultNKodePolicy() - customer, err := model.NewCustomer(nkodePolicy) + kp := KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 7} + nkodePolicy := NewDefaultNKodePolicy() + customer, err := NewCustomer(nkodePolicy) assert.NoError(t, err) - mockSvgInterface := make(model.SvgIdInterface, kp.TotalAttrs()) - userInterface, err := model.NewUserInterface(&kp, mockSvgInterface) + mockSvgInterface := make(SvgIdInterface, kp.TotalAttrs()) + userInterface, err := NewUserInterface(&kp, mockSvgInterface) assert.NoError(t, err) - username := model.Username("testing123") + username := Username("testing123") passcodeIdx := []int{0, 1, 2, 3} user, err := NewUser(*customer, username, passcodeIdx, *userInterface, kp) assert.NoError(t, err) diff --git a/core/nkode/in_memory_db.go b/core/in_memory_db.go similarity index 61% rename from core/nkode/in_memory_db.go rename to core/in_memory_db.go index 1aed53c..face3e7 100644 --- a/core/nkode/in_memory_db.go +++ b/core/in_memory_db.go @@ -1,26 +1,25 @@ -package nkode +package core import ( "errors" "fmt" - m "go-nkode/core/model" ) type InMemoryDb struct { - Customers map[m.CustomerId]m.Customer - Users map[m.UserId]m.User - userIdMap map[string]m.UserId + Customers map[CustomerId]Customer + Users map[UserId]User + userIdMap map[string]UserId } func NewInMemoryDb() InMemoryDb { return InMemoryDb{ - Customers: make(map[m.CustomerId]m.Customer), - Users: make(map[m.UserId]m.User), - userIdMap: make(map[string]m.UserId), + Customers: make(map[CustomerId]Customer), + Users: make(map[UserId]User), + userIdMap: make(map[string]UserId), } } -func (db *InMemoryDb) GetCustomer(id m.CustomerId) (*m.Customer, error) { +func (db *InMemoryDb) GetCustomer(id CustomerId) (*Customer, error) { customer, exists := db.Customers[id] if !exists { return nil, errors.New(fmt.Sprintf("customer %s dne", customer.Id)) @@ -28,7 +27,7 @@ func (db *InMemoryDb) GetCustomer(id m.CustomerId) (*m.Customer, error) { return &customer, nil } -func (db *InMemoryDb) GetUser(username m.Username, customerId m.CustomerId) (*m.User, error) { +func (db *InMemoryDb) GetUser(username Username, customerId CustomerId) (*User, error) { key := userIdKey(customerId, username) userId, exists := db.userIdMap[key] if !exists { @@ -41,7 +40,7 @@ func (db *InMemoryDb) GetUser(username m.Username, customerId m.CustomerId) (*m. return &user, nil } -func (db *InMemoryDb) WriteNewCustomer(customer m.Customer) error { +func (db *InMemoryDb) WriteNewCustomer(customer Customer) error { _, exists := db.Customers[customer.Id] if exists { @@ -51,7 +50,7 @@ func (db *InMemoryDb) WriteNewCustomer(customer m.Customer) error { return nil } -func (db *InMemoryDb) WriteNewUser(user m.User) error { +func (db *InMemoryDb) WriteNewUser(user 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,16 +66,17 @@ func (db *InMemoryDb) WriteNewUser(user m.User) error { return nil } -func (db *InMemoryDb) UpdateUserInterface(userId m.UserId, ui m.UserInterface) error { +func (db *InMemoryDb) UpdateUserInterface(userId UserId, ui UserInterface) error { user, exists := db.Users[userId] if !exists { return errors.New(fmt.Sprintf("can't update user %s, dne", user.Id)) } user.Interface = ui + db.Users[userId] = user return nil } -func (db *InMemoryDb) Renew(id m.CustomerId) error { +func (db *InMemoryDb) Renew(id CustomerId) error { customer, exists := db.Customers[id] if !exists { return errors.New(fmt.Sprintf("customer %s does not exist", id)) @@ -88,7 +88,7 @@ func (db *InMemoryDb) Renew(id m.CustomerId) error { if user.CustomerId == id { err = user.RenewKeys(setXor, attrsXor) if err != nil { - panic(err) + return err } db.Users[user.Id] = user } @@ -96,7 +96,7 @@ func (db *InMemoryDb) Renew(id m.CustomerId) error { return nil } -func (db *InMemoryDb) RefreshUser(user m.User, passocode []int, customerAttr m.CustomerAttributes) error { +func (db *InMemoryDb) RefreshUser(user User, passocode []int, customerAttr CustomerAttributes) error { err := user.RefreshPasscode(passocode, customerAttr) if err != nil { return err @@ -105,19 +105,23 @@ func (db *InMemoryDb) RefreshUser(user m.User, passocode []int, customerAttr m.C return nil } -func (db *InMemoryDb) RandomSvgInterface(kp m.KeypadDimension) ([]string, error) { - return nil, errors.ErrUnsupported +func (db *InMemoryDb) RandomSvgInterface(kp KeypadDimension) ([]string, error) { + return make([]string, kp.TotalAttrs()), nil } -func (db *InMemoryDb) RandomSvgIdxInterface(kp m.KeypadDimension) (m.SvgIdInterface, error) { - return nil, errors.ErrUnsupported +func (db *InMemoryDb) RandomSvgIdxInterface(kp KeypadDimension) (SvgIdInterface, error) { + svgs := make(SvgIdInterface, kp.TotalAttrs()) + for idx := range svgs { + svgs[idx] = idx + } + return svgs, nil } -func (db InMemoryDb) GetSvgStringInterface(idxs m.SvgIdInterface) ([]string, error) { - return nil, errors.ErrUnsupported +func (db InMemoryDb) GetSvgStringInterface(idxs SvgIdInterface) ([]string, error) { + return make([]string, len(idxs)), nil } -func userIdKey(customerId m.CustomerId, username m.Username) string { +func userIdKey(customerId CustomerId, username Username) string { key := fmt.Sprintf("%s:%s", customerId, username) return key } diff --git a/core/model/keypad_dimension.go b/core/keypad_dimension.go similarity index 94% rename from core/model/keypad_dimension.go rename to core/keypad_dimension.go index 258451c..964319e 100644 --- a/core/model/keypad_dimension.go +++ b/core/keypad_dimension.go @@ -1,4 +1,4 @@ -package model +package core import "errors" @@ -29,8 +29,8 @@ var ( } KeypadMin = KeypadDimension{ - AttrsPerKey: 6, - NumbOfKeys: 5, + AttrsPerKey: 5, + NumbOfKeys: 4, } KeypadDefault = KeypadDimension{ diff --git a/core/model/user.go b/core/model/user.go deleted file mode 100644 index fe9dff5..0000000 --- a/core/model/user.go +++ /dev/null @@ -1,58 +0,0 @@ -package model - -import ( - "go-nkode/util" -) - -type User struct { - Id UserId - CustomerId CustomerId - Username Username - EncipheredPasscode EncipheredNKode - Kp KeypadDimension - CipherKeys UserCipherKeys - Interface UserInterface - Renew bool -} - -func (u *User) DecipherMask(setVals []uint64, passcodeLen int) ([]uint64, error) { - return u.CipherKeys.DecipherMask(u.EncipheredPasscode.Mask, setVals, passcodeLen) -} - -func (u *User) RenewKeys(setXor []uint64, attrXor []uint64) error { - u.Renew = true - var err error - u.CipherKeys.SetKey, err = util.XorLists(setXor[:u.Kp.AttrsPerKey], u.CipherKeys.SetKey) - if err != nil { - panic(err) - } - u.CipherKeys.AlphaKey, err = util.XorLists(attrXor[:u.Kp.TotalAttrs()], u.CipherKeys.AlphaKey) - return err -} - -func (u *User) RefreshPasscode(passcodeAttrIdx []int, customerAttributes CustomerAttributes) error { - setVals, err := customerAttributes.SetValsForKp(u.Kp) - newKeys, err := NewUserCipherKeys(&u.Kp, setVals, u.CipherKeys.MaxNKodeLen) - if err != nil { - return err - } - - encipheredPasscode, err := newKeys.EncipherNKode(passcodeAttrIdx, customerAttributes) - if err != nil { - return err - } - - u.CipherKeys = *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 -} diff --git a/core/nkode/common.go b/core/nkode/common.go deleted file mode 100644 index 6b8bf8a..0000000 --- a/core/nkode/common.go +++ /dev/null @@ -1,89 +0,0 @@ -package nkode - -import ( - "errors" - "github.com/google/uuid" - m "go-nkode/core/model" - py "go-nkode/py-builtin" -) - -var KeyIndexOutOfRange = errors.New("one or more keys is out of range") - -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 - }) - if !validKeys { - panic(KeyIndexOutOfRange) - } - - var err error - passcodeLen := len(selectedKeys) - err = customer.NKodePolicy.ValidLength(passcodeLen) - if err != nil { - return nil, err - } - - setVals, err := customer.Attributes.SetValsForKp(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 := customer.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 - } - err = customer.IsValidNKode(user.Kp, presumedAttrIdxVals) - if err != nil { - panic(err) - } - attrVals, err := customer.Attributes.AttrValsForKp(user.Kp) - if err != nil { - panic(err) - } - err = user.CipherKeys.ValidPassword(user.EncipheredPasscode.Code, presumedAttrIdxVals, attrVals) - if err != nil { - return nil, err - } - - return presumedAttrIdxVals, nil -} - -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 := m.NewUserCipherKeys(&kp, setVals, customer.NKodePolicy.MaxNkodeLen) - if err != nil { - return nil, err - } - encipheredNKode, err := newKeys.EncipherNKode(passcodeIdx, customer.Attributes) - if err != nil { - return nil, err - } - newUser := m.User{ - Id: m.UserId(uuid.New()), - Username: username, - EncipheredPasscode: *encipheredNKode, - CipherKeys: *newKeys, - Interface: ui, - Kp: kp, - CustomerId: customer.Id, - } - return &newUser, nil -} diff --git a/core/nkode/db_accessor.go b/core/nkode/db_accessor.go deleted file mode 100644 index a8a8da3..0000000 --- a/core/nkode/db_accessor.go +++ /dev/null @@ -1,18 +0,0 @@ -package nkode - -import ( - "go-nkode/core/model" -) - -type DbAccessor interface { - GetCustomer(model.CustomerId) (*model.Customer, error) - GetUser(model.Username, model.CustomerId) (*model.User, error) - WriteNewCustomer(model.Customer) error - WriteNewUser(model.User) error - UpdateUserInterface(model.UserId, model.UserInterface) error - Renew(model.CustomerId) error - RefreshUser(model.User, []int, model.CustomerAttributes) error - RandomSvgInterface(model.KeypadDimension) ([]string, error) - RandomSvgIdxInterface(model.KeypadDimension) (model.SvgIdInterface, error) - GetSvgStringInterface(model.SvgIdInterface) ([]string, error) -} diff --git a/core/nkode/nkode_api.go b/core/nkode_api.go similarity index 67% rename from core/nkode/nkode_api.go rename to core/nkode_api.go index 819a141..6ba3ba4 100644 --- a/core/nkode/nkode_api.go +++ b/core/nkode_api.go @@ -1,26 +1,25 @@ -package nkode +package core import ( "errors" "fmt" "github.com/google/uuid" - m "go-nkode/core/model" ) type NKodeAPI struct { Db DbAccessor - SignupSessions map[m.SessionId]UserSignSession + SignupSessions map[SessionId]UserSignSession } func NewNKodeAPI(db DbAccessor) NKodeAPI { return NKodeAPI{ Db: db, - SignupSessions: make(map[m.SessionId]UserSignSession), + SignupSessions: make(map[SessionId]UserSignSession), } } -func (n *NKodeAPI) CreateNewCustomer(nkodePolicy m.NKodePolicy, id *m.CustomerId) (*m.CustomerId, error) { - newCustomer, err := m.NewCustomer(nkodePolicy) +func (n *NKodeAPI) CreateNewCustomer(nkodePolicy NKodePolicy, id *CustomerId) (*CustomerId, error) { + newCustomer, err := NewCustomer(nkodePolicy) if id != nil { newCustomer.Id = *id } @@ -35,7 +34,7 @@ func (n *NKodeAPI) CreateNewCustomer(nkodePolicy m.NKodePolicy, id *m.CustomerId return &newCustomer.Id, nil } -func (n *NKodeAPI) GenerateSignupInterface(username m.Username, customerId m.CustomerId, kp m.KeypadDimension) (*m.GenerateSignupInterfaceResp, error) { +func (n *NKodeAPI) GenerateSignupInterface(username Username, customerId CustomerId, kp KeypadDimension) (*GenerateSignupInterfaceResp, error) { svgIdxInterface, err := n.Db.RandomSvgIdxInterface(kp) if err != nil { return nil, err @@ -49,7 +48,7 @@ func (n *NKodeAPI) GenerateSignupInterface(username m.Username, customerId m.Cus if err != nil { return nil, err } - resp := m.GenerateSignupInterfaceResp{ + resp := GenerateSignupInterfaceResp{ UserIdxInterface: signupSession.SetIdxInterface, SvgInterface: svgInterface, SessionId: uuid.UUID(signupSession.Id).String(), @@ -57,7 +56,7 @@ func (n *NKodeAPI) GenerateSignupInterface(username m.Username, customerId m.Cus return &resp, nil } -func (n *NKodeAPI) SetNKode(customerId m.CustomerId, sessionId m.SessionId, keySelection m.KeySelection) (m.IdxInterface, error) { +func (n *NKodeAPI) SetNKode(customerId CustomerId, sessionId SessionId, keySelection KeySelection) (IdxInterface, error) { _, err := n.Db.GetCustomer(customerId) if err != nil { @@ -75,7 +74,7 @@ func (n *NKodeAPI) SetNKode(customerId m.CustomerId, sessionId m.SessionId, keyS return confirmInterface, nil } -func (n *NKodeAPI) ConfirmNKode(customerId m.CustomerId, sessionId m.SessionId, keySelection m.KeySelection) error { +func (n *NKodeAPI) ConfirmNKode(customerId CustomerId, sessionId SessionId, keySelection KeySelection) error { session, exists := n.SignupSessions[sessionId] if !exists { return errors.New(fmt.Sprintf("session id does not exist %s", sessionId)) @@ -101,7 +100,7 @@ func (n *NKodeAPI) ConfirmNKode(customerId m.CustomerId, sessionId m.SessionId, return err } -func (n *NKodeAPI) GetLoginInterface(username m.Username, customerId m.CustomerId) (*m.GetLoginInterfaceResp, error) { +func (n *NKodeAPI) GetLoginInterface(username Username, customerId CustomerId) (*GetLoginInterfaceResp, error) { user, err := n.Db.GetUser(username, customerId) if err != nil { return nil, err @@ -118,43 +117,50 @@ func (n *NKodeAPI) GetLoginInterface(username m.Username, customerId m.CustomerI if err != nil { return nil, err } - resp := m.GetLoginInterfaceResp{ + resp := GetLoginInterfaceResp{ UserIdxInterface: user.Interface.IdxInterface, SvgInterface: svgInterface, + NumbOfKeys: user.Kp.NumbOfKeys, + AttrsPerKey: user.Kp.AttrsPerKey, } return &resp, nil } -func (n *NKodeAPI) Login(customerId m.CustomerId, username m.Username, keySelection m.KeySelection) error { +func (n *NKodeAPI) Login(customerId CustomerId, username Username, keySelection KeySelection) (string, error) { customer, err := n.Db.GetCustomer(customerId) if err != nil { - return err + return "", err } user, err := n.Db.GetUser(username, customerId) if err != nil { - return errors.New(fmt.Sprintf("user dne %s", username)) + return "", errors.New(fmt.Sprintf("user dne %s", username)) } passcode, err := ValidKeyEntry(*user, *customer, keySelection) if err != nil { - return err + return "", err } + if user.Renew { err = n.Db.RefreshUser(*user, passcode, customer.Attributes) if err != nil { - return err + return "", err } } - return nil + return "", nil } -func (n *NKodeAPI) RenewAttributes(customerId m.CustomerId) error { +func (n *NKodeAPI) RenewAttributes(customerId CustomerId) error { return n.Db.Renew(customerId) } func (n *NKodeAPI) RandomSvgInterface() ([]string, error) { - return n.Db.RandomSvgInterface(m.KeypadMax) + return n.Db.RandomSvgInterface(KeypadMax) } -func (n *NKodeAPI) GetSvgStringInterface(svgId m.SvgIdInterface) ([]string, error) { +func (n *NKodeAPI) GetSvgStringInterface(svgId SvgIdInterface) ([]string, error) { return n.Db.GetSvgStringInterface(svgId) } + +func (n *NKodeAPI) RefreshToken(jwt string) (string, error) { + return "", nil +} diff --git a/core/nkode/nkode_api_test.go b/core/nkode_api_test.go similarity index 52% rename from core/nkode/nkode_api_test.go rename to core/nkode_api_test.go index 6f2e952..5ea15cf 100644 --- a/core/nkode/nkode_api_test.go +++ b/core/nkode_api_test.go @@ -1,22 +1,20 @@ -package nkode +package core import ( "github.com/stretchr/testify/assert" - m "go-nkode/core/model" "go-nkode/util" "testing" ) func TestNKodeAPI(t *testing.T) { - //db1 := NewInMemoryDb() - //1testNKodeAPI(t, &db1) + db1 := NewInMemoryDb() + testNKodeAPI(t, &db1) - dbFile := "../../test.db" + //dbFile := "../../test.db" - // sql_driver.MakeTables(dbFile) - db2 := NewSqliteDB(dbFile) - defer db2.CloseDb() - testNKodeAPI(t, db2) + //db2 := NewSqliteDB(dbFile) + //defer db2.CloseDb() + //testNKodeAPI(t, db2) // if _, err := os.Stat(dbFile); err == nil { // err = os.Remove(dbFile) @@ -27,35 +25,38 @@ func TestNKodeAPI(t *testing.T) { } func testNKodeAPI(t *testing.T, db DbAccessor) { - + attrsPerKey := 5 + numbOfKeys := 4 for idx := 0; idx < 1; idx++ { - username := m.Username("test_username" + util.GenerateRandomString(12)) + username := Username("test_username" + util.GenerateRandomString(12)) passcodeLen := 4 - nkodePolicy := m.NewDefaultNKodePolicy() - keypadSize := m.KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 8} + nkodePolicy := NewDefaultNKodePolicy() + keypadSize := KeypadDimension{AttrsPerKey: attrsPerKey, NumbOfKeys: numbOfKeys} nkodeApi := NewNKodeAPI(db) customerId, err := nkodeApi.CreateNewCustomer(nkodePolicy, nil) assert.NoError(t, err) signupResponse, err := nkodeApi.GenerateSignupInterface(username, *customerId, keypadSize) assert.NoError(t, err) setInterface := signupResponse.UserIdxInterface - sessionId := signupResponse.SessionId - keypadSize = m.KeypadDimension{AttrsPerKey: 8, NumbOfKeys: 8} + sessionIdStr := signupResponse.SessionId + sessionId, err := SessionIdFromString(sessionIdStr) + assert.NoError(t, err) + keypadSize = KeypadDimension{AttrsPerKey: numbOfKeys, NumbOfKeys: numbOfKeys} userPasscode := setInterface[:passcodeLen] - setKeySelect, err := m.SelectKeyByAttrIdx(setInterface, userPasscode, keypadSize) + setKeySelect, err := SelectKeyByAttrIdx(setInterface, userPasscode, keypadSize) assert.NoError(t, err) confirmInterface, err := nkodeApi.SetNKode(*customerId, sessionId, setKeySelect) assert.NoError(t, err) - confirmKeySelect, err := m.SelectKeyByAttrIdx(confirmInterface, userPasscode, keypadSize) + confirmKeySelect, err := SelectKeyByAttrIdx(confirmInterface, userPasscode, keypadSize) err = nkodeApi.ConfirmNKode(*customerId, sessionId, confirmKeySelect) assert.NoError(t, err) - keypadSize = m.KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 8} + keypadSize = KeypadDimension{AttrsPerKey: attrsPerKey, NumbOfKeys: numbOfKeys} loginInterface, err := nkodeApi.GetLoginInterface(username, *customerId) assert.NoError(t, err) - loginKeySelection, err := m.SelectKeyByAttrIdx(loginInterface.UserIdxInterface, userPasscode, keypadSize) + loginKeySelection, err := SelectKeyByAttrIdx(loginInterface.UserIdxInterface, userPasscode, keypadSize) assert.NoError(t, err) - err = nkodeApi.Login(*customerId, username, loginKeySelection) + _, err = nkodeApi.Login(*customerId, username, loginKeySelection) assert.NoError(t, err) err = nkodeApi.RenewAttributes(*customerId) @@ -63,9 +64,9 @@ func testNKodeAPI(t *testing.T, db DbAccessor) { loginInterface, err = nkodeApi.GetLoginInterface(username, *customerId) assert.NoError(t, err) - loginKeySelection, err = m.SelectKeyByAttrIdx(loginInterface.UserIdxInterface, userPasscode, keypadSize) + loginKeySelection, err = SelectKeyByAttrIdx(loginInterface.UserIdxInterface, userPasscode, keypadSize) assert.NoError(t, err) - err = nkodeApi.Login(*customerId, username, loginKeySelection) + _, err = nkodeApi.Login(*customerId, username, loginKeySelection) assert.NoError(t, err) } diff --git a/core/model/nkode_handler.go b/core/nkode_handler.go similarity index 90% rename from core/model/nkode_handler.go rename to core/nkode_handler.go index 94c3cb9..0a4d070 100644 --- a/core/model/nkode_handler.go +++ b/core/nkode_handler.go @@ -1,36 +1,49 @@ -package model +package core import ( "encoding/json" "errors" "github.com/google/uuid" - "go-nkode/core/api" "log" "net/http" ) type NKodeHandler struct { - Api NKodeAPIInterface + Api NKodeAPI } +const ( + CreateNewCustomer = "/create-new-customer" + GenerateSignupInterface = "/generate-signup-interface" + SetNKode = "/set-nkode" + ConfirmNKode = "/confirm-nkode" + GetLoginInterface = "/get-login-interface" + Login = "/login" + RenewAttributes = "/renew-attributes" + RandomSvgInterface = "/random-svg-interface" + RefreshToken = "refresh-token" +) + func (h *NKodeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { - case api.CreateNewCustomer: + case CreateNewCustomer: h.CreateNewCustomerHandler(w, r) - case api.GenerateSignupInterface: + case GenerateSignupInterface: h.GenerateSignupInterfaceHandler(w, r) - case api.SetNKode: + case SetNKode: h.SetNKodeHandler(w, r) - case api.ConfirmNKode: + case ConfirmNKode: h.ConfirmNKodeHandler(w, r) - case api.GetLoginInterface: + case GetLoginInterface: h.GetLoginInterfaceHandler(w, r) - case api.Login: + case Login: h.LoginHandler(w, r) - case api.RenewAttributes: + case RenewAttributes: h.RenewAttributesHandler(w, r) - case api.RandomSvgInterface: + case RandomSvgInterface: h.RandomSvgInterfaceHandler(w, r) + case RefreshToken: + h.RefreshTokenHandler(w, r) default: w.WriteHeader(http.StatusNotFound) _, err := w.Write([]byte("404 not found")) @@ -273,7 +286,7 @@ func (h *NKodeHandler) LoginHandler(w http.ResponseWriter, r *http.Request) { log.Println(err) return } - err = h.Api.Login(CustomerId(customerId), loginPost.Username, loginPost.KeySelection) + _, err = h.Api.Login(CustomerId(customerId), loginPost.Username, loginPost.KeySelection) if err != nil { internalServerErrorHandler(w) log.Println(err) @@ -340,6 +353,10 @@ func (h *NKodeHandler) RandomSvgInterfaceHandler(w http.ResponseWriter, r *http. w.WriteHeader(http.StatusOK) } +func (h *NKodeHandler) RefreshTokenHandler(w http.ResponseWriter, r *http.Request) { + +} + func decodeJson(w http.ResponseWriter, r *http.Request, post any) error { if r.Body == nil { invalidJson(w) diff --git a/core/model/nkode_policy.go b/core/nkode_policy.go similarity index 98% rename from core/model/nkode_policy.go rename to core/nkode_policy.go index 0aba02b..e5be258 100644 --- a/core/model/nkode_policy.go +++ b/core/nkode_policy.go @@ -1,4 +1,4 @@ -package model +package core import ( "errors" diff --git a/core/nkode/sqlite_db.go b/core/sqlite_db.go similarity index 86% rename from core/nkode/sqlite_db.go rename to core/sqlite_db.go index 630e0fc..2e87c17 100644 --- a/core/nkode/sqlite_db.go +++ b/core/sqlite_db.go @@ -1,4 +1,4 @@ -package nkode +package core import ( "database/sql" @@ -6,7 +6,6 @@ import ( "fmt" "github.com/google/uuid" _ "github.com/mattn/go-sqlite3" // Import the SQLite3 driver - m "go-nkode/core/model" "go-nkode/util" "log" ) @@ -32,7 +31,7 @@ func (d *SqliteDB) CloseDb() { } } -func (d *SqliteDB) WriteNewCustomer(c m.Customer) error { +func (d *SqliteDB) WriteNewCustomer(c Customer) error { tx, err := d.db.Begin() if err != nil { return err @@ -60,7 +59,7 @@ VALUES (?,?,?,?,?,?,?,?,?) return nil } -func (d *SqliteDB) WriteNewUser(u m.User) error { +func (d *SqliteDB) WriteNewUser(u User) error { tx, err := d.db.Begin() if err != nil { return err @@ -95,7 +94,7 @@ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) return nil } -func (d *SqliteDB) GetCustomer(id m.CustomerId) (*m.Customer, error) { +func (d *SqliteDB) GetCustomer(id CustomerId) (*Customer, error) { 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 := d.db.Query(selectCustomer, uuid.UUID(id)) @@ -119,9 +118,9 @@ func (d *SqliteDB) GetCustomer(id m.CustomerId) (*m.Customer, error) { if rows.Next() { return nil, errors.New(fmt.Sprintf("too many rows for customer %s", id)) } - customer := m.Customer{ + customer := Customer{ Id: id, - NKodePolicy: m.NKodePolicy{ + NKodePolicy: NKodePolicy{ MaxNkodeLen: maxNKodeLen, MinNkodeLen: minNKodeLen, DistinctSets: distinctSets, @@ -129,13 +128,13 @@ func (d *SqliteDB) GetCustomer(id m.CustomerId) (*m.Customer, error) { LockOut: lockOut, Expiration: expiration, }, - Attributes: m.NewCustomerAttributesFromBytes(attributeValues, setValues), + Attributes: NewCustomerAttributesFromBytes(attributeValues, setValues), } return &customer, nil } -func (d *SqliteDB) GetUser(username m.Username, customerId m.CustomerId) (*m.User, error) { +func (d *SqliteDB) GetUser(username Username, customerId CustomerId) (*User, error) { 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, svg_id_interface FROM user WHERE user.username = ? AND user.customer_id = ? @@ -175,19 +174,19 @@ WHERE user.username = ? AND user.customer_id = ? renew = true } - user := m.User{ - Id: m.UserId(userId), + user := User{ + Id: UserId(userId), CustomerId: customerId, Username: username, - EncipheredPasscode: m.EncipheredNKode{ + EncipheredPasscode: EncipheredNKode{ Code: code, Mask: mask, }, - Kp: m.KeypadDimension{ + Kp: KeypadDimension{ AttrsPerKey: attrsPerKey, NumbOfKeys: numbOfKeys, }, - CipherKeys: m.UserCipherKeys{ + CipherKeys: UserCipherKeys{ AlphaKey: util.ByteArrToUint64Arr(alphaKey), SetKey: util.ByteArrToUint64Arr(setKey), PassKey: util.ByteArrToUint64Arr(passKey), @@ -196,7 +195,7 @@ WHERE user.username = ? AND user.customer_id = ? MaxNKodeLen: maxNKodeLen, Kp: nil, }, - Interface: m.UserInterface{ + Interface: UserInterface{ IdxInterface: util.ByteArrToIntArr(idxInterface), SvgId: util.ByteArrToIntArr(svgIdInterface), Kp: nil, @@ -209,7 +208,7 @@ WHERE user.username = ? AND user.customer_id = ? return &user, nil } -func (d *SqliteDB) UpdateUserInterface(id m.UserId, ui m.UserInterface) error { +func (d *SqliteDB) UpdateUserInterface(id UserId, ui UserInterface) error { updateUserInterface := ` UPDATE user SET idx_interface = ? WHERE id = ? ` @@ -218,7 +217,7 @@ UPDATE user SET idx_interface = ? WHERE id = ? return err } -func (d *SqliteDB) Renew(id m.CustomerId) error { +func (d *SqliteDB) Renew(id CustomerId) error { customer, err := d.GetCustomer(id) if err != nil { return err @@ -246,20 +245,20 @@ SELECT id, alpha_key, set_key, attributes_per_key, number_of_keys FROM user WHER if err != nil { return err } - user := m.User{ - Id: m.UserId{}, - CustomerId: m.CustomerId{}, + user := User{ + Id: UserId{}, + CustomerId: CustomerId{}, Username: "", - EncipheredPasscode: m.EncipheredNKode{}, - Kp: m.KeypadDimension{ + EncipheredPasscode: EncipheredNKode{}, + Kp: KeypadDimension{ AttrsPerKey: attrsPerKey, NumbOfKeys: numbOfKeys, }, - CipherKeys: m.UserCipherKeys{ + CipherKeys: UserCipherKeys{ AlphaKey: util.ByteArrToUint64Arr(alphaBytes), SetKey: util.ByteArrToUint64Arr(setBytes), }, - Interface: m.UserInterface{}, + Interface: UserInterface{}, Renew: false, } err = user.RenewKeys(setXor, attrXor) @@ -277,7 +276,7 @@ COMMIT; return err } -func (d *SqliteDB) RefreshUser(user m.User, passcodeIdx []int, customerAttr m.CustomerAttributes) error { +func (d *SqliteDB) RefreshUser(user User, passcodeIdx []int, customerAttr CustomerAttributes) error { err := user.RefreshPasscode(passcodeIdx, customerAttr) if err != nil { return err @@ -289,7 +288,7 @@ UPDATE user SET renew = ?, code = ?, mask = ?, alpha_key = ?, set_key = ?, pass_ return err } -func (d *SqliteDB) RandomSvgInterface(kp m.KeypadDimension) ([]string, error) { +func (d *SqliteDB) RandomSvgInterface(kp KeypadDimension) ([]string, error) { ids, err := d.getRandomIds(kp.TotalAttrs()) if err != nil { return nil, err @@ -297,11 +296,11 @@ func (d *SqliteDB) RandomSvgInterface(kp m.KeypadDimension) ([]string, error) { return d.getSvgsById(ids) } -func (d *SqliteDB) RandomSvgIdxInterface(kp m.KeypadDimension) (m.SvgIdInterface, error) { +func (d *SqliteDB) RandomSvgIdxInterface(kp KeypadDimension) (SvgIdInterface, error) { return d.getRandomIds(kp.TotalAttrs()) } -func (d *SqliteDB) GetSvgStringInterface(idxs m.SvgIdInterface) ([]string, error) { +func (d *SqliteDB) GetSvgStringInterface(idxs SvgIdInterface) ([]string, error) { return d.getSvgsById(idxs) } diff --git a/core/nkode/sqlite_db_test.go b/core/sqlite_db_test.go similarity index 77% rename from core/nkode/sqlite_db_test.go rename to core/sqlite_db_test.go index 15ee21f..1d22bd6 100644 --- a/core/nkode/sqlite_db_test.go +++ b/core/sqlite_db_test.go @@ -1,13 +1,12 @@ -package nkode +package core import ( "github.com/stretchr/testify/assert" - m "go-nkode/core/model" "testing" ) func TestNewSqliteDB(t *testing.T) { - dbFile := "../../test.db" + dbFile := "../test.db" // sql_driver.MakeTables(dbFile) db := NewSqliteDB(dbFile) defer db.CloseDb() @@ -23,19 +22,19 @@ func TestNewSqliteDB(t *testing.T) { } func testSignupLoginRenew(t *testing.T, db DbAccessor) { - nkode_policy := m.NewDefaultNKodePolicy() - customerOrig, err := m.NewCustomer(nkode_policy) + nkode_policy := NewDefaultNKodePolicy() + customerOrig, err := 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 + username := Username("test_user") + kp := KeypadDefault passcodeIdx := []int{0, 1, 2, 3} - mockSvgInterface := make(m.SvgIdInterface, kp.TotalAttrs()) - ui, err := m.NewUserInterface(&kp, mockSvgInterface) + mockSvgInterface := make(SvgIdInterface, kp.TotalAttrs()) + ui, err := NewUserInterface(&kp, mockSvgInterface) assert.NoError(t, err) userOrig, err := NewUser(*customer, username, passcodeIdx, *ui, kp) assert.NoError(t, err) @@ -51,7 +50,7 @@ func testSignupLoginRenew(t *testing.T, db DbAccessor) { } func testSqliteDBRandomSvgInterface(t *testing.T, db DbAccessor) { - kp := m.KeypadMax + kp := KeypadMax svgs, err := db.RandomSvgInterface(kp) assert.NoError(t, err) assert.Len(t, svgs, kp.TotalAttrs()) diff --git a/core/svg-icon/db_interface.go b/core/svg-icon/db_interface.go deleted file mode 100644 index 37603a1..0000000 --- a/core/svg-icon/db_interface.go +++ /dev/null @@ -1,58 +0,0 @@ -package svg_icon - -import ( - "database/sql" - "go-nkode/util" -) - -type SvgIcon struct { - Id int - Svg string -} - -type SvgIconDb struct { - path string -} - -func (d *SvgIconDb) GetSvgsById(ids []int) ([]string, error) { - db, err := sql.Open("sqlite3", d.path) - if err != nil { - return nil, err - } - defer db.Close() - selectId := "SELECT svg FROM svg_icon where id = ?" - svgs := make([]string, len(ids)) - for idx, id := range ids { - rows, err := db.Query(selectId, id) - if err != nil { - return nil, err - } - err = rows.Scan(&svgs[idx]) - if err != nil { - return nil, err - } - } - return svgs, nil -} - -func (d *SvgIconDb) GetRandomIds(count int) ([]int, error) { - db, err := sql.Open("sqlite3", d.path) - if err != nil { - return nil, err - } - defer db.Close() - rows, err := db.Query("SELECT COUNT(*) FROM svg_icon;") - if err != nil { - return nil, err - } - var tableLen int - err = rows.Scan(&tableLen) - if err != nil { - return nil, err - } - perm, err := util.RandomPermutation(tableLen) - if err != nil { - return nil, err - } - return perm[:count], nil -} diff --git a/core/model/test_helper.go b/core/test_helper.go similarity index 97% rename from core/model/test_helper.go rename to core/test_helper.go index 5a5e361..ceb7761 100644 --- a/core/model/test_helper.go +++ b/core/test_helper.go @@ -1,4 +1,4 @@ -package model +package core import ( "errors" diff --git a/core/model/type.go b/core/type.go similarity index 70% rename from core/model/type.go rename to core/type.go index 6177560..1981cef 100644 --- a/core/model/type.go +++ b/core/type.go @@ -1,4 +1,4 @@ -package model +package core import ( "github.com/google/uuid" @@ -63,6 +63,8 @@ type GenerateSignupInterfaceResp struct { type GetLoginInterfaceResp struct { UserIdxInterface IdxInterface `json:"user_interface"` SvgInterface []string `json:"svg_interface"` + AttrsPerKey int `json:"attrs_per_key"` + NumbOfKeys int `json:"numb_of_keys"` } type KeySelection []int @@ -74,19 +76,29 @@ type Username string type IdxInterface []int type SvgIdInterface []int -type NKodeAPIInterface interface { - CreateNewCustomer(NKodePolicy, *CustomerId) (*CustomerId, error) - GenerateSignupInterface(Username, CustomerId, KeypadDimension) (*GenerateSignupInterfaceResp, error) - SetNKode(CustomerId, SessionId, KeySelection) (IdxInterface, error) - ConfirmNKode(CustomerId, SessionId, KeySelection) error - GetLoginInterface(Username, CustomerId) (*GetLoginInterfaceResp, error) - Login(CustomerId, Username, KeySelection) error - RenewAttributes(CustomerId) error - RandomSvgInterface() ([]string, error) - GetSvgStringInterface(idInterface SvgIdInterface) ([]string, error) +func SessionIdFromString(sessionId string) (SessionId, error) { + id, err := uuid.Parse(sessionId) + if err != nil { + return SessionId{}, err + } + + return SessionId(id), nil } type EncipheredNKode struct { Code string Mask string } + +type DbAccessor interface { + GetCustomer(CustomerId) (*Customer, error) + GetUser(Username, CustomerId) (*User, error) + WriteNewCustomer(Customer) error + WriteNewUser(User) error + UpdateUserInterface(UserId, UserInterface) error + Renew(CustomerId) error + RefreshUser(User, []int, CustomerAttributes) error + RandomSvgInterface(KeypadDimension) ([]string, error) + RandomSvgIdxInterface(KeypadDimension) (SvgIdInterface, error) + GetSvgStringInterface(SvgIdInterface) ([]string, error) +} diff --git a/core/user.go b/core/user.go new file mode 100644 index 0000000..47751a9 --- /dev/null +++ b/core/user.go @@ -0,0 +1,142 @@ +package core + +import ( + "errors" + "github.com/google/uuid" + "go-nkode/py-builtin" + "go-nkode/util" +) + +type User struct { + Id UserId + CustomerId CustomerId + Username Username + EncipheredPasscode EncipheredNKode + Kp KeypadDimension + CipherKeys UserCipherKeys + Interface UserInterface + Renew bool +} + +func (u *User) DecipherMask(setVals []uint64, passcodeLen int) ([]uint64, error) { + return u.CipherKeys.DecipherMask(u.EncipheredPasscode.Mask, setVals, passcodeLen) +} + +func (u *User) RenewKeys(setXor []uint64, attrXor []uint64) error { + u.Renew = true + var err error + u.CipherKeys.SetKey, err = util.XorLists(setXor[:u.Kp.AttrsPerKey], u.CipherKeys.SetKey) + if err != nil { + return err + } + u.CipherKeys.AlphaKey, err = util.XorLists(attrXor[:u.Kp.TotalAttrs()], u.CipherKeys.AlphaKey) + return err +} + +func (u *User) RefreshPasscode(passcodeAttrIdx []int, customerAttributes CustomerAttributes) error { + setVals, err := customerAttributes.SetValsForKp(u.Kp) + newKeys, err := NewUserCipherKeys(&u.Kp, setVals, u.CipherKeys.MaxNKodeLen) + if err != nil { + return err + } + + encipheredPasscode, err := newKeys.EncipherNKode(passcodeAttrIdx, customerAttributes) + if err != nil { + return err + } + + u.CipherKeys = *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 +} + +var KeyIndexOutOfRange = errors.New("one or more keys is out of range") + +func ValidKeyEntry(user User, customer Customer, selectedKeys []int) ([]int, error) { + validKeys := py_builtin.All[int](selectedKeys, func(idx int) bool { + return 0 <= idx && idx < user.Kp.NumbOfKeys + }) + if !validKeys { + panic(KeyIndexOutOfRange) + } + + var err error + passcodeLen := len(selectedKeys) + err = customer.NKodePolicy.ValidLength(passcodeLen) + if err != nil { + return nil, err + } + + setVals, err := customer.Attributes.SetValsForKp(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 := customer.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 + } + err = customer.IsValidNKode(user.Kp, presumedAttrIdxVals) + if err != nil { + return nil, err + } + attrVals, err := customer.Attributes.AttrValsForKp(user.Kp) + if err != nil { + return nil, err + } + err = user.CipherKeys.ValidPassword(user.EncipheredPasscode.Code, presumedAttrIdxVals, attrVals) + if err != nil { + return nil, err + } + + return presumedAttrIdxVals, nil +} + +func NewUser(customer Customer, username Username, passcodeIdx []int, ui UserInterface, kp KeypadDimension) (*User, error) { + setVals, err := customer.Attributes.SetValsForKp(kp) + if err != nil { + return nil, err + } + newKeys, err := NewUserCipherKeys(&kp, setVals, customer.NKodePolicy.MaxNkodeLen) + if err != nil { + return nil, err + } + encipheredNKode, err := newKeys.EncipherNKode(passcodeIdx, customer.Attributes) + if err != nil { + return nil, err + } + newUser := User{ + Id: UserId(uuid.New()), + Username: username, + EncipheredPasscode: *encipheredNKode, + CipherKeys: *newKeys, + Interface: ui, + Kp: kp, + CustomerId: customer.Id, + } + return &newUser, nil +} diff --git a/core/model/user_cipher_keys.go b/core/user_cipher_keys.go similarity index 88% rename from core/model/user_cipher_keys.go rename to core/user_cipher_keys.go index df63a21..022d46c 100644 --- a/core/model/user_cipher_keys.go +++ b/core/user_cipher_keys.go @@ -1,4 +1,4 @@ -package model +package core import ( "crypto/sha256" @@ -31,12 +31,12 @@ func NewUserCipherKeys(kp *KeypadDimension, setVals []uint64, maxNKodeLen int) ( return nil, err } - alphakey, _ := util.GenerateRandomNonRepeatingUint64(kp.TotalAttrs()) + alphaKey, _ := util.GenerateRandomNonRepeatingUint64(kp.TotalAttrs()) passKey, _ := util.GenerateRandomNonRepeatingUint64(maxNKodeLen) maskKey, _ := util.GenerateRandomNonRepeatingUint64(maxNKodeLen) salt, _ := util.RandomBytes(10) userCipherKeys := UserCipherKeys{ - AlphaKey: alphakey, + AlphaKey: alphaKey, PassKey: passKey, MaskKey: maskKey, SetKey: setKey, @@ -62,11 +62,8 @@ func (u *UserCipherKeys) PadUserMask(userMask []uint64, setVals []uint64) ([]uin 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) + passwordDigest := u.saltAndDigest(passcodeCipher) + err := bcrypt.CompareHashAndPassword(hashBytes, passwordDigest) if err != nil { return err } @@ -76,10 +73,7 @@ func (u *UserCipherKeys) ValidPassword(hashedPassword string, passcodeAttrIdx [] 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 - } + passcodeDigest := u.saltAndDigest(passcodeCipher) passcodeBytes, err := u.hashPasscode(passcodeDigest) if err != nil { return "", err @@ -91,37 +85,33 @@ func (u *UserCipherKeys) encipherCode(passcodeAttrIdx []int, attrVals []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] + attrVal := attrVals[attrIdx] pass := u.PassKey[idx] passcodeCipher[idx] = alpha ^ pass ^ attrVal } return passcodeCipher } -func (u *UserCipherKeys) saltAndDigest(passcode []uint64) ([]byte, error) { +func (u *UserCipherKeys) saltAndDigest(passcode []uint64) []byte { 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 + h.Write(passcodeBytes) + + return h.Sum(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 KeypadDimension) (string, error) { setVals, err := customerAttrs.SetValsForKp(userKp) if err != nil { diff --git a/core/model/user_interface.go b/core/user_interface.go similarity index 99% rename from core/model/user_interface.go rename to core/user_interface.go index 1c0b064..804210e 100644 --- a/core/model/user_interface.go +++ b/core/user_interface.go @@ -1,4 +1,4 @@ -package model +package core import ( "errors" diff --git a/core/nkode/user_signup_session.go b/core/user_signup_session.go similarity index 76% rename from core/nkode/user_signup_session.go rename to core/user_signup_session.go index 6fdd87e..830d00c 100644 --- a/core/nkode/user_signup_session.go +++ b/core/user_signup_session.go @@ -1,29 +1,28 @@ -package nkode +package core import ( "errors" "fmt" "github.com/google/uuid" - m "go-nkode/core/model" "go-nkode/hashset" py "go-nkode/py-builtin" "go-nkode/util" ) type UserSignSession struct { - Id m.SessionId - CustomerId m.CustomerId - LoginUserInterface m.UserInterface - Kp m.KeypadDimension - SetIdxInterface m.IdxInterface - ConfirmIdxInterface m.IdxInterface - SetKeySelection m.KeySelection - Username m.Username + Id SessionId + CustomerId CustomerId + LoginUserInterface UserInterface + Kp KeypadDimension + SetIdxInterface IdxInterface + ConfirmIdxInterface IdxInterface + SetKeySelection KeySelection + Username Username Expire int } -func NewSignupSession(username m.Username, kp m.KeypadDimension, customerId m.CustomerId, svgInterface m.SvgIdInterface) (*UserSignSession, error) { - loginInterface, err := m.NewUserInterface(&kp, svgInterface) +func NewSignupSession(username Username, kp KeypadDimension, customerId CustomerId, svgInterface SvgIdInterface) (*UserSignSession, error) { + loginInterface, err := NewUserInterface(&kp, svgInterface) if err != nil { return nil, err } @@ -32,7 +31,7 @@ func NewSignupSession(username m.Username, kp m.KeypadDimension, customerId m.Cu return nil, err } session := UserSignSession{ - Id: m.SessionId(uuid.New()), + Id: SessionId(uuid.New()), CustomerId: customerId, LoginUserInterface: *loginInterface, SetIdxInterface: signupInterface.IdxInterface, @@ -45,7 +44,7 @@ func NewSignupSession(username m.Username, kp m.KeypadDimension, customerId m.Cu return &session, nil } -func (s *UserSignSession) DeducePasscode(confirmKeyEntry m.KeySelection) ([]int, error) { +func (s *UserSignSession) DeducePasscode(confirmKeyEntry KeySelection) ([]int, error) { validEntry := py.All[int](confirmKeyEntry, func(i int) bool { return 0 <= i && i < s.Kp.NumbOfKeys }) @@ -98,7 +97,7 @@ func (s *UserSignSession) DeducePasscode(confirmKeyEntry m.KeySelection) ([]int, return passcode, nil } -func (s *UserSignSession) SetUserNKode(keySelection m.KeySelection) (m.IdxInterface, error) { +func (s *UserSignSession) SetUserNKode(keySelection KeySelection) (IdxInterface, error) { validKeySelection := py.All[int](keySelection, func(i int) bool { return 0 <= i && i < s.Kp.NumbOfKeys }) @@ -108,7 +107,7 @@ func (s *UserSignSession) SetUserNKode(keySelection m.KeySelection) (m.IdxInterf s.SetKeySelection = keySelection setKp := s.SignupKeypad() - setInterface := m.UserInterface{IdxInterface: s.SetIdxInterface, Kp: &setKp} + setInterface := UserInterface{IdxInterface: s.SetIdxInterface, Kp: &setKp} err := setInterface.DisperseInterface() if err != nil { return nil, err @@ -117,7 +116,7 @@ func (s *UserSignSession) SetUserNKode(keySelection m.KeySelection) (m.IdxInterf return s.ConfirmIdxInterface, nil } -func (s *UserSignSession) getSelectedKeyVals(keySelections m.KeySelection, userInterface []int) ([][]int, error) { +func (s *UserSignSession) getSelectedKeyVals(keySelections KeySelection, userInterface []int) ([][]int, error) { signupKp := s.SignupKeypad() keypadInterface, err := util.ListToMatrix(userInterface, signupKp.AttrsPerKey) if err != nil { @@ -131,7 +130,7 @@ func (s *UserSignSession) getSelectedKeyVals(keySelections m.KeySelection, userI return keyVals, nil } -func signupInterface(baseUserInterface m.UserInterface, kp m.KeypadDimension) (*m.UserInterface, error) { +func signupInterface(baseUserInterface UserInterface, kp KeypadDimension) (*UserInterface, error) { if kp.IsDispersable() { return nil, errors.New("keypad is dispersable, can't use signupInterface") } @@ -157,9 +156,9 @@ func signupInterface(baseUserInterface m.UserInterface, kp m.KeypadDimension) (* if err != nil { return nil, err } - signupUserInterface := m.UserInterface{ + signupUserInterface := UserInterface{ IdxInterface: util.MatrixToList(attrSetView), - Kp: &m.KeypadDimension{ + Kp: &KeypadDimension{ AttrsPerKey: numbOfKeys, NumbOfKeys: numbOfKeys, }, @@ -167,8 +166,8 @@ func signupInterface(baseUserInterface m.UserInterface, kp m.KeypadDimension) (* return &signupUserInterface, nil } -func (s *UserSignSession) SignupKeypad() m.KeypadDimension { - return m.KeypadDimension{ +func (s *UserSignSession) SignupKeypad() KeypadDimension { + return KeypadDimension{ AttrsPerKey: s.Kp.NumbOfKeys, NumbOfKeys: s.Kp.NumbOfKeys, } diff --git a/core/model/user_test.go b/core/user_test.go similarity index 95% rename from core/model/user_test.go rename to core/user_test.go index aa9aeb6..b7b5e09 100644 --- a/core/model/user_test.go +++ b/core/user_test.go @@ -1,4 +1,4 @@ -package model +package core import ( "github.com/stretchr/testify/assert" @@ -22,6 +22,11 @@ func TestUserCipherKeys_EncipherSaltHashCode(t *testing.T) { assert.NoError(t, err) err = newUser.ValidPassword(encipher0, passcodeIdx, attrVals) assert.NoError(t, err) + + passcodeIdxInvalid := []int{1, 0, 3, 2} + assert.NoError(t, err) + err = newUser.ValidPassword(encipher0, passcodeIdxInvalid, attrVals) + assert.Error(t, err) } func TestUserCipherKeys_EncipherDecipherMask(t *testing.T) { diff --git a/go.mod b/go.mod index 770c591..b58ce95 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 1d4e786..aa30304 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= diff --git a/main.go b/main.go index 4bda7cc..de239d2 100644 --- a/main.go +++ b/main.go @@ -3,29 +3,27 @@ package main import ( "fmt" "github.com/google/uuid" - "go-nkode/core/api" - "go-nkode/core/model" - "go-nkode/core/nkode" + "go-nkode/core" "log" "net/http" ) func main() { //db := nkode.NewInMemoryDb() - db := nkode.NewSqliteDB("nkode.db") + db := core.NewSqliteDB("nkode.db") defer db.CloseDb() - nkodeApi := nkode.NewNKodeAPI(db) + nkodeApi := core.NewNKodeAPI(db) AddDefaultCustomer(nkodeApi) - handler := model.NKodeHandler{Api: &nkodeApi} + handler := core.NKodeHandler{Api: nkodeApi} mux := http.NewServeMux() - mux.Handle(api.CreateNewCustomer, &handler) - mux.Handle(api.GenerateSignupInterface, &handler) - mux.Handle(api.SetNKode, &handler) - mux.Handle(api.ConfirmNKode, &handler) - mux.Handle(api.GetLoginInterface, &handler) - mux.Handle(api.Login, &handler) - mux.Handle(api.RenewAttributes, &handler) - mux.Handle(api.RandomSvgInterface, &handler) + mux.Handle(core.CreateNewCustomer, &handler) + mux.Handle(core.GenerateSignupInterface, &handler) + mux.Handle(core.SetNKode, &handler) + mux.Handle(core.ConfirmNKode, &handler) + mux.Handle(core.GetLoginInterface, &handler) + mux.Handle(core.Login, &handler) + mux.Handle(core.RenewAttributes, &handler) + mux.Handle(core.RandomSvgInterface, &handler) fmt.Println("Running on localhost:8080...") log.Fatal(http.ListenAndServe("localhost:8080", corsMiddleware(mux))) } @@ -48,13 +46,13 @@ func corsMiddleware(next http.Handler) http.Handler { }) } -func AddDefaultCustomer(api nkode.NKodeAPI) { +func AddDefaultCustomer(api core.NKodeAPI) { newId, err := uuid.Parse("ed9ed6e0-082c-4b57-8d8c-f00ed6493457") if err != nil { log.Fatal(err) } - customerId := model.CustomerId(newId) - nkode_policy := model.NewDefaultNKodePolicy() + customerId := core.CustomerId(newId) + nkode_policy := core.NewDefaultNKodePolicy() _, err = api.CreateNewCustomer(nkode_policy, &customerId) if err != nil { log.Print(err) diff --git a/main_test.go b/main_test.go index b6afe0f..d16352b 100644 --- a/main_test.go +++ b/main_test.go @@ -4,8 +4,7 @@ import ( "bytes" "encoding/json" "github.com/stretchr/testify/assert" - "go-nkode/core/api" - m "go-nkode/core/model" + "go-nkode/core" "io" "net/http" "testing" @@ -13,84 +12,84 @@ import ( func TestApi(t *testing.T) { base := "http://localhost:8080" - newCustomerBody := m.NewCustomerPost{ - NKodePolicy: m.NewDefaultNKodePolicy(), + newCustomerBody := core.NewCustomerPost{ + NKodePolicy: core.NewDefaultNKodePolicy(), } - kp := m.KeypadDimension{ + kp := core.KeypadDimension{ AttrsPerKey: 14, NumbOfKeys: 10, } - var customerResp m.CreateNewCustomerResp - testApiPost(t, base+api.CreateNewCustomer, newCustomerBody, &customerResp) + var customerResp core.CreateNewCustomerResp + testApiPost(t, base+core.CreateNewCustomer, newCustomerBody, &customerResp) - username := m.Username("test_username") - signupInterfaceBody := m.GenerateSignupInterfacePost{ + username := core.Username("test_username") + signupInterfaceBody := core.GenerateSignupInterfacePost{ CustomerId: customerResp.CustomerId, AttrsPerKey: kp.AttrsPerKey, NumbOfKeys: kp.NumbOfKeys, Username: username, } - var signupInterfaceResp m.GenerateSignupInterfaceResp - testApiPost(t, base+api.GenerateSignupInterface, signupInterfaceBody, &signupInterfaceResp) + var signupInterfaceResp core.GenerateSignupInterfaceResp + testApiPost(t, base+core.GenerateSignupInterface, signupInterfaceBody, &signupInterfaceResp) assert.Len(t, signupInterfaceResp.SvgInterface, kp.TotalAttrs()) passcodeLen := 4 setInterface := signupInterfaceResp.UserIdxInterface userPasscode := setInterface[:passcodeLen] - kp_set := m.KeypadDimension{NumbOfKeys: kp.NumbOfKeys, AttrsPerKey: kp.NumbOfKeys} - setKeySelection, err := m.SelectKeyByAttrIdx(setInterface, userPasscode, kp_set) + kp_set := core.KeypadDimension{NumbOfKeys: kp.NumbOfKeys, AttrsPerKey: kp.NumbOfKeys} + setKeySelection, err := core.SelectKeyByAttrIdx(setInterface, userPasscode, kp_set) assert.NoError(t, err) - setNKodeBody := m.SetNKodePost{ + setNKodeBody := core.SetNKodePost{ CustomerId: customerResp.CustomerId, SessionId: signupInterfaceResp.SessionId, KeySelection: setKeySelection, } - var setNKodeResp m.SetNKodeResp - testApiPost(t, base+api.SetNKode, setNKodeBody, &setNKodeResp) + var setNKodeResp core.SetNKodeResp + testApiPost(t, base+core.SetNKode, setNKodeBody, &setNKodeResp) confirmInterface := setNKodeResp.UserInterface - confirmKeySelection, err := m.SelectKeyByAttrIdx(confirmInterface, userPasscode, kp_set) + confirmKeySelection, err := core.SelectKeyByAttrIdx(confirmInterface, userPasscode, kp_set) assert.NoError(t, err) - confirmNKodeBody := m.ConfirmNKodePost{ + confirmNKodeBody := core.ConfirmNKodePost{ CustomerId: customerResp.CustomerId, KeySelection: confirmKeySelection, SessionId: signupInterfaceResp.SessionId, } - testApiPost(t, base+api.ConfirmNKode, confirmNKodeBody, nil) + testApiPost(t, base+core.ConfirmNKode, confirmNKodeBody, nil) - loginInterfaceBody := m.GetLoginInterfacePost{ + loginInterfaceBody := core.GetLoginInterfacePost{ CustomerId: customerResp.CustomerId, Username: username, } - var loginInterfaceResp m.GetLoginInterfaceResp - testApiPost(t, base+api.GetLoginInterface, loginInterfaceBody, &loginInterfaceResp) - - loginKeySelection, err := m.SelectKeyByAttrIdx(loginInterfaceResp.UserIdxInterface, userPasscode, kp) + var loginInterfaceResp core.GetLoginInterfaceResp + testApiPost(t, base+core.GetLoginInterface, loginInterfaceBody, &loginInterfaceResp) + assert.Equal(t, loginInterfaceResp.AttrsPerKey, kp.AttrsPerKey) + assert.Equal(t, loginInterfaceResp.NumbOfKeys, kp.NumbOfKeys) + loginKeySelection, err := core.SelectKeyByAttrIdx(loginInterfaceResp.UserIdxInterface, userPasscode, kp) assert.NoError(t, err) - loginBody := m.LoginPost{ + loginBody := core.LoginPost{ CustomerId: customerResp.CustomerId, Username: username, KeySelection: loginKeySelection, } - testApiPost(t, base+api.Login, loginBody, nil) + testApiPost(t, base+core.Login, loginBody, nil) - renewBody := m.RenewAttributesPost{CustomerId: customerResp.CustomerId} - testApiPost(t, base+api.RenewAttributes, renewBody, nil) + renewBody := core.RenewAttributesPost{CustomerId: customerResp.CustomerId} + testApiPost(t, base+core.RenewAttributes, renewBody, nil) - loginKeySelection, err = m.SelectKeyByAttrIdx(loginInterfaceResp.UserIdxInterface, userPasscode, kp) + loginKeySelection, err = core.SelectKeyByAttrIdx(loginInterfaceResp.UserIdxInterface, userPasscode, kp) assert.NoError(t, err) - loginBody = m.LoginPost{ + loginBody = core.LoginPost{ CustomerId: customerResp.CustomerId, Username: username, KeySelection: loginKeySelection, } - testApiPost(t, base+api.Login, loginBody, nil) - - var randomSvgInterfaceResp m.RandomSvgInterfaceResp - testApiGet(t, base+api.RandomSvgInterface, &randomSvgInterfaceResp) - assert.Equal(t, m.KeypadMax.TotalAttrs(), len(randomSvgInterfaceResp.Svgs)) + testApiPost(t, base+core.Login, loginBody, nil) + var randomSvgInterfaceResp core.RandomSvgInterfaceResp + testApiGet(t, base+core.RandomSvgInterface, &randomSvgInterfaceResp) + assert.Equal(t, core.KeypadMax.TotalAttrs(), len(randomSvgInterfaceResp.Svgs)) } func Unmarshal(t *testing.T, resp *http.Response, data any) { diff --git a/sql-driver/sql_driver.go b/sql-driver/sql_driver.go deleted file mode 100644 index 0af4846..0000000 --- a/sql-driver/sql_driver.go +++ /dev/null @@ -1,71 +0,0 @@ -package sql_driver - -import ( - "database/sql" - _ "github.com/mattn/go-sqlite3" // Import the SQLite3 driver - "log" -) - -func MakeTables(dbPath string) { - db, err := sql.Open("sqlite3", dbPath) - if err != nil { - log.Fatal(err) - } - defer db.Close() - createTable := ` -PRAGMA journal_mode=WAL; -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, - svg_id_interface BLOB NOT NULL, - - - FOREIGN KEY (customer_id) REFERENCES customers(id), - UNIQUE(customer_id, username) -); - -CREATE TABLE IF NOT EXISTS svg_icon ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - svg TEXT NOT NULL -); -` - _, err = db.Exec(createTable) - if err != nil { - log.Fatal(err) - } -} diff --git a/sqlite-init/sqlite_init.go b/sqlite-init/sqlite_init.go index f8d5435..40e74d4 100644 --- a/sqlite-init/sqlite_init.go +++ b/sqlite-init/sqlite_init.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" _ "github.com/mattn/go-sqlite3" // Import the SQLite3 driver - "go-nkode/sql-driver" "io/ioutil" "log" "path/filepath" @@ -28,7 +27,7 @@ func main() { dbPaths := []string{"test.db", "nkode.db"} outputStr := MakeSvgFiles() for _, path := range dbPaths { - sql_driver.MakeTables(path) + MakeTables(path) SaveToSqlite(path, outputStr) } } @@ -136,3 +135,67 @@ func LoadJson(filename string) (*Root, error) { return &root, nil } + +func MakeTables(dbPath string) { + db, err := sql.Open("sqlite3", dbPath) + if err != nil { + log.Fatal(err) + } + defer db.Close() + createTable := ` +PRAGMA journal_mode=WAL; +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, + svg_id_interface BLOB NOT NULL, + + + FOREIGN KEY (customer_id) REFERENCES customers(id), + UNIQUE(customer_id, username) +); + +CREATE TABLE IF NOT EXISTS svg_icon ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + svg TEXT NOT NULL +); +` + _, err = db.Exec(createTable) + if err != nil { + log.Fatal(err) + } +}