implement and test sql db accessor

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

91
core/model/customer.go Normal file
View File

@@ -0,0 +1,91 @@
package m
import (
"errors"
"fmt"
"github.com/google/uuid"
"go-nkode/hashset"
py "go-nkode/py-builtin"
"go-nkode/util"
)
type Customer struct {
Id CustomerId
NKodePolicy NKodePolicy
Attributes CustomerAttributes
}
func NewCustomer(nkodePolicy NKodePolicy) (*Customer, error) {
customerAttrs, err := NewCustomerAttributes()
if err != nil {
return nil, err
}
customer := Customer{
Id: CustomerId(uuid.New()),
NKodePolicy: nkodePolicy,
Attributes: *customerAttrs,
}
return &customer, nil
}
func (c *Customer) IsValidNKode(kp KeypadDimension, passcodeAttrIdx []int) error {
nkodeLen := len(passcodeAttrIdx)
if nkodeLen < c.NKodePolicy.MinNkodeLen {
return errors.New(fmt.Sprintf("NKode length %d is too short. Minimum nKode length is %d", nkodeLen, c.NKodePolicy.MinNkodeLen))
}
validIdx := py.All[int](passcodeAttrIdx, func(i int) bool {
return i >= 0 && i < kp.TotalAttrs()
})
if !validIdx {
return errors.New(fmt.Sprintf("One or more idx out of range 0-%d in IsValidNKode", kp.TotalAttrs()-1))
}
passcodeSetVals := make(hashset.Set[uint64])
passcodeAttrVals := make(hashset.Set[uint64])
attrVals, err := c.Attributes.AttrValsForKp(kp)
if err != nil {
return err
}
for idx := 0; idx < nkodeLen; idx++ {
attrVal := attrVals[passcodeAttrIdx[idx]]
setVal, err := c.Attributes.GetAttrSetVal(attrVal, kp)
if err != nil {
return err
}
passcodeSetVals.Add(setVal)
passcodeAttrVals.Add(attrVal)
}
if passcodeSetVals.Size() < c.NKodePolicy.DistinctSets {
return errors.New(fmt.Sprintf("passcode has two few distinct sets min %d, has %d", c.NKodePolicy.DistinctSets, passcodeSetVals.Size()))
}
if passcodeAttrVals.Size() < c.NKodePolicy.DistinctAttributes {
return errors.New(fmt.Sprintf("passcode has two few distinct attributes min %d, has %d", c.NKodePolicy.DistinctAttributes, passcodeAttrVals.Size()))
}
return nil
}
func (c *Customer) RenewKeys() ([]uint64, []uint64) {
oldAttrs := make([]uint64, len(c.Attributes.AttrVals))
oldSets := make([]uint64, len(c.Attributes.SetVals))
copy(oldAttrs, c.Attributes.AttrVals)
copy(oldSets, c.Attributes.SetVals)
err := c.Attributes.Renew()
if err != nil {
panic(err)
}
attrsXor, err := util.XorLists(oldAttrs, c.Attributes.AttrVals)
if err != nil {
panic(err)
}
setXor, err := util.XorLists(oldSets, c.Attributes.SetVals)
if err != nil {
panic(err)
}
return setXor, attrsXor
}

View File

@@ -0,0 +1,97 @@
package m
import (
"errors"
"fmt"
"go-nkode/util"
)
type CustomerAttributes struct {
AttrVals []uint64
SetVals []uint64
}
func NewCustomerAttributes() (*CustomerAttributes, error) {
attrVals, errAttr := util.GenerateRandomNonRepeatingUint64(KeypadMax.TotalAttrs())
if errAttr != nil {
return nil, errAttr
}
setVals, errSet := util.GenerateRandomNonRepeatingUint64(KeypadMax.AttrsPerKey)
if errSet != nil {
return nil, errSet
}
customerAttrs := CustomerAttributes{
AttrVals: attrVals,
SetVals: setVals,
}
return &customerAttrs, nil
}
func NewCustomerAttributesFromBytes(attrBytes []byte, setBytes []byte) CustomerAttributes {
return CustomerAttributes{
AttrVals: util.ByteArrToUint64Arr(attrBytes),
SetVals: util.ByteArrToUint64Arr(setBytes),
}
}
func (c *CustomerAttributes) Renew() error {
attrVals, errAttr := util.GenerateRandomNonRepeatingUint64(KeypadMax.TotalAttrs())
if errAttr != nil {
return errAttr
}
setVals, errSet := util.GenerateRandomNonRepeatingUint64(KeypadMax.AttrsPerKey)
if errSet != nil {
return errSet
}
c.AttrVals = attrVals
c.SetVals = setVals
return nil
}
func (c *CustomerAttributes) IndexOfAttr(attrVal uint64) int {
// TODO: should this be mapped instead?
return util.IndexOf[uint64](c.AttrVals, attrVal)
}
func (c *CustomerAttributes) IndexOfSet(setVal uint64) (int, error) {
// TODO: should this be mapped instead?
idx := util.IndexOf[uint64](c.SetVals, setVal)
if idx == -1 {
return -1, errors.New(fmt.Sprintf("Set Val %d is invalid", setVal))
}
return idx, nil
}
func (c *CustomerAttributes) GetAttrSetVal(attrVal uint64, userKeypad KeypadDimension) (uint64, error) {
indexOfAttr := c.IndexOfAttr(attrVal)
if indexOfAttr == -1 {
return 0, errors.New(fmt.Sprintf("No attribute %d", attrVal))
}
setIdx := indexOfAttr % userKeypad.AttrsPerKey
return c.SetVals[setIdx], nil
}
func (c *CustomerAttributes) AttrValsForKp(userKp KeypadDimension) ([]uint64, error) {
err := userKp.IsValidKeypadDimension()
if err != nil {
return nil, err
}
return c.AttrVals[:userKp.TotalAttrs()], nil
}
func (c *CustomerAttributes) SetValsForKp(userKp KeypadDimension) ([]uint64, error) {
err := userKp.IsValidKeypadDimension()
if err != nil {
return nil, err
}
return c.SetVals[:userKp.AttrsPerKey], nil
}
func (c *CustomerAttributes) AttrBytes() []byte {
return util.Uint64ArrToByteArr(c.AttrVals)
}
func (c *CustomerAttributes) SetBytes() []byte {
return util.Uint64ArrToByteArr(c.SetVals)
}

View File

@@ -1,31 +1,31 @@
package api package m
import ( import (
"encoding/json" "encoding/json"
m "go-nkode/core/model" "go-nkode/core/api"
"log" "log"
"net/http" "net/http"
) )
type NKodeHandler struct { type NKodeHandler struct {
Api m.NKodeAPIInterface Api NKodeAPIInterface
} }
func (h *NKodeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *NKodeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path { switch r.URL.Path {
case CreateNewCustomer: case api.CreateNewCustomer:
h.CreateNewCustomerHandler(w, r) h.CreateNewCustomerHandler(w, r)
case GenerateSignupInterface: case api.GenerateSignupInterface:
h.GenerateSignupInterfaceHandler(w, r) h.GenerateSignupInterfaceHandler(w, r)
case SetNKode: case api.SetNKode:
h.SetNKodeHandler(w, r) h.SetNKodeHandler(w, r)
case ConfirmNKode: case api.ConfirmNKode:
h.ConfirmNKodeHandler(w, r) h.ConfirmNKodeHandler(w, r)
case GetLoginInterface: case api.GetLoginInterface:
h.GetLoginInterfaceHandler(w, r) h.GetLoginInterfaceHandler(w, r)
case Login: case api.Login:
h.LoginHandler(w, r) h.LoginHandler(w, r)
case RenewAttributes: case api.RenewAttributes:
h.RenewAttributesHandler(w, r) h.RenewAttributesHandler(w, r)
default: default:
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
@@ -39,7 +39,7 @@ func (h *NKodeHandler) CreateNewCustomerHandler(w http.ResponseWriter, r *http.R
methodNotAllowed(w) methodNotAllowed(w)
return return
} }
var customerPost m.NewCustomerPost var customerPost NewCustomerPost
err := decodeJson(w, r, &customerPost) err := decodeJson(w, r, &customerPost)
if err != nil { if err != nil {
internalServerErrorHandler(w) internalServerErrorHandler(w)
@@ -52,7 +52,7 @@ func (h *NKodeHandler) CreateNewCustomerHandler(w http.ResponseWriter, r *http.R
log.Fatal(err) log.Fatal(err)
return return
} }
respBody := m.CreateNewCustomerResp{ respBody := CreateNewCustomerResp{
CustomerId: *customerId, CustomerId: *customerId,
} }
respBytes, err := json.Marshal(respBody) respBytes, err := json.Marshal(respBody)
@@ -77,14 +77,14 @@ func (h *NKodeHandler) GenerateSignupInterfaceHandler(w http.ResponseWriter, r *
return return
} }
var signupPost m.GenerateSignupInterfacePost var signupPost GenerateSignupInterfacePost
err := decodeJson(w, r, &signupPost) err := decodeJson(w, r, &signupPost)
if err != nil { if err != nil {
internalServerErrorHandler(w) internalServerErrorHandler(w)
log.Fatal(err) log.Fatal(err)
return return
} }
resp, err := h.Api.GenerateSignupInterface(signupPost.CustomerId, m.KeypadDefault) resp, err := h.Api.GenerateSignupInterface(signupPost.CustomerId, KeypadDefault)
if err != nil { if err != nil {
internalServerErrorHandler(w) internalServerErrorHandler(w)
log.Fatal(err) log.Fatal(err)
@@ -112,7 +112,7 @@ func (h *NKodeHandler) SetNKodeHandler(w http.ResponseWriter, r *http.Request) {
methodNotAllowed(w) methodNotAllowed(w)
return return
} }
var setNKodePost m.SetNKodePost var setNKodePost SetNKodePost
err := decodeJson(w, r, &setNKodePost) err := decodeJson(w, r, &setNKodePost)
if err != nil { if err != nil {
internalServerErrorHandler(w) internalServerErrorHandler(w)
@@ -125,7 +125,7 @@ func (h *NKodeHandler) SetNKodeHandler(w http.ResponseWriter, r *http.Request) {
log.Fatal(err) log.Fatal(err)
return return
} }
respBody := m.SetNKodeResp{UserInterface: confirmInterface} respBody := SetNKodeResp{UserInterface: confirmInterface}
respBytes, err := json.Marshal(respBody) respBytes, err := json.Marshal(respBody)
if err != nil { if err != nil {
@@ -150,7 +150,7 @@ func (h *NKodeHandler) ConfirmNKodeHandler(w http.ResponseWriter, r *http.Reques
return return
} }
var confirmNKodePost m.ConfirmNKodePost var confirmNKodePost ConfirmNKodePost
err := decodeJson(w, r, &confirmNKodePost) err := decodeJson(w, r, &confirmNKodePost)
if err != nil { if err != nil {
internalServerErrorHandler(w) internalServerErrorHandler(w)
@@ -173,7 +173,7 @@ func (h *NKodeHandler) GetLoginInterfaceHandler(w http.ResponseWriter, r *http.R
methodNotAllowed(w) methodNotAllowed(w)
return return
} }
var loginInterfacePost m.GetLoginInterfacePost var loginInterfacePost GetLoginInterfacePost
err := decodeJson(w, r, &loginInterfacePost) err := decodeJson(w, r, &loginInterfacePost)
if err != nil { if err != nil {
internalServerErrorHandler(w) internalServerErrorHandler(w)
@@ -187,7 +187,7 @@ func (h *NKodeHandler) GetLoginInterfaceHandler(w http.ResponseWriter, r *http.R
return return
} }
respBody := m.GetLoginInterfaceResp{UserInterface: loginInterface} respBody := GetLoginInterfaceResp{UserInterface: loginInterface}
respBytes, err := json.Marshal(respBody) respBytes, err := json.Marshal(respBody)
if err != nil { if err != nil {
internalServerErrorHandler(w) internalServerErrorHandler(w)
@@ -210,7 +210,7 @@ func (h *NKodeHandler) LoginHandler(w http.ResponseWriter, r *http.Request) {
methodNotAllowed(w) methodNotAllowed(w)
return return
} }
var loginPost m.LoginPost var loginPost LoginPost
err := decodeJson(w, r, &loginPost) err := decodeJson(w, r, &loginPost)
if err != nil { if err != nil {
internalServerErrorHandler(w) internalServerErrorHandler(w)
@@ -232,7 +232,7 @@ func (h *NKodeHandler) RenewAttributesHandler(w http.ResponseWriter, r *http.Req
methodNotAllowed(w) methodNotAllowed(w)
return return
} }
var renewAttributesPost m.RenewAttributesPost var renewAttributesPost RenewAttributesPost
err := decodeJson(w, r, &renewAttributesPost) err := decodeJson(w, r, &renewAttributesPost)
if err != nil { if err != nil {

View File

@@ -31,5 +31,8 @@ func (p *NKodePolicy) ValidLength(nkodeLen int) error {
if nkodeLen < p.MinNkodeLen || nkodeLen > p.MaxNkodeLen { if nkodeLen < p.MinNkodeLen || nkodeLen > p.MaxNkodeLen {
return InvalidNKodeLen return InvalidNKodeLen
} }
// TODO: validate Max > Min
// Validate lockout
// Add Lockout To User
return nil return nil
} }

View File

@@ -1,13 +1,12 @@
package nkode package m
import ( import (
"errors" "errors"
"fmt" "fmt"
m "go-nkode/core/model"
"go-nkode/util" "go-nkode/util"
) )
func SelectKeyByAttrIdx(interfaceUser []int, passcodeIdxs []int, keypadSize m.KeypadDimension) ([]int, error) { func SelectKeyByAttrIdx(interfaceUser []int, passcodeIdxs []int, keypadSize KeypadDimension) ([]int, error) {
selectedKeys := make([]int, len(passcodeIdxs)) selectedKeys := make([]int, len(passcodeIdxs))
for idx := range passcodeIdxs { for idx := range passcodeIdxs {
attrIdx := util.IndexOf[int](interfaceUser, passcodeIdxs[idx]) attrIdx := util.IndexOf[int](interfaceUser, passcodeIdxs[idx])

View File

@@ -1,16 +1,15 @@
package nkode package m
import ( import (
m "go-nkode/core/model"
"go-nkode/util" "go-nkode/util"
) )
type User struct { type User struct {
Id m.UserId Id UserId
CustomerId m.CustomerId CustomerId CustomerId
Username m.Username Username Username
EncipheredPasscode m.EncipheredNKode EncipheredPasscode EncipheredNKode
Kp m.KeypadDimension Kp KeypadDimension
UserKeys UserCipherKeys UserKeys UserCipherKeys
Interface UserInterface Interface UserInterface
Renew bool Renew bool
@@ -23,19 +22,16 @@ func (u *User) DecipherMask(setVals []uint64, passcodeLen int) ([]uint64, error)
func (u *User) RenewKeys(setXor []uint64, attrXor []uint64) error { func (u *User) RenewKeys(setXor []uint64, attrXor []uint64) error {
u.Renew = true u.Renew = true
var err error var err error
u.UserKeys.SetKey, err = util.XorLists(setXor, u.UserKeys.SetKey) u.UserKeys.SetKey, err = util.XorLists(setXor[:u.Kp.AttrsPerKey], u.UserKeys.SetKey)
if err != nil { if err != nil {
panic(err) panic(err)
} }
u.UserKeys.AlphaKey, err = util.XorLists(attrXor, u.UserKeys.AlphaKey) u.UserKeys.AlphaKey, err = util.XorLists(attrXor[:u.Kp.TotalAttrs()], u.UserKeys.AlphaKey)
if err != nil { return err
panic(err)
}
return nil
} }
func (u *User) RefreshPasscode(passcodeAttrIdx []int, customerAttributes CustomerAttributes) error { func (u *User) RefreshPasscode(passcodeAttrIdx []int, customerAttributes CustomerAttributes) error {
setVals, err := customerAttributes.SetVals(u.Kp) setVals, err := customerAttributes.SetValsForKp(u.Kp)
newKeys, err := NewUserCipherKeys(&u.Kp, setVals, u.UserKeys.MaxNKodeLen) newKeys, err := NewUserCipherKeys(&u.Kp, setVals, u.UserKeys.MaxNKodeLen)
if err != nil { if err != nil {
return err return err

View File

@@ -1,27 +1,23 @@
package nkode package m
import ( import (
"crypto/sha256" "crypto/sha256"
"errors" "errors"
"github.com/google/uuid"
m "go-nkode/core/model"
"go-nkode/util" "go-nkode/util"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
type UserCipherKeys struct { type UserCipherKeys struct {
Id uuid.UUID
UserId m.UserId
AlphaKey []uint64 AlphaKey []uint64
SetKey []uint64 SetKey []uint64
PassKey []uint64 PassKey []uint64
MaskKey []uint64 MaskKey []uint64
Salt []byte Salt []byte
MaxNKodeLen int MaxNKodeLen int
kp *m.KeypadDimension Kp *KeypadDimension
} }
func NewUserCipherKeys(kp *m.KeypadDimension, setVals []uint64, maxNKodeLen int) (*UserCipherKeys, error) { func NewUserCipherKeys(kp *KeypadDimension, setVals []uint64, maxNKodeLen int) (*UserCipherKeys, error) {
err := kp.IsValidKeypadDimension() err := kp.IsValidKeypadDimension()
if err != nil { if err != nil {
return nil, err return nil, err
@@ -40,14 +36,13 @@ func NewUserCipherKeys(kp *m.KeypadDimension, setVals []uint64, maxNKodeLen int)
maskKey, _ := util.GenerateRandomNonRepeatingUint64(maxNKodeLen) maskKey, _ := util.GenerateRandomNonRepeatingUint64(maxNKodeLen)
salt, _ := util.RandomBytes(10) salt, _ := util.RandomBytes(10)
userCipherKeys := UserCipherKeys{ userCipherKeys := UserCipherKeys{
Id: uuid.New(),
AlphaKey: alphakey, AlphaKey: alphakey,
PassKey: passKey, PassKey: passKey,
MaskKey: maskKey, MaskKey: maskKey,
SetKey: setKey, SetKey: setKey,
Salt: salt, Salt: salt,
MaxNKodeLen: maxNKodeLen, MaxNKodeLen: maxNKodeLen,
kp: kp, Kp: kp,
} }
return &userCipherKeys, nil return &userCipherKeys, nil
} }
@@ -127,8 +122,8 @@ func (u *UserCipherKeys) hashPasscode(passcodeDigest []byte) ([]byte, error) {
} }
return hashedPassword, nil return hashedPassword, nil
} }
func (u *UserCipherKeys) EncipherMask(passcodeSet []uint64, customerAttrs CustomerAttributes, userKp m.KeypadDimension) (string, error) { func (u *UserCipherKeys) EncipherMask(passcodeSet []uint64, customerAttrs CustomerAttributes, userKp KeypadDimension) (string, error) {
setVals, err := customerAttrs.SetVals(userKp) setVals, err := customerAttrs.SetValsForKp(userKp)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -174,8 +169,8 @@ func (u *UserCipherKeys) DecipherMask(mask string, setVals []uint64, passcodeLen
return passcodeSet, nil return passcodeSet, nil
} }
func (u *UserCipherKeys) EncipherNKode(passcodeAttrIdx []int, customerAttrs CustomerAttributes) (*m.EncipheredNKode, error) { func (u *UserCipherKeys) EncipherNKode(passcodeAttrIdx []int, customerAttrs CustomerAttributes) (*EncipheredNKode, error) {
attrVals, err := customerAttrs.AttrVals(*u.kp) attrVals, err := customerAttrs.AttrValsForKp(*u.Kp)
code, err := u.EncipherSaltHashCode(passcodeAttrIdx, attrVals) code, err := u.EncipherSaltHashCode(passcodeAttrIdx, attrVals)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -184,13 +179,13 @@ func (u *UserCipherKeys) EncipherNKode(passcodeAttrIdx []int, customerAttrs Cust
for idx := range passcodeSet { for idx := range passcodeSet {
passcodeAttr := attrVals[passcodeAttrIdx[idx]] passcodeAttr := attrVals[passcodeAttrIdx[idx]]
passcodeSet[idx], err = customerAttrs.GetAttrSetVal(passcodeAttr, *u.kp) passcodeSet[idx], err = customerAttrs.GetAttrSetVal(passcodeAttr, *u.Kp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
mask, err := u.EncipherMask(passcodeSet, customerAttrs, *u.kp) mask, err := u.EncipherMask(passcodeSet, customerAttrs, *u.Kp)
encipheredCode := m.EncipheredNKode{ encipheredCode := EncipheredNKode{
Code: code, Code: code,
Mask: mask, Mask: mask,
} }

View File

@@ -1,19 +1,18 @@
package nkode package m
import ( import (
"errors" "errors"
"fmt" "fmt"
m "go-nkode/core/model"
"go-nkode/hashset" "go-nkode/hashset"
"go-nkode/util" "go-nkode/util"
) )
type UserInterface struct { type UserInterface struct {
IdxInterface m.IdxInterface IdxInterface IdxInterface
Kp *m.KeypadDimension Kp *KeypadDimension
} }
func NewUserInterface(kp *m.KeypadDimension) (*UserInterface, error) { func NewUserInterface(kp *KeypadDimension) (*UserInterface, error) {
idxInterface := util.IdentityArray(kp.TotalAttrs()) idxInterface := util.IdentityArray(kp.TotalAttrs())
userInterface := UserInterface{ userInterface := UserInterface{
IdxInterface: idxInterface, IdxInterface: idxInterface,

View File

@@ -1,20 +1,19 @@
package nkode package m
import ( import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
m "go-nkode/core/model"
py "go-nkode/py-builtin" py "go-nkode/py-builtin"
"testing" "testing"
) )
func TestUserCipherKeys_EncipherSaltHashCode(t *testing.T) { func TestUserCipherKeys_EncipherSaltHashCode(t *testing.T) {
kp := m.KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 8} kp := KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 8}
maxNKodeLen := 10 maxNKodeLen := 10
customerAttrs, err := NewCustomerAttributes() customerAttrs, err := NewCustomerAttributes()
assert.NoError(t, err) assert.NoError(t, err)
setVals, err := customerAttrs.SetVals(kp) setVals, err := customerAttrs.SetValsForKp(kp)
assert.NoError(t, err) assert.NoError(t, err)
attrVals, err := customerAttrs.AttrVals(kp) attrVals, err := customerAttrs.AttrValsForKp(kp)
assert.NoError(t, err) assert.NoError(t, err)
newUser, err := NewUserCipherKeys(&kp, setVals, maxNKodeLen) newUser, err := NewUserCipherKeys(&kp, setVals, maxNKodeLen)
assert.NoError(t, err) assert.NoError(t, err)
@@ -26,14 +25,14 @@ func TestUserCipherKeys_EncipherSaltHashCode(t *testing.T) {
} }
func TestUserCipherKeys_EncipherDecipherMask(t *testing.T) { func TestUserCipherKeys_EncipherDecipherMask(t *testing.T) {
kp := m.KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 8} kp := KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 8}
maxNKodeLen := 10 maxNKodeLen := 10
customerAttrs, err := NewCustomerAttributes() customerAttrs, err := NewCustomerAttributes()
assert.NoError(t, err) assert.NoError(t, err)
setVals, err := customerAttrs.SetVals(kp) setVals, err := customerAttrs.SetValsForKp(kp)
assert.NoError(t, err) assert.NoError(t, err)
attrVals, err := customerAttrs.AttrVals(kp) attrVals, err := customerAttrs.AttrValsForKp(kp)
assert.NoError(t, err) assert.NoError(t, err)
newUser, err := NewUserCipherKeys(&kp, setVals, maxNKodeLen) newUser, err := NewUserCipherKeys(&kp, setVals, maxNKodeLen)
assert.NoError(t, err) assert.NoError(t, err)
@@ -56,7 +55,7 @@ func TestUserCipherKeys_EncipherDecipherMask(t *testing.T) {
} }
func TestUserInterface_RandomShuffle(t *testing.T) { func TestUserInterface_RandomShuffle(t *testing.T) {
kp := m.KeypadDimension{ kp := KeypadDimension{
AttrsPerKey: 10, AttrsPerKey: 10,
NumbOfKeys: 8, NumbOfKeys: 8,
} }
@@ -81,7 +80,7 @@ func TestUserInterface_RandomShuffle(t *testing.T) {
func TestUserInterface_DisperseInterface(t *testing.T) { func TestUserInterface_DisperseInterface(t *testing.T) {
for idx := 0; idx < 10000; idx++ { for idx := 0; idx < 10000; idx++ {
kp := m.KeypadDimension{AttrsPerKey: 7, NumbOfKeys: 10} kp := KeypadDimension{AttrsPerKey: 7, NumbOfKeys: 10}
userInterface, err := NewUserInterface(&kp) userInterface, err := NewUserInterface(&kp)
assert.NoError(t, err) assert.NoError(t, err)
@@ -100,7 +99,7 @@ func TestUserInterface_DisperseInterface(t *testing.T) {
} }
func TestUserInterface_PartialInterfaceShuffle(t *testing.T) { func TestUserInterface_PartialInterfaceShuffle(t *testing.T) {
kp := m.KeypadDimension{AttrsPerKey: 7, NumbOfKeys: 10} kp := KeypadDimension{AttrsPerKey: 7, NumbOfKeys: 10}
userInterface, err := NewUserInterface(&kp) userInterface, err := NewUserInterface(&kp)
assert.NoError(t, err) assert.NoError(t, err)
preShuffle := userInterface.IdxInterface preShuffle := userInterface.IdxInterface

View File

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

View File

@@ -1,200 +0,0 @@
package nkode
import (
"errors"
"fmt"
"github.com/google/uuid"
m "go-nkode/core/model"
"go-nkode/hashset"
py "go-nkode/py-builtin"
"go-nkode/util"
)
type Customer struct {
Id m.CustomerId
NKodePolicy m.NKodePolicy
Attributes CustomerAttributes
}
func NewCustomer(nkodePolicy m.NKodePolicy) (*Customer, error) {
customerAttrs, err := NewCustomerAttributes()
if err != nil {
return nil, err
}
customer := Customer{
Id: m.CustomerId(uuid.New()),
NKodePolicy: nkodePolicy,
Attributes: *customerAttrs,
}
return &customer, nil
}
//func (c *Customer) AddNewUser(username m.Username, passcodeIdx []int, ui UserInterface, kp m.KeypadDimension) error {
// _, exists := c.Users[username]
// if exists {
// return errors.New(fmt.Sprintf("User %s already exists for customer %+v exists", username, c.Id))
// }
// setVals, err := c.Attributes.SetVals(kp)
// if err != nil {
// return err
// }
// newKeys, err := NewUserCipherKeys(&kp, setVals, c.NKodePolicy.MaxNkodeLen)
// if err != nil {
// return err
// }
// encipheredNKode, err := newKeys.EncipherNKode(passcodeIdx, c.Attributes)
// if err != nil {
// return err
// }
// newUser := User{
// Id: m.UserId(uuid.New()),
// Username: username,
// EncipheredPasscode: *encipheredNKode,
// UserKeys: *newKeys,
// Interface: ui,
// Kp: kp,
// }
// c.Users[username] = newUser
// return nil
//}
//func (c *Customer) ValidKeyEntry(username m.Username, selectedKeys []int) ([]int, error) {
// user, exists := c.Users[username]
// if !exists {
// return nil, errors.New(fmt.Sprintf("user %s does not exist for customer %+v", username, c.Id))
// }
//
// validKeys := py.All[int](selectedKeys, func(idx int) bool {
// return 0 <= idx && idx < user.Kp.NumbOfKeys
// })
// if !validKeys {
// return nil, errors.New(fmt.Sprintf("one or more keys not in range 0-%d", user.Kp.NumbOfKeys-1))
// }
// presumedAttrIdxVals, err := c.getPresumedAttributeIdxVals(user, selectedKeys)
// if err != nil {
// return nil, err
// }
// err = c.IsValidNKode(user.Kp, presumedAttrIdxVals)
// if err != nil {
// return nil, err
// }
// attrVals, err := c.Attributes.AttrVals(user.Kp)
// if err != nil {
// return nil, err
// }
// err = user.UserKeys.ValidPassword(user.EncipheredPasscode.Code, presumedAttrIdxVals, attrVals)
// if err != nil {
// return nil, err
// }
//
// return presumedAttrIdxVals, nil
//}
//func (c *Customer) getPresumedAttributeIdxVals(user User, selectedKeys []int) ([]int, error) {
//
// passcodeLen := len(selectedKeys)
// if passcodeLen < c.NKodePolicy.MinNkodeLen || passcodeLen > c.NKodePolicy.MaxNkodeLen {
// return nil, errors.New(fmt.Sprintf("Invalid passcode length of %d. Passcode length must be in range %d-%d", passcodeLen, c.NKodePolicy.MinNkodeLen, c.NKodePolicy.MaxNkodeLen))
// }
//
// setVals, err := c.Attributes.SetVals(user.Kp)
// if err != nil {
// return nil, err
// }
// passcodeSetVals, err := user.DecipherMask(setVals, passcodeLen)
// if err != nil {
// return nil, err
// }
// presumedAttrIdxVals := make([]int, passcodeLen)
//
// for idx := range presumedAttrIdxVals {
// keyNumb := selectedKeys[idx]
// setIdx, err := c.Attributes.IndexOfSet(passcodeSetVals[idx])
// if err != nil {
// return nil, err
// }
// selectedAttrIdx, err := user.Interface.GetAttrIdxByKeyNumbSetIdx(setIdx, keyNumb)
// if err != nil {
// return nil, err
// }
// presumedAttrIdxVals[idx] = selectedAttrIdx
// }
// return presumedAttrIdxVals, nil
//}
func (c *Customer) IsValidNKode(kp m.KeypadDimension, passcodeAttrIdx []int) error {
nkodeLen := len(passcodeAttrIdx)
if nkodeLen < c.NKodePolicy.MinNkodeLen {
return errors.New(fmt.Sprintf("NKode length %d is too short. Minimum nKode length is %d", nkodeLen, c.NKodePolicy.MinNkodeLen))
}
validIdx := py.All[int](passcodeAttrIdx, func(i int) bool {
return i >= 0 && i < kp.TotalAttrs()
})
if !validIdx {
return errors.New(fmt.Sprintf("One or more idx out of range 0-%d in IsValidNKode", kp.TotalAttrs()-1))
}
passcodeSetVals := make(hashset.Set[uint64])
passcodeAttrVals := make(hashset.Set[uint64])
attrVals, err := c.Attributes.AttrVals(kp)
if err != nil {
return err
}
for idx := 0; idx < nkodeLen; idx++ {
attrVal := attrVals[passcodeAttrIdx[idx]]
setVal, err := c.Attributes.GetAttrSetVal(attrVal, kp)
if err != nil {
return err
}
passcodeSetVals.Add(setVal)
passcodeAttrVals.Add(attrVal)
}
if passcodeSetVals.Size() < c.NKodePolicy.DistinctSets {
return errors.New(fmt.Sprintf("passcode has two few distinct sets min %d, has %d", c.NKodePolicy.DistinctSets, passcodeSetVals.Size()))
}
if passcodeAttrVals.Size() < c.NKodePolicy.DistinctAttributes {
return errors.New(fmt.Sprintf("passcode has two few distinct attributes min %d, has %d", c.NKodePolicy.DistinctAttributes, passcodeAttrVals.Size()))
}
return nil
}
func (c *Customer) RenewKeys() ([]uint64, []uint64) {
oldAttrs := make([]uint64, m.KeypadMax.TotalAttrs())
oldSets := make([]uint64, m.KeypadMax.AttrsPerKey)
allAttrVals, err := c.Attributes.AttrVals(m.KeypadMax)
if err != nil {
panic(err)
}
allSetVals, err := c.Attributes.AttrVals(m.KeypadMax)
if err != nil {
panic(err)
}
copy(oldAttrs, allAttrVals)
copy(oldSets, allSetVals)
err = c.Attributes.Renew()
if err != nil {
panic(err)
}
allAttrVals, err = c.Attributes.AttrVals(m.KeypadMax)
if err != nil {
panic(err)
}
allSetVals, err = c.Attributes.SetVals(m.KeypadMax)
if err != nil {
panic(err)
}
attrsXor, err := util.XorLists(oldAttrs, allAttrVals)
if err != nil {
panic(err)
}
setXor, err := util.XorLists(oldSets, allSetVals)
if err != nil {
panic(err)
}
return setXor, attrsXor
}

View File

@@ -1,83 +0,0 @@
package nkode
import (
"errors"
"fmt"
"go-nkode/core/model"
"go-nkode/util"
)
type CustomerAttributes struct {
attrVals []uint64
setVals []uint64
}
func NewCustomerAttributes() (*CustomerAttributes, error) {
attrVals, errAttr := util.GenerateRandomNonRepeatingUint64(m.KeypadMax.TotalAttrs())
if errAttr != nil {
return nil, errAttr
}
setVals, errSet := util.GenerateRandomNonRepeatingUint64(m.KeypadMax.AttrsPerKey)
if errSet != nil {
return nil, errSet
}
customerAttrs := CustomerAttributes{
attrVals: attrVals,
setVals: setVals,
}
return &customerAttrs, nil
}
func (c *CustomerAttributes) Renew() error {
attrVals, errAttr := util.GenerateRandomNonRepeatingUint64(m.KeypadMax.TotalAttrs())
if errAttr != nil {
return errAttr
}
setVals, errSet := util.GenerateRandomNonRepeatingUint64(m.KeypadMax.AttrsPerKey)
if errSet != nil {
return errSet
}
c.attrVals = attrVals
c.setVals = setVals
return nil
}
func (c *CustomerAttributes) IndexOfAttr(attrVal uint64) int {
// TODO: should this be mapped instead?
return util.IndexOf[uint64](c.attrVals, attrVal)
}
func (c *CustomerAttributes) IndexOfSet(setVal uint64) (int, error) {
// TODO: should this be mapped instead?
idx := util.IndexOf[uint64](c.setVals, setVal)
if idx == -1 {
return -1, errors.New(fmt.Sprintf("Set Val %d is invalid", setVal))
}
return idx, nil
}
func (c *CustomerAttributes) GetAttrSetVal(attrVal uint64, userKeypad m.KeypadDimension) (uint64, error) {
indexOfAttr := c.IndexOfAttr(attrVal)
if indexOfAttr == -1 {
return 0, errors.New(fmt.Sprintf("No attribute %d", attrVal))
}
setIdx := indexOfAttr % userKeypad.AttrsPerKey
return c.setVals[setIdx], nil
}
func (c *CustomerAttributes) AttrVals(userKp m.KeypadDimension) ([]uint64, error) {
err := userKp.IsValidKeypadDimension()
if err != nil {
return nil, err
}
return c.attrVals[:userKp.TotalAttrs()], nil
}
func (c *CustomerAttributes) SetVals(userKp m.KeypadDimension) ([]uint64, error) {
err := userKp.IsValidKeypadDimension()
if err != nil {
return nil, err
}
return c.setVals[:userKp.AttrsPerKey], nil
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

11
main.go
View File

@@ -3,15 +3,20 @@ package main
import ( import (
"fmt" "fmt"
"go-nkode/core/api" "go-nkode/core/api"
"go-nkode/core/model"
"go-nkode/core/nkode" "go-nkode/core/nkode"
"log" "log"
"net/http" "net/http"
) )
func main() { func main() {
db := nkode.NewInMemoryDb() //db := nkode.NewInMemoryDb()
nkodeApi := nkode.NewNKodeAPI(&db) db, err := nkode.NewSqliteDB("nkode.db")
handler := api.NKodeHandler{Api: &nkodeApi} if err != nil {
log.Fatal(err)
}
nkodeApi := nkode.NewNKodeAPI(db)
handler := m.NKodeHandler{Api: &nkodeApi}
mux := http.NewServeMux() mux := http.NewServeMux()
mux.Handle(api.CreateNewCustomer, &handler) mux.Handle(api.CreateNewCustomer, &handler)
mux.Handle(api.GenerateSignupInterface, &handler) mux.Handle(api.GenerateSignupInterface, &handler)

View File

@@ -6,7 +6,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go-nkode/core/api" "go-nkode/core/api"
m "go-nkode/core/model" m "go-nkode/core/model"
"go-nkode/core/nkode"
"io" "io"
"net/http" "net/http"
"testing" "testing"
@@ -30,7 +29,7 @@ func TestApi(t *testing.T) {
setInterface := signupInterfaceResp.UserInterface setInterface := signupInterfaceResp.UserInterface
userPasscode := setInterface[:passcodeLen] userPasscode := setInterface[:passcodeLen]
kp = m.KeypadDimension{NumbOfKeys: kp.NumbOfKeys, AttrsPerKey: kp.NumbOfKeys} kp = m.KeypadDimension{NumbOfKeys: kp.NumbOfKeys, AttrsPerKey: kp.NumbOfKeys}
setKeySelection, err := nkode.SelectKeyByAttrIdx(setInterface, userPasscode, kp) setKeySelection, err := m.SelectKeyByAttrIdx(setInterface, userPasscode, kp)
assert.NoError(t, err) assert.NoError(t, err)
setNKodeBody := m.SetNKodePost{ setNKodeBody := m.SetNKodePost{
CustomerId: customerResp.CustomerId, CustomerId: customerResp.CustomerId,
@@ -41,7 +40,7 @@ func TestApi(t *testing.T) {
var setNKodeResp m.SetNKodeResp var setNKodeResp m.SetNKodeResp
testApiCall(t, base+api.SetNKode, setNKodeBody, &setNKodeResp) testApiCall(t, base+api.SetNKode, setNKodeBody, &setNKodeResp)
confirmInterface := setNKodeResp.UserInterface confirmInterface := setNKodeResp.UserInterface
confirmKeySelection, err := nkode.SelectKeyByAttrIdx(confirmInterface, userPasscode, kp) confirmKeySelection, err := m.SelectKeyByAttrIdx(confirmInterface, userPasscode, kp)
assert.NoError(t, err) assert.NoError(t, err)
confirmNKodeBody := m.ConfirmNKodePost{ confirmNKodeBody := m.ConfirmNKodePost{
CustomerId: customerResp.CustomerId, CustomerId: customerResp.CustomerId,
@@ -59,7 +58,7 @@ func TestApi(t *testing.T) {
testApiCall(t, base+api.GetLoginInterface, loginInterfaceBody, &loginInterfaceResp) testApiCall(t, base+api.GetLoginInterface, loginInterfaceBody, &loginInterfaceResp)
kp = m.KeypadDefault kp = m.KeypadDefault
loginKeySelection, err := nkode.SelectKeyByAttrIdx(loginInterfaceResp.UserInterface, userPasscode, kp) loginKeySelection, err := m.SelectKeyByAttrIdx(loginInterfaceResp.UserInterface, userPasscode, kp)
assert.NoError(t, err) assert.NoError(t, err)
loginBody := m.LoginPost{ loginBody := m.LoginPost{
CustomerId: customerResp.CustomerId, CustomerId: customerResp.CustomerId,
@@ -72,7 +71,7 @@ func TestApi(t *testing.T) {
renewBody := m.RenewAttributesPost{CustomerId: customerResp.CustomerId} renewBody := m.RenewAttributesPost{CustomerId: customerResp.CustomerId}
testApiCall(t, base+api.RenewAttributes, renewBody, nil) testApiCall(t, base+api.RenewAttributes, renewBody, nil)
loginKeySelection, err = nkode.SelectKeyByAttrIdx(loginInterfaceResp.UserInterface, userPasscode, kp) loginKeySelection, err = m.SelectKeyByAttrIdx(loginInterfaceResp.UserInterface, userPasscode, kp)
assert.NoError(t, err) assert.NoError(t, err)
loginBody = m.LoginPost{ loginBody = m.LoginPost{
CustomerId: customerResp.CustomerId, CustomerId: customerResp.CustomerId,

View File

@@ -117,6 +117,17 @@ func Uint64ArrToByteArr(intArr []uint64) []byte {
return byteArr return byteArr
} }
func IntArrToByteArr(intArr []int) []byte {
byteArr := make([]byte, len(intArr)*4)
for idx, val := range intArr {
uval := uint32(val)
startIdx := idx * 4
endIdx := (idx + 1) * 4
binary.LittleEndian.PutUint32(byteArr[startIdx:endIdx], uval)
}
return byteArr
}
func ByteArrToUint64Arr(byteArr []byte) []uint64 { func ByteArrToUint64Arr(byteArr []byte) []uint64 {
intArr := make([]uint64, len(byteArr)/8) intArr := make([]uint64, len(byteArr)/8)
for idx := 0; idx < len(intArr); idx++ { for idx := 0; idx < len(intArr); idx++ {
@@ -127,6 +138,17 @@ func ByteArrToUint64Arr(byteArr []byte) []uint64 {
return intArr return intArr
} }
func ByteArrToIntArr(byteArr []byte) []int {
intArr := make([]int, len(byteArr)/4)
for idx := 0; idx < len(intArr); idx++ {
startIdx := idx * 4
endIdx := (idx + 1) * 4
uval := binary.LittleEndian.Uint32(byteArr[startIdx:endIdx])
intArr[idx] = int(uval)
}
return intArr
}
func IndexOf[T uint64 | int](arr []T, el T) int { func IndexOf[T uint64 | int](arr []T, el T) int {
for idx, val := range arr { for idx, val := range arr {
if val == el { if val == el {

View File

@@ -44,3 +44,11 @@ func TestMatrixTranspose(t *testing.T) {
assert.Equal(t, expectedFlatMat[idx], flatMat[idx]) assert.Equal(t, expectedFlatMat[idx], flatMat[idx])
} }
} }
func TestIntToByteAndBack(t *testing.T) {
origIntArr := []int{1, 2, 3, 4, 5}
byteArr := IntArrToByteArr(origIntArr)
intArr := ByteArrToIntArr(byteArr)
assert.ElementsMatch(t, origIntArr, intArr)
}