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 }