implement api

This commit is contained in:
2024-08-21 08:20:51 -05:00
parent 603936b50c
commit 6a56b2300a
18 changed files with 505 additions and 124 deletions

View File

@@ -1,11 +1,13 @@
package main
package api
import (
"errors"
"fmt"
"github.com/google/uuid"
"go-nkode/hashset"
m "go-nkode/models"
py "go-nkode/py-builtin"
"go-nkode/util"
)
type Customer struct {
@@ -128,16 +130,25 @@ func (c *Customer) IsValidNKode(passcodeAttrIdx []int) error {
if !validIdx {
return errors.New(fmt.Sprintf("One or more idx out of range 0-%d in IsValidNKode", c.Attributes.KeypadSize.TotalAttrs()-1))
}
passcodeSetVals := make([]uint64, nkodeLen)
var err error
for idx := range passcodeSetVals {
passcodeSetVals := make(hashset.Set[uint64])
passcodeAttrVals := make(hashset.Set[uint64])
for idx := 0; idx < nkodeLen; idx++ {
attrVal := c.Attributes.AttrVals[passcodeAttrIdx[idx]]
passcodeSetVals[idx], err = c.Attributes.GetAttrSetVal(attrVal)
setVal, err := c.Attributes.GetAttrSetVal(attrVal)
if err != nil {
return err
}
passcodeSetVals.Add(setVal)
passcodeAttrVals.Add(attrVal)
}
if passcodeSetVals.Size() < c.NKodePolicy.DistinctSets {
return errors.New(fmt.Sprintf("passcode has two few distinct sets min %d, has %d", c.NKodePolicy.DistinctSets, passcodeSetVals.Size()))
}
if passcodeAttrVals.Size() < c.NKodePolicy.DistinctAttributes {
return errors.New(fmt.Sprintf("passcode has two few distinct attributes min %d, has %d", c.NKodePolicy.DistinctAttributes, passcodeAttrVals.Size()))
}
return nil
}
@@ -154,3 +165,30 @@ func (c *Customer) GetLoginInterface(username string) ([]int, error) {
c.Users[username] = user
return user.Interface.IdxInterface, nil
}
func (c *Customer) RenewKeys() error {
oldAttrs := make([]uint64, c.Attributes.KeypadSize.TotalAttrs())
oldSets := make([]uint64, c.Attributes.KeypadSize.AttrsPerKey)
copy(oldAttrs, c.Attributes.AttrVals)
copy(oldSets, c.Attributes.SetVals)
err := c.Attributes.Renew()
if err != nil {
return nil
}
attrsXor, err := util.XorLists(oldAttrs, c.Attributes.AttrVals)
if err != nil {
return nil
}
setXor, err := util.XorLists(oldSets, c.Attributes.SetVals)
if err != nil {
return nil
}
for _, user := range c.Users {
err = user.RenewKeys(setXor, attrsXor)
if err != nil {
return nil
}
}
return nil
}

View File

@@ -1,4 +1,4 @@
package main
package api
import (
"errors"

View File

@@ -1,27 +1,11 @@
package main
package api
import (
"errors"
"fmt"
"github.com/stretchr/testify/assert"
"go-nkode/models"
"go-nkode/util"
"testing"
)
func SelectKeyByAttrIdx(interfaceUser []int, passcodeIdxs []int, keypadSize models.KeypadSize) ([]int, error) {
selectedKeys := make([]int, len(passcodeIdxs))
for idx := range passcodeIdxs {
attrIdx := util.IndexOf[int](interfaceUser, passcodeIdxs[idx])
keyNumb := attrIdx / keypadSize.AttrsPerKey
if keyNumb < 0 || keyNumb >= keypadSize.NumbOfKeys {
return nil, errors.New(fmt.Sprintf("index key number: %d out of range 0-%d", keyNumb, keypadSize.NumbOfKeys-1))
}
selectedKeys[idx] = keyNumb
}
return selectedKeys, nil
}
func TestNewCustomerAttributes(t *testing.T) {
keypad := models.KeypadSize{AttrsPerKey: 10, NumbOfKeys: 5}
_, nil := NewCustomerAttributes(keypad)

140
core/api/nkode_api.go Normal file
View File

@@ -0,0 +1,140 @@
package api
import (
"errors"
"fmt"
"github.com/google/uuid"
"go-nkode/models"
)
type NKodeAPI struct {
Customers map[uuid.UUID]Customer
SignupSessions map[uuid.UUID]UserSignSession
}
func NewNKodeAPI() NKodeAPI {
return NKodeAPI{
Customers: make(map[uuid.UUID]Customer),
SignupSessions: make(map[uuid.UUID]UserSignSession),
}
}
func (n *NKodeAPI) CreateNewCustomer(keypadSize models.KeypadSize, nkodePolicy models.NKodePolicy) (*uuid.UUID, error) {
newCustomer, err := NewCustomer(keypadSize, nkodePolicy)
if err != nil {
return nil, err
}
n.Customers[newCustomer.CustomerId] = *newCustomer
return &newCustomer.CustomerId, nil
}
func (n *NKodeAPI) GenerateSignupInterface(customerId uuid.UUID) (*uuid.UUID, []int, error) {
customer, exists := n.Customers[customerId]
if !exists {
return nil, nil, errors.New(fmt.Sprintf("customer doesnt exists: %s", customerId.String()))
}
signupSession, err := NewSignupSession(customer.Attributes.KeypadSize, customer.CustomerId)
if err != nil {
return nil, nil, err
}
n.SignupSessions[signupSession.SessionId] = *signupSession
return &signupSession.SessionId, signupSession.SetInterface, nil
}
func (n *NKodeAPI) SetNKode(username string, customerId uuid.UUID, keySelection []int, sessionId uuid.UUID) ([]int, error) {
_, exists := n.Customers[customerId]
if !exists {
return nil, errors.New(fmt.Sprintf("set nkode customer id does not exist %s", customerId.String()))
}
session, exists := n.SignupSessions[sessionId]
if !exists {
return nil, errors.New(fmt.Sprintf("session id does not exist %s", sessionId.String()))
}
confirmInterface, err := session.SetUserNKode(username, keySelection)
if err != nil {
return nil, err
}
n.SignupSessions[sessionId] = session
return confirmInterface, nil
}
func (n *NKodeAPI) ConfirmNKode(customerId uuid.UUID, keySelection []int, sessionId uuid.UUID) error {
session, exists := n.SignupSessions[sessionId]
if !exists {
return errors.New(fmt.Sprintf("session id does not exist %s", sessionId.String()))
}
customer, exists := n.Customers[customerId]
passcode, err := session.DeducePasscode(keySelection)
if err != nil {
return err
}
err = customer.IsValidNKode(passcode)
if err != nil {
return err
}
err = customer.AddNewUser(session.Username, passcode, session.LoginInterface)
if err != nil {
return err
}
delete(n.SignupSessions, session.SessionId)
n.Customers[customerId] = customer
return nil
}
func (n *NKodeAPI) GetLoginInterface(username string, customerId uuid.UUID) ([]int, error) {
err := n.customerUserExists(username, customerId)
if err != nil {
return nil, err
}
user := n.Customers[customerId].Users[username]
err = user.Interface.PartialInterfaceShuffle()
if err != nil {
return nil, err
}
n.Customers[customerId].Users[username] = user
return user.Interface.IdxInterface, nil
}
func (n *NKodeAPI) Login(customerId uuid.UUID, username string, keySelection []int) error {
customer, exists := n.Customers[customerId]
if !exists {
return errors.New(fmt.Sprintf("customer %s does not exist", customerId.String()))
}
user, exists := customer.Users[username]
if !exists {
return errors.New(fmt.Sprintf("user dne %s", username))
}
passcode, err := customer.ValidKeyEntry(username, keySelection)
if err != nil {
return err
}
if user.Renew {
err = user.RefreshPasscode(passcode, customer.Attributes)
if err != nil {
return err
}
}
return nil
}
func (n *NKodeAPI) customerUserExists(username string, customerId uuid.UUID) error {
customer, exists := n.Customers[customerId]
if !exists {
return errors.New(fmt.Sprintf("customer %s does not exist", customerId.String()))
}
_, exists = customer.Users[username]
if !exists {
return errors.New(fmt.Sprintf("user dne %s", username))
}
return nil
}
func (n *NKodeAPI) RenewAttributes(customerId uuid.UUID) error {
customer, exists := n.Customers[customerId]
if !exists {
return errors.New(fmt.Sprintf("customer %s does not exist", customerId.String()))
}
return customer.RenewKeys()
}

View File

@@ -0,0 +1,159 @@
package api
import (
"encoding/json"
"fmt"
"github.com/google/uuid"
"go-nkode/models"
"net/http"
)
const (
CreateNewCustomer = "/create-new-customer"
GenerateSignupInterface = "/generate-signup-interface"
SetNKode = "/set-nkode"
ConfirmNKode = "/confirm-nkode"
GetLoginInterface = "/get-login-interface"
Login = "/login"
RenewAttributes = "/renew-attributes"
)
type NKodeHandler struct {
Api NKodeAPI
}
type NewCustomerPost struct {
KeypadSize models.KeypadSize `json:"keypad_size"`
NKodePolicy models.NKodePolicy `json:"nkode_policy"`
}
type GenerateSignupInterfacePost struct {
CustomerId uuid.UUID `json:"customer_id"`
}
type SetNKodePost struct {
Username string `json:"username"`
CustomerId uuid.UUID `json:"customer_id"`
KeySelection []int `json:"key_selection"`
SessionId uuid.UUID `json:"session_id"`
}
type ConfirmNKodePost struct {
CustomerId uuid.UUID `json:"customer_id"`
KeySelection []int `json:"key_selection"`
SessionId uuid.UUID `json:"session_id"`
}
type GetLoginInterfacePost struct {
Username string `json:"username"`
CustomerId uuid.UUID `json:"customer_id"`
}
type LoginPost struct {
CustomerId uuid.UUID `json:"customer_id"`
Username string `json:"username"`
KeySelection []int `json:"key_selection"`
}
type RenewAttributesPost struct {
CustomerId uuid.UUID `json:"customer_id"`
}
func (h *NKodeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case CreateNewCustomer:
h.CreateNewCustomerHandler(w, r)
return
case GenerateSignupInterface:
h.GenerateSignupInterfaceHandler(w, r)
case SetNKode:
if r.Method != http.MethodPost {
methodNotAllowed(w)
return
}
h.SetNKodeHandler(w, r)
case ConfirmNKode:
if r.Method != http.MethodPost {
methodNotAllowed(w)
return
}
case GetLoginInterface:
if r.Method != http.MethodPost {
methodNotAllowed(w)
return
}
case Login:
if r.Method != http.MethodPost {
methodNotAllowed(w)
return
}
case RenewAttributes:
if r.Method != http.MethodPost {
methodNotAllowed(w)
return
}
default:
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("404 not found"))
}
}
func internalServerErrorHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("500 Internal Server Error"))
}
func methodNotAllowed(w http.ResponseWriter) {
w.WriteHeader(http.StatusMethodNotAllowed)
w.Write([]byte("405 method not allowed"))
}
func (h *NKodeHandler) CreateNewCustomerHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
methodNotAllowed(w)
return
}
var customerPost NewCustomerPost
if err := json.NewDecoder(r.Body).Decode(&customerPost); err != nil {
internalServerErrorHandler(w, r)
return
}
customerId, err := h.Api.CreateNewCustomer(customerPost.KeypadSize, customerPost.NKodePolicy)
if err != nil {
internalServerErrorHandler(w, r)
return
}
data := map[string]interface{}{
"customer_id": customerId,
}
jsonBytes, err := json.Marshal(data)
w.WriteHeader(http.StatusOK)
w.Write(jsonBytes)
}
func (h *NKodeHandler) GenerateSignupInterfaceHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
methodNotAllowed(w)
return
}
var signupPost GenerateSignupInterfacePost
if err := json.NewDecoder(r.Body).Decode(&signupPost); err != nil {
internalServerErrorHandler(w, r)
return
}
fmt.Println("Customer Id: ", signupPost.CustomerId)
}
func (h *NKodeHandler) SetNKodeHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
methodNotAllowed(w)
return
}
}

View File

@@ -0,0 +1,48 @@
package api
import (
"github.com/stretchr/testify/assert"
"go-nkode/models"
"testing"
)
func TestNKodeAPI(t *testing.T) {
for idx := 0; idx < 10; idx++ {
username := "test_username"
passcodeLen := 4
nkodePolicy := models.NewDefaultNKodePolicy()
keypadSize := models.KeypadSize{AttrsPerKey: 10, NumbOfKeys: 3}
nkodeApi := NewNKodeAPI()
customerId, err := nkodeApi.CreateNewCustomer(keypadSize, nkodePolicy)
assert.NoError(t, err)
sessionId, setInterface, err := nkodeApi.GenerateSignupInterface(*customerId)
assert.NoError(t, err)
keypadSize = models.KeypadSize{AttrsPerKey: 3, NumbOfKeys: 3}
userPasscode := setInterface[:passcodeLen]
setKeySelect, err := SelectKeyByAttrIdx(setInterface, userPasscode, keypadSize)
assert.NoError(t, err)
confirmInterface, err := nkodeApi.SetNKode(username, *customerId, setKeySelect, *sessionId)
assert.NoError(t, err)
confirmKeySelect, err := SelectKeyByAttrIdx(confirmInterface, userPasscode, keypadSize)
err = nkodeApi.ConfirmNKode(*customerId, confirmKeySelect, *sessionId)
assert.NoError(t, err)
keypadSize = models.KeypadSize{AttrsPerKey: 10, NumbOfKeys: 3}
loginInterface, err := nkodeApi.GetLoginInterface(username, *customerId)
assert.NoError(t, err)
loginKeySelection, err := SelectKeyByAttrIdx(loginInterface, userPasscode, keypadSize)
assert.NoError(t, err)
err = nkodeApi.Login(*customerId, username, loginKeySelection)
assert.NoError(t, err)
err = nkodeApi.RenewAttributes(*customerId)
assert.NoError(t, err)
loginInterface, err = nkodeApi.GetLoginInterface(username, *customerId)
assert.NoError(t, err)
loginKeySelection, err = SelectKeyByAttrIdx(loginInterface, userPasscode, keypadSize)
assert.NoError(t, err)
err = nkodeApi.Login(*customerId, username, loginKeySelection)
assert.NoError(t, err)
}
}

21
core/api/test_helper.go Normal file
View File

@@ -0,0 +1,21 @@
package api
import (
"errors"
"fmt"
"go-nkode/models"
"go-nkode/util"
)
func SelectKeyByAttrIdx(interfaceUser []int, passcodeIdxs []int, keypadSize models.KeypadSize) ([]int, error) {
selectedKeys := make([]int, len(passcodeIdxs))
for idx := range passcodeIdxs {
attrIdx := util.IndexOf[int](interfaceUser, passcodeIdxs[idx])
keyNumb := attrIdx / keypadSize.AttrsPerKey
if keyNumb < 0 || keyNumb >= keypadSize.NumbOfKeys {
return nil, errors.New(fmt.Sprintf("index key number: %d out of range 0-%d", keyNumb, keypadSize.NumbOfKeys-1))
}
selectedKeys[idx] = keyNumb
}
return selectedKeys, nil
}

46
core/api/user.go Normal file
View File

@@ -0,0 +1,46 @@
package api
import (
m "go-nkode/models"
"go-nkode/util"
)
type User struct {
Username string
EncipheredPasscode m.EncipheredNKode
UserKeys UserCipherKeys
Interface UserInterface
Renew bool
}
func (u *User) DecipherMask(setVals []uint64, passcodeLen int) ([]uint64, error) {
return u.UserKeys.DecipherMask(u.EncipheredPasscode.Mask, setVals, passcodeLen)
}
func (u *User) RenewKeys(setXor []uint64, attrXor []uint64) error {
u.Renew = true
var err error
u.UserKeys.SetKey, err = util.XorLists(setXor, u.UserKeys.SetKey)
if err != nil {
return err
}
u.UserKeys.AlphaKey, err = util.XorLists(attrXor, u.UserKeys.AlphaKey)
return err
}
func (u *User) RefreshPasscode(passcodeAttrIdx []int, customerAttributes CustomerAttributes) error {
newKeys, err := NewUserCipherKeys(customerAttributes.KeypadSize, customerAttributes.SetVals, u.UserKeys.MaxNKodeLen)
if err != nil {
return err
}
encipheredPasscode, err := newKeys.EncipherNKode(passcodeAttrIdx, customerAttributes)
if err != nil {
return err
}
u.UserKeys = *newKeys
u.EncipheredPasscode = *encipheredPasscode
u.Renew = false
return nil
}

View File

@@ -1,4 +1,4 @@
package main
package api
import (
"crypto/sha256"
@@ -32,7 +32,7 @@ func NewUserCipherKeys(keypadSize models.KeypadSize, setVals []uint64, maxNKodeL
return nil, err
}
alphakey, _ := util.GenerateRandomNonRepeatingUint64(keypadSize.AttrsPerKey)
alphakey, _ := util.GenerateRandomNonRepeatingUint64(keypadSize.TotalAttrs())
passKey, _ := util.GenerateRandomNonRepeatingUint64(maxNKodeLen)
maskKey, _ := util.GenerateRandomNonRepeatingUint64(maxNKodeLen)
salt, _ := util.RandomBytes(10)

View File

@@ -1,4 +1,4 @@
package main
package api
import (
"errors"

View File

@@ -1,4 +1,4 @@
package main
package api
import (
"errors"
@@ -66,7 +66,7 @@ func (s *UserSignSession) DeducePasscode(confirmKeyEntry []int) ([]int, error) {
return nil, errors.New("signup session username is nil")
}
if len(confirmKeyEntry) == len(s.SetKeyEntry) {
if len(confirmKeyEntry) != len(s.SetKeyEntry) {
return nil, errors.New(fmt.Sprintf("confirm and set key entry lenght mismatch %d != %d", len(confirmKeyEntry), len(s.SetKeyEntry)))
}

View File

@@ -1,4 +1,4 @@
package main
package api
import (
"github.com/stretchr/testify/assert"

22
main.go
View File

@@ -1,9 +1,23 @@
package main
import "fmt"
import (
"fmt"
"go-nkode/core/api"
"log"
"net/http"
)
func main() {
a := 3
b := a / 2
fmt.Println(b)
nkodeApi := api.NewNKodeAPI()
handler := api.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)
fmt.Println("Running on localhost:8080")
log.Fatal(http.ListenAndServe("localhost:8080", mux))
}

14
main_test.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import (
"go-nkode/core/api"
"net/http"
"testing"
)
func TestMain(t *testing.T) {
base := "http://localhost:8080"
resp, err := http.Post(base+api.CreateNewCustomer, "application/json")
}

View File

@@ -1,8 +1,8 @@
package models
type KeypadSize struct {
AttrsPerKey int
NumbOfKeys int
AttrsPerKey int `json:"attrs_per_key"`
NumbOfKeys int `json:"numb_of_keys"`
}
func (kp *KeypadSize) TotalAttrs() int {

View File

@@ -1,12 +1,12 @@
package models
type NKodePolicy struct {
MaxNkodeLen int
MinNkodeLen int
DistinctSets int
DistinctAttributes int
LockOut int
Expiration int // seconds, -1 no expiration
MaxNkodeLen int `json:"max_nkode_len"`
MinNkodeLen int `json:"min_nkode_len"`
DistinctSets int `json:"distinct_sets"`
DistinctAttributes int `json:"distinct_attributes"`
LockOut int `json:"lock_out"`
Expiration int `json:"expiration"` // seconds, -1 no expiration
}
func NewDefaultNKodePolicy() NKodePolicy {

View File

@@ -1,66 +0,0 @@
package main
import (
"errors"
"fmt"
"github.com/google/uuid"
"go-nkode/models"
)
type NKodeAPI struct {
Customers map[uuid.UUID]Customer
SignupSessions map[uuid.UUID]UserSignSession
}
func NewNKodeAPI() NKodeAPI {
return NKodeAPI{
Customers: make(map[uuid.UUID]Customer),
SignupSessions: make(map[uuid.UUID]UserSignSession),
}
}
func (n *NKodeAPI) CreateNewCustomer(keypadSize models.KeypadSize, nkodePolicy models.NKodePolicy) (*uuid.UUID, error) {
newCustomer, err := NewCustomer(keypadSize, nkodePolicy)
if err != nil {
return nil, err
}
n.Customers[newCustomer.CustomerId] = *newCustomer
return &newCustomer.CustomerId, nil
}
func (n *NKodeAPI) GenerateSignupInterface(customerId uuid.UUID) (*uuid.UUID, []int, error) {
customer, exists := n.Customers[customerId]
if !exists {
return nil, nil, errors.New(fmt.Sprintf("customer doesnt exists: %s", customerId.String()))
}
signupSession, err := NewSignupSession(customer.Attributes.KeypadSize, customer.CustomerId)
if err != nil {
return nil, nil, err
}
n.SignupSessions[signupSession.SessionId] = *signupSession
return &signupSession.SessionId, signupSession.SetInterface, nil
}
func (n *NKodeAPI) SetNKode(username string, customerId uuid.UUID, keySelection []int, sessionId uuid.UUID) ([]int, error) {
customer, exists := n.Customers[customerId]
if !exists {
return nil, errors.New(fmt.Sprintf("set nkode customer id does not exist %s", customerId.String()))
}
_, exists = customer.Users[username]
if exists {
return nil, errors.New(fmt.Sprintf("user already exists %s", username))
}
session, exists := n.SignupSessions[sessionId]
if !exists {
return nil, errors.New(fmt.Sprintf("session id does not exist %s", sessionId.String()))
}
confirmInterface, err := session.SetUserNKode(username, keySelection)
if err != nil {
return nil, err
}
return confirmInterface, nil
}
func (n *NKodeAPI) ConfirmNKode(username string, customerId uuid.UUID, confirmKeyEntry []int)

17
user.go
View File

@@ -1,17 +0,0 @@
package main
import (
m "go-nkode/models"
)
type User struct {
Username string
EncipheredPasscode m.EncipheredNKode
UserKeys UserCipherKeys
Interface UserInterface
Renew bool
}
func (u *User) DecipherMask(setVals []uint64, passcodeLen int) ([]uint64, error) {
return u.UserKeys.DecipherMask(u.EncipheredPasscode.Mask, setVals, passcodeLen)
}