diff --git a/core/model/nkode_policy.go b/core/model/nkode_policy.go index 9e03b0d..35b168a 100644 --- a/core/model/nkode_policy.go +++ b/core/model/nkode_policy.go @@ -1,5 +1,9 @@ package m +import ( + "errors" +) + type NKodePolicy struct { MaxNkodeLen int `json:"max_nkode_len"` MinNkodeLen int `json:"min_nkode_len"` @@ -19,3 +23,13 @@ func NewDefaultNKodePolicy() NKodePolicy { Expiration: -1, } } + +var InvalidNKodeLen = errors.New("invalid nkode length") + +func (p *NKodePolicy) ValidLength(nkodeLen int) error { + + if nkodeLen < p.MinNkodeLen || nkodeLen > p.MaxNkodeLen { + return InvalidNKodeLen + } + return nil +} diff --git a/core/model/type.go b/core/model/type.go index fb45d05..56a9c3a 100644 --- a/core/model/type.go +++ b/core/model/type.go @@ -60,6 +60,8 @@ type GetLoginInterfaceResp struct { type KeySelection []int type CustomerId uuid.UUID type SessionId uuid.UUID +type UserId uuid.UUID + type Username string type IdxInterface []int diff --git a/core/nkode/common.go b/core/nkode/common.go new file mode 100644 index 0000000..99f58e5 --- /dev/null +++ b/core/nkode/common.go @@ -0,0 +1,89 @@ +package nkode + +import ( + "errors" + "github.com/google/uuid" + m "go-nkode/core/model" + py "go-nkode/py-builtin" +) + +var KeyIndexOutOfRange error = errors.New("one or more keys is out of range") + +func ValidKeyEntry(user User, customer 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.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 := 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.AttrVals(user.Kp) + if err != nil { + panic(err) + } + err = user.UserKeys.ValidPassword(user.EncipheredPasscode.Code, presumedAttrIdxVals, attrVals) + if err != nil { + return nil, 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) + 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: m.UserId(uuid.New()), + Username: username, + EncipheredPasscode: *encipheredNKode, + UserKeys: *newKeys, + Interface: ui, + Kp: kp, + CustomerId: customer.Id, + } + return &newUser, nil +} diff --git a/core/nkode/customer.go b/core/nkode/customer.go index 6bcf38f..6dbc5dc 100644 --- a/core/nkode/customer.go +++ b/core/nkode/customer.go @@ -11,10 +11,9 @@ import ( ) type Customer struct { - CustomerId m.CustomerId + Id m.CustomerId NKodePolicy m.NKodePolicy Attributes CustomerAttributes - Users map[m.Username]User } func NewCustomer(nkodePolicy m.NKodePolicy) (*Customer, error) { @@ -23,106 +22,106 @@ func NewCustomer(nkodePolicy m.NKodePolicy) (*Customer, error) { return nil, err } customer := Customer{ - CustomerId: m.CustomerId(uuid.New()), + Id: m.CustomerId(uuid.New()), NKodePolicy: nkodePolicy, Attributes: *customerAttrs, - Users: make(map[m.Username]User), } 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.CustomerId)) - } - 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{ - Username: username, - EncipheredPasscode: *encipheredNKode, - UserKeys: *newKeys, - Interface: ui, - Kp: kp, - } - c.Users[username] = newUser - return 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.CustomerId)) - } +//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 +//} - 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) 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) @@ -163,59 +162,39 @@ func (c *Customer) IsValidNKode(kp m.KeypadDimension, passcodeAttrIdx []int) err return nil } -func (c *Customer) GetLoginInterface(username m.Username) ([]int, error) { - user, exists := c.Users[username] - if !exists { - return nil, errors.New(fmt.Sprintf("can't get login interface for non-existant user %s in customer %s", username, c.CustomerId)) - } - err := user.Interface.PartialInterfaceShuffle() - - if err != nil { - return nil, err - } - c.Users[username] = user - return user.Interface.IdxInterface, nil -} - -func (c *Customer) RenewKeys() error { +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 { - return nil + panic(err) } allSetVals, err := c.Attributes.AttrVals(m.KeypadMax) if err != nil { - return nil + panic(err) } copy(oldAttrs, allAttrVals) copy(oldSets, allSetVals) err = c.Attributes.Renew() if err != nil { - return nil + panic(err) } allAttrVals, err = c.Attributes.AttrVals(m.KeypadMax) if err != nil { - return nil + panic(err) } - allSetVals, err = c.Attributes.AttrVals(m.KeypadMax) + allSetVals, err = c.Attributes.SetVals(m.KeypadMax) if err != nil { - return nil + panic(err) } attrsXor, err := util.XorLists(oldAttrs, allAttrVals) if err != nil { - return nil + panic(err) } setXor, err := util.XorLists(oldSets, allSetVals) if err != nil { - return nil + panic(err) } - for _, user := range c.Users { - err = user.RenewKeys(setXor, attrsXor) - if err != nil { - return nil - } - } - return nil + return setXor, attrsXor } diff --git a/core/nkode/customer_attributes.go b/core/nkode/customer_attributes.go index daf4086..a0a44d5 100644 --- a/core/nkode/customer_attributes.go +++ b/core/nkode/customer_attributes.go @@ -7,8 +7,6 @@ import ( "go-nkode/util" ) -var NotDispersableError = errors.New("number of keys must be less than the number of attributes per key to be dispersion resistant") - type CustomerAttributes struct { attrVals []uint64 setVals []uint64 diff --git a/core/nkode/customer_test.go b/core/nkode/customer_test.go index 69c75fb..8c4d0d1 100644 --- a/core/nkode/customer_test.go +++ b/core/nkode/customer_test.go @@ -27,13 +27,13 @@ func testCustomerValidKeyEntry(t *testing.T) { assert.NoError(t, err) username := m.Username("testing123") passcodeIdx := []int{0, 1, 2, 3} - err = customer.AddNewUser(username, passcodeIdx, *newUserInterface, kp) + user, err := NewUser(*customer, username, passcodeIdx, *newUserInterface, kp) assert.NoError(t, err) - userLoginInterface, err := customer.GetLoginInterface(username) + userLoginInterface, err := user.GetLoginInterface() assert.NoError(t, err) selectedKeys, err := SelectKeyByAttrIdx(userLoginInterface, passcodeIdx, kp) assert.NoError(t, err) - validatedPasscode, err := customer.ValidKeyEntry(username, selectedKeys) + validatedPasscode, err := ValidKeyEntry(*user, *customer, selectedKeys) assert.NoError(t, err) assert.Equal(t, len(validatedPasscode), len(passcodeIdx)) for idx := range validatedPasscode { @@ -50,8 +50,8 @@ func testCustomerIsValidNKode(t *testing.T) { assert.NoError(t, err) username := m.Username("testing123") passcodeIdx := []int{0, 1, 2, 3} - err = customer.AddNewUser(username, passcodeIdx, *newUserInterface, kp) + user, err := NewUser(*customer, username, passcodeIdx, *newUserInterface, kp) assert.NoError(t, err) - err = customer.IsValidNKode(kp, passcodeIdx) + err = customer.IsValidNKode(user.Kp, passcodeIdx) assert.NoError(t, err) } diff --git a/core/nkode/db_accessor.go b/core/nkode/db_accessor.go new file mode 100644 index 0000000..220152b --- /dev/null +++ b/core/nkode/db_accessor.go @@ -0,0 +1,14 @@ +package nkode + +import ( + "go-nkode/core/model" +) + +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 + Renew(m.CustomerId) error +} diff --git a/core/nkode/in_memory_db.go b/core/nkode/in_memory_db.go new file mode 100644 index 0000000..1eae1ba --- /dev/null +++ b/core/nkode/in_memory_db.go @@ -0,0 +1,100 @@ +package nkode + +import ( + "errors" + "fmt" + m "go-nkode/core/model" +) + +type InMemoryDb struct { + Customers map[m.CustomerId]Customer + Users map[m.UserId]User + userIdMap map[string]m.UserId +} + +func NewInMemoryDb() InMemoryDb { + return InMemoryDb{ + Customers: make(map[m.CustomerId]Customer), + Users: make(map[m.UserId]User), + userIdMap: make(map[string]m.UserId), + } +} + +func (db *InMemoryDb) GetCustomer(id m.CustomerId) (*Customer, error) { + customer, exists := db.Customers[id] + if !exists { + return nil, errors.New(fmt.Sprintf("customer %s dne", customer.Id)) + } + return &customer, nil +} + +func (db *InMemoryDb) GetUser(username m.Username, customerId m.CustomerId) (*User, error) { + key := userIdKey(customerId, username) + userId, exists := db.userIdMap[key] + if !exists { + return nil, errors.New(fmt.Sprintf("customer %s with username %s dne", customerId, username)) + } + user, exists := db.Users[userId] + if !exists { + panic(fmt.Sprintf("userId %s with customerId %s and username %s with no user", userId, customerId, username)) + } + return &user, nil +} + +func (db *InMemoryDb) WriteNewCustomer(customer Customer) error { + _, exists := db.Customers[customer.Id] + + if exists { + return errors.New(fmt.Sprintf("can write customer %s; already exists", customer.Id)) + } + db.Customers[customer.Id] = customer + return nil +} + +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)) + } + userExists, _ := db.GetUser(user.Username, user.CustomerId) + + if userExists != nil { + return errors.New(fmt.Sprintf("can't write new user %s, alread exists", user.Username)) + } + key := userIdKey(user.CustomerId, user.Username) + db.userIdMap[key] = user.Id + db.Users[user.Id] = user + return nil +} + +func (db *InMemoryDb) UpdateUserInterface(userId m.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 + return nil +} + +func (db *InMemoryDb) Renew(id m.CustomerId) error { + customer, exists := db.Customers[id] + if !exists { + return errors.New(fmt.Sprintf("customer %s does not exist", id)) + } + setXor, attrsXor := customer.RenewKeys() + var err error + for _, user := range db.Users { + if user.CustomerId == id { + err = user.RenewKeys(setXor[:user.Kp.AttrsPerKey], attrsXor[:user.Kp.TotalAttrs()]) + if err != nil { + panic(err) + } + } + } + return nil +} + +func userIdKey(customerId m.CustomerId, username m.Username) string { + key := fmt.Sprintf("%s:%s", customerId, username) + return key +} diff --git a/core/nkode/nkode_api.go b/core/nkode/nkode_api.go new file mode 100644 index 0000000..7b6a253 --- /dev/null +++ b/core/nkode/nkode_api.go @@ -0,0 +1,131 @@ +package nkode + +import ( + "errors" + "fmt" + m "go-nkode/core/model" +) + +type NKodeAPI struct { + Db DbAccessor + SignupSessions map[m.SessionId]UserSignSession +} + +func NewNKodeAPI(db DbAccessor) NKodeAPI { + return NKodeAPI{ + Db: db, + SignupSessions: make(map[m.SessionId]UserSignSession), + } +} + +func (n *NKodeAPI) CreateNewCustomer(nkodePolicy m.NKodePolicy) (*m.CustomerId, error) { + newCustomer, err := NewCustomer(nkodePolicy) + if err != nil { + return nil, err + } + err = n.Db.WriteNewCustomer(*newCustomer) + + if err != nil { + return nil, err + } + return &newCustomer.Id, nil +} + +func (n *NKodeAPI) GenerateSignupInterface(customerId m.CustomerId, kp m.KeypadDimension) (*m.GenerateSignupInterfaceResp, error) { + signupSession, err := NewSignupSession(kp, customerId) + if err != nil { + return nil, err + } + n.SignupSessions[signupSession.Id] = *signupSession + resp := m.GenerateSignupInterfaceResp{ + UserInterface: signupSession.SetIdxInterface, + SessionId: signupSession.Id, + } + return &resp, nil +} + +func (n *NKodeAPI) SetNKode(username m.Username, customerId m.CustomerId, sessionId m.SessionId, keySelection m.KeySelection) (m.IdxInterface, error) { + _, err := n.Db.GetCustomer(customerId) + + if err != nil { + return nil, err + } + session, exists := n.SignupSessions[sessionId] + if !exists { + return nil, errors.New(fmt.Sprintf("session id does not exist %s", sessionId)) + } + confirmInterface, err := session.SetUserNKode(username, keySelection) + if err != nil { + return nil, err + } + n.SignupSessions[sessionId] = session + return confirmInterface, nil +} + +func (n *NKodeAPI) ConfirmNKode(customerId m.CustomerId, sessionId m.SessionId, keySelection m.KeySelection) error { + session, exists := n.SignupSessions[sessionId] + if !exists { + return errors.New(fmt.Sprintf("session id does not exist %s", sessionId)) + } + customer, err := n.Db.GetCustomer(customerId) + if err != nil { + return err + } + passcode, err := session.DeducePasscode(keySelection) + if err != nil { + return err + } + err = customer.IsValidNKode(session.Kp, passcode) + if err != nil { + return err + } + user, err := NewUser(*customer, session.Username, passcode, session.LoginUserInterface, session.Kp) + if err != nil { + return err + } + delete(n.SignupSessions, session.Id) + err = n.Db.WriteNewUser(*user) + return err +} + +func (n *NKodeAPI) GetLoginInterface(username m.Username, customerId m.CustomerId) (m.IdxInterface, error) { + user, err := n.Db.GetUser(username, customerId) + if err != nil { + return nil, err + } + err = user.Interface.PartialInterfaceShuffle() + if err != nil { + return nil, err + } + err = n.Db.UpdateUserInterface(user.Id, user.Interface) + if err != nil { + return nil, err + } + return user.Interface.IdxInterface, nil +} + +func (n *NKodeAPI) Login(customerId m.CustomerId, username m.Username, keySelection m.KeySelection) error { + customer, err := n.Db.GetCustomer(customerId) + if err != nil { + return err + } + user, err := n.Db.GetUser(username, customerId) + if err != nil { + return errors.New(fmt.Sprintf("user dne %s", username)) + } + passcode, err := ValidKeyEntry(*user, *customer, keySelection) + if err != nil { + return err + } + if user.Renew { + err = user.RefreshPasscode(passcode, customer.Attributes) + if err != nil { + return err + } + } + return nil +} + +func (n *NKodeAPI) RenewAttributes(customerId m.CustomerId) error { + return n.Db.Renew(customerId) +} diff --git a/core/nkode/nkode_in_memory_test.go b/core/nkode/nkode_api_test.go similarity index 97% rename from core/nkode/nkode_in_memory_test.go rename to core/nkode/nkode_api_test.go index 377a19c..aa72782 100644 --- a/core/nkode/nkode_in_memory_test.go +++ b/core/nkode/nkode_api_test.go @@ -8,11 +8,12 @@ import ( func TestNKodeAPI(t *testing.T) { 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 := NewNKodeInMemory() + nkodeApi := NewNKodeAPI(&db) customerId, err := nkodeApi.CreateNewCustomer(nkodePolicy) assert.NoError(t, err) signupResponse, err := nkodeApi.GenerateSignupInterface(*customerId, keypadSize) diff --git a/core/nkode/nkode_in_memory.go b/core/nkode/nkode_in_memory.go deleted file mode 100644 index c5a2ab6..0000000 --- a/core/nkode/nkode_in_memory.go +++ /dev/null @@ -1,143 +0,0 @@ -package nkode - -import ( - "errors" - "fmt" - m "go-nkode/core/model" -) - -type NKodeInMemory struct { - Customers map[m.CustomerId]Customer - SignupSessions map[m.SessionId]UserSignSession -} - -func NewNKodeInMemory() NKodeInMemory { - return NKodeInMemory{ - Customers: make(map[m.CustomerId]Customer), - SignupSessions: make(map[m.SessionId]UserSignSession), - } -} - -func (n *NKodeInMemory) CreateNewCustomer(nkodePolicy m.NKodePolicy) (*m.CustomerId, error) { - newCustomer, err := NewCustomer(nkodePolicy) - if err != nil { - return nil, err - } - n.Customers[newCustomer.CustomerId] = *newCustomer - return &newCustomer.CustomerId, nil -} - -func (n *NKodeInMemory) GenerateSignupInterface(customerId m.CustomerId, kp m.KeypadDimension) (*m.GenerateSignupInterfaceResp, error) { - customer, exists := n.Customers[customerId] - if !exists { - return nil, errors.New(fmt.Sprintf("customer doesnt exists: %s", customerId)) - } - - signupSession, err := NewSignupSession(kp, customer.CustomerId) - if err != nil { - return nil, err - } - n.SignupSessions[signupSession.SessionId] = *signupSession - resp := m.GenerateSignupInterfaceResp{ - UserInterface: signupSession.SetIdxInterface, - SessionId: signupSession.SessionId, - } - return &resp, nil -} - -func (n *NKodeInMemory) SetNKode(username m.Username, customerId m.CustomerId, sessionId m.SessionId, keySelection m.KeySelection) (m.IdxInterface, error) { - _, exists := n.Customers[customerId] - if !exists { - return nil, errors.New(fmt.Sprintf("set nkode customer id does not exist %s", customerId)) - } - - session, exists := n.SignupSessions[sessionId] - if !exists { - return nil, errors.New(fmt.Sprintf("session id does not exist %s", sessionId)) - } - confirmInterface, err := session.SetUserNKode(username, keySelection) - if err != nil { - return nil, err - } - n.SignupSessions[sessionId] = session - return confirmInterface, nil -} - -func (n *NKodeInMemory) ConfirmNKode(customerId m.CustomerId, sessionId m.SessionId, keySelection m.KeySelection) error { - session, exists := n.SignupSessions[sessionId] - if !exists { - return errors.New(fmt.Sprintf("session id does not exist %s", sessionId)) - } - customer, exists := n.Customers[customerId] - passcode, err := session.DeducePasscode(keySelection) - if err != nil { - return err - } - err = customer.IsValidNKode(session.Kp, passcode) - if err != nil { - return err - } - err = customer.AddNewUser(session.Username, passcode, session.LoginUserInterface, session.Kp) - if err != nil { - return err - } - delete(n.SignupSessions, session.SessionId) - n.Customers[customerId] = customer - return nil -} - -func (n *NKodeInMemory) GetLoginInterface(username m.Username, customerId m.CustomerId) (m.IdxInterface, error) { - err := n.customerUserExists(username, customerId) - if err != nil { - return nil, err - } - user := n.Customers[customerId].Users[username] - err = user.Interface.PartialInterfaceShuffle() - if err != nil { - return nil, err - } - n.Customers[customerId].Users[username] = user - return user.Interface.IdxInterface, nil -} - -func (n *NKodeInMemory) Login(customerId m.CustomerId, username m.Username, keySelection m.KeySelection) error { - customer, exists := n.Customers[customerId] - if !exists { - return errors.New(fmt.Sprintf("customer %s does not exist", customerId)) - } - user, exists := customer.Users[username] - if !exists { - return errors.New(fmt.Sprintf("user dne %s", username)) - } - passcode, err := customer.ValidKeyEntry(username, keySelection) - if err != nil { - return err - } - if user.Renew { - err = user.RefreshPasscode(passcode, customer.Attributes) - if err != nil { - return err - } - } - return nil -} - -func (n *NKodeInMemory) customerUserExists(username m.Username, customerId m.CustomerId) error { - customer, exists := n.Customers[customerId] - if !exists { - return errors.New(fmt.Sprintf("customer %s does not exist", customerId)) - } - _, exists = customer.Users[username] - if !exists { - return errors.New(fmt.Sprintf("user dne %s", username)) - } - return nil -} - -func (n *NKodeInMemory) RenewAttributes(customerId m.CustomerId) error { - customer, exists := n.Customers[customerId] - if !exists { - return errors.New(fmt.Sprintf("customer %s does not exist", customerId)) - } - return customer.RenewKeys() -} diff --git a/core/nkode/sqlite_db.go b/core/nkode/sqlite_db.go new file mode 100644 index 0000000..324c377 --- /dev/null +++ b/core/nkode/sqlite_db.go @@ -0,0 +1 @@ +package nkode diff --git a/core/nkode/user.go b/core/nkode/user.go index 6b7a15f..8138297 100644 --- a/core/nkode/user.go +++ b/core/nkode/user.go @@ -6,6 +6,8 @@ import ( ) type User struct { + Id m.UserId + CustomerId m.CustomerId Username m.Username EncipheredPasscode m.EncipheredNKode Kp m.KeypadDimension @@ -23,10 +25,13 @@ func (u *User) RenewKeys(setXor []uint64, attrXor []uint64) error { var err error u.UserKeys.SetKey, err = util.XorLists(setXor, u.UserKeys.SetKey) if err != nil { - return err + panic(err) } u.UserKeys.AlphaKey, err = util.XorLists(attrXor, u.UserKeys.AlphaKey) - return err + if err != nil { + panic(err) + } + return nil } func (u *User) RefreshPasscode(passcodeAttrIdx []int, customerAttributes CustomerAttributes) error { @@ -46,3 +51,12 @@ func (u *User) RefreshPasscode(passcodeAttrIdx []int, customerAttributes Custome 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/user_cipher_keys.go b/core/nkode/user_cipher_keys.go index 0702dda..1e0c5ea 100644 --- a/core/nkode/user_cipher_keys.go +++ b/core/nkode/user_cipher_keys.go @@ -3,12 +3,15 @@ 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 @@ -37,6 +40,7 @@ func NewUserCipherKeys(kp *m.KeypadDimension, setVals []uint64, maxNKodeLen int) maskKey, _ := util.GenerateRandomNonRepeatingUint64(maxNKodeLen) salt, _ := util.RandomBytes(10) userCipherKeys := UserCipherKeys{ + Id: uuid.New(), AlphaKey: alphakey, PassKey: passKey, MaskKey: maskKey, diff --git a/core/nkode/user_interface.go b/core/nkode/user_interface.go index 2d4d604..2470889 100644 --- a/core/nkode/user_interface.go +++ b/core/nkode/user_interface.go @@ -10,14 +10,14 @@ import ( type UserInterface struct { IdxInterface m.IdxInterface - kp *m.KeypadDimension + Kp *m.KeypadDimension } func NewUserInterface(kp *m.KeypadDimension) (*UserInterface, error) { idxInterface := util.IdentityArray(kp.TotalAttrs()) userInterface := UserInterface{ IdxInterface: idxInterface, - kp: kp, + Kp: kp, } err := userInterface.RandomShuffle() if err != nil { @@ -55,7 +55,7 @@ func (u *UserInterface) RandomShuffle() error { } func (u *UserInterface) InterfaceMatrix() ([][]int, error) { - return util.ListToMatrix(u.IdxInterface, u.kp.AttrsPerKey) + return util.ListToMatrix(u.IdxInterface, u.Kp.AttrsPerKey) } func (u *UserInterface) SetViewMatrix() ([][]int, error) { @@ -67,7 +67,7 @@ func (u *UserInterface) SetViewMatrix() ([][]int, error) { } func (u *UserInterface) DisperseInterface() error { - if !u.kp.IsDispersable() { + if !u.Kp.IsDispersable() { panic("interface is not dispersable") } @@ -84,7 +84,7 @@ func (u *UserInterface) DisperseInterface() error { } func (u *UserInterface) shuffleKeys() error { - userInterfaceMatrix, err := util.ListToMatrix(u.IdxInterface, u.kp.AttrsPerKey) + userInterfaceMatrix, err := util.ListToMatrix(u.IdxInterface, u.Kp.AttrsPerKey) if err != nil { return err } @@ -144,11 +144,11 @@ func (u *UserInterface) PartialInterfaceShuffle() error { if err != nil { return err } - numbOfSelectedSets := u.kp.AttrsPerKey / 2 - if u.kp.AttrsPerKey&1 == 1 { + 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) + setIdxs, err := util.RandomPermutation(u.Kp.AttrsPerKey) if err != nil { return err } @@ -158,7 +158,7 @@ func (u *UserInterface) PartialInterfaceShuffle() error { if err != nil { return err } - interfaceBySet := make([][]int, u.kp.AttrsPerKey) + interfaceBySet := make([][]int, u.Kp.AttrsPerKey) for idx, attrs := range keypadSetView { if selectedSets.Contains(idx) { err = util.FisherYatesShuffle[int](&attrs) @@ -177,12 +177,12 @@ func (u *UserInterface) PartialInterfaceShuffle() error { } 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 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)) + 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 { diff --git a/core/nkode/user_signup_session.go b/core/nkode/user_signup_session.go index 2f33083..8d96870 100644 --- a/core/nkode/user_signup_session.go +++ b/core/nkode/user_signup_session.go @@ -11,7 +11,7 @@ import ( ) type UserSignSession struct { - SessionId m.SessionId + Id m.SessionId CustomerId m.CustomerId LoginUserInterface UserInterface Kp m.KeypadDimension @@ -19,6 +19,7 @@ type UserSignSession struct { ConfirmIdxInterface m.IdxInterface SetKeySelection m.KeySelection Username m.Username + Expire int } func NewSignupSession(kp m.KeypadDimension, customerId m.CustomerId) (*UserSignSession, error) { @@ -31,7 +32,7 @@ func NewSignupSession(kp m.KeypadDimension, customerId m.CustomerId) (*UserSignS return nil, err } session := UserSignSession{ - SessionId: m.SessionId(uuid.New()), + Id: m.SessionId(uuid.New()), CustomerId: customerId, LoginUserInterface: *loginInterface, SetIdxInterface: signupInterface.IdxInterface, @@ -108,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 := UserInterface{IdxInterface: s.SetIdxInterface, Kp: &setKp} err := setInterface.DisperseInterface() if err != nil { return nil, err @@ -159,7 +160,7 @@ func signupInterface(baseUserInterface UserInterface, kp m.KeypadDimension) (*Us } signupUserInterface := UserInterface{ IdxInterface: util.MatrixToList(attrSetView), - kp: &m.KeypadDimension{ + Kp: &m.KeypadDimension{ AttrsPerKey: numbOfKeys, NumbOfKeys: numbOfKeys, }, diff --git a/main.go b/main.go index a8f53c2..21ef794 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,8 @@ import ( ) func main() { - nkodeApi := nkode.NewNKodeInMemory() + db := nkode.NewInMemoryDb() + nkodeApi := nkode.NewNKodeAPI(&db) handler := api.NKodeHandler{Api: &nkodeApi} mux := http.NewServeMux() mux.Handle(api.CreateNewCustomer, &handler) diff --git a/core/sql-driver/sql_driver.go b/sql-driver/sql_driver.go similarity index 79% rename from core/sql-driver/sql_driver.go rename to sql-driver/sql_driver.go index cef2923..441c094 100644 --- a/core/sql-driver/sql_driver.go +++ b/sql-driver/sql_driver.go @@ -3,6 +3,7 @@ package sql_driver import ( "database/sql" "fmt" + "github.com/google/uuid" _ "github.com/mattn/go-sqlite3" // Import the SQLite3 driver "log" ) @@ -17,7 +18,7 @@ func InitTables() { // Create a table createTableSQL := ` CREATE TABLE IF NOT EXISTS users ( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + id TEXT NOT NULL PRIMARY KEY, name TEXT, age INTEGER ); @@ -28,13 +29,13 @@ func InitTables() { } // Insert data into the table - insertUserSQL := `INSERT INTO users (name, age) VALUES (?, ?)` - _, err = db.Exec(insertUserSQL, "Alice", 30) + insertUserSQL := `INSERT INTO users (id, name, age) VALUES (?, ?, ?)` + _, err = db.Exec(insertUserSQL, uuid.New(), "Alice", 30) if err != nil { log.Fatalf("Error inserting data: %s", err) } - _, err = db.Exec(insertUserSQL, "Bob", 25) + _, err = db.Exec(insertUserSQL, uuid.New(), "Bob", 25) if err != nil { log.Fatalf("Error inserting data: %s", err) } @@ -48,14 +49,14 @@ func InitTables() { defer rows.Close() for rows.Next() { - var id int + var id string var name string var age int err = rows.Scan(&id, &name, &age) if err != nil { log.Fatalf("Error scanning data: %s", err) } - fmt.Printf("User: ID=%d, Name=%s, Age=%d\n", id, name, age) + fmt.Printf("User: ID=%s, Name=%s, Age=%d\n", id, name, age) } // Update data @@ -74,13 +75,13 @@ func InitTables() { defer rows.Close() for rows.Next() { - var id int + var id string var name string var age int err = rows.Scan(&id, &name, &age) if err != nil { log.Fatalf("Error scanning data: %s", err) } - fmt.Printf("User: ID=%d, Name=%s, Age=%d\n", id, name, age) + fmt.Printf("User: ID=%s, Name=%s, Age=%d\n", id, name, age) } } diff --git a/sql-driver/sql_driver_test.go b/sql-driver/sql_driver_test.go new file mode 100644 index 0000000..b3f2fe4 --- /dev/null +++ b/sql-driver/sql_driver_test.go @@ -0,0 +1,7 @@ +package sql_driver + +import "testing" + +func TestInitTables(t *testing.T) { + InitTables() +}