Skip to content
This repository has been archived by the owner on Jan 2, 2025. It is now read-only.

Commit

Permalink
fix(backend): device key mismatch bug
Browse files Browse the repository at this point in the history
  • Loading branch information
burdiyan committed Jul 11, 2024
1 parent 0c7f987 commit fd7fbf5
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 116 deletions.
17 changes: 0 additions & 17 deletions backend/daemon/storage2/sqlite.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package storage
import (
"context"
"path/filepath"
"seed/backend/core"
"seed/backend/daemon/storage/dbext"
"seed/backend/testutil"
"testing"
Expand Down Expand Up @@ -95,22 +94,6 @@ func MakeTestDB(t testing.TB) *sqlitex.Pool {
})
require.NoError(t, InitSQLiteSchema(pool))
return pool

}

// MakeTestRepo is a test helper to use our database schema in tests.
func MakeTestRepo(t testing.TB) *Store {
t.Helper()

path := testutil.MakeRepoPath(t)

//u := coretest.NewTester("alice")

repo, err := Open(path, nil, core.NewMemoryKeyStore(), "debug")
require.NoError(t, err)

return repo

}

// SetKV sets a key-value pair in the database.
Expand Down
128 changes: 75 additions & 53 deletions backend/daemon/storage2/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
package storage

import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"seed/backend/core"
Expand All @@ -26,78 +28,98 @@ type Store struct {

// Open initializes the storage directory.
// Device can be nil in which case a random new device key will be generated.
func Open(dataDir string, device crypto.PrivKey, kms core.KeyStore, logLevel string) (r *Store, err error) {
// Users are responsible for calling Close() to release the resources.
func Open(dataDir string, device crypto.PrivKey, kms core.KeyStore, logLevel string) (_ *Store, err error) {
log := logging.New("seed/repo", logLevel)

var kp core.KeyPair
if device != nil {
kp, err = core.NewKeyPair(device)
if err != nil {
return nil, err
if !filepath.IsAbs(dataDir) {
return nil, fmt.Errorf("must provide absolute repo path, got = %s", dataDir)
}

{
dirs := [...]string{
filepath.Join(dataDir, keysDir),
filepath.Join(dataDir, dbDir),
}
} else {
kp, err = core.NewKeyPairRandom()
if err != nil {
return nil, err
for _, d := range dirs {
if err := os.MkdirAll(d, 0700); err != nil {
return nil, fmt.Errorf("failed to create dir %s: %w", d, err)
}
}
}

r, err = newStore(dataDir, kp, log)
db, err := newSQLite(sqlitePath(dataDir))
if err != nil {
return nil, err
}
r.kms = kms
defer func() {
if err != nil {
err = errors.Join(err, db.Close())
}
}()

// TODO(hm24): This should probably be called from the outside somehow,
// because we want to provide the feedback about the migration and reindexing
// to the frontend that would call the Daemon API polling until everything is ready.
if err := r.Migrate(); err != nil {
return nil, err
ver, err := readVersionFile(dataDir)
if err != nil {
return nil, fmt.Errorf("failed to read version file: %w", err)
}

return r, nil
}
if ver == "" {
if device == nil {
kp, err := core.NewKeyPairRandom()
if err != nil {
return nil, fmt.Errorf("failed to generate device key pair: %w", err)
}

// Close the storage.
func (s *Store) Close() error {
return s.db.Close()
}
device = kp.Wrapped()
}

// newStore creates a newStore storage directory.
func newStore(path string, device core.KeyPair, log *zap.Logger) (s *Store, err error) {
if !filepath.IsAbs(path) {
return nil, fmt.Errorf("must provide absolute repo path, got = %s", path)
}
if err := InitSQLiteSchema(db); err != nil {
return nil, fmt.Errorf("failed to initialize SQLite database: %w", err)
}

s = &Store{
path: path,
log: log,
device: device,
if err := writeDeviceKeyFile(dataDir, device); err != nil {
return nil, err
}

if err := writeVersionFile(dataDir, desiredVersion()); err != nil {
return nil, fmt.Errorf("failed to write version file to init data directory: %w", err)
}
}

ver, err := readVersionFile(s.path)
kp, err := readDeviceKeyFile(dataDir)
if err != nil {
return nil, fmt.Errorf("failed to read version file: %w", err)
return nil, fmt.Errorf("failed to check device key from file: %w", err)
}

// TODO(burdiyan): this ended up being unnecessarily messy.
if ver == "" {
if _, err := s.init(); err != nil {
return nil, fmt.Errorf("failed to initialize data directory: %w", err)
}
} else {
if s.db != nil {
panic("BUG: db must not be set when starting a new store")
if device != nil {
if !kp.Wrapped().Equals(device) {
return nil, fmt.Errorf("provided device key (%s) doesn't match the stored one (%s)", device, kp.Wrapped())
}
}

if err := s.initDB(); err != nil {
return nil, err
}
s := &Store{
path: dataDir,
log: log,
kms: kms,
device: kp,
db: db,
}

// TODO(hm24): This should probably be called from the outside somehow,
// because we want to provide the feedback about the migration and reindexing
// to the frontend that would call the Daemon API polling until everything is ready.
if err := s.Migrate(); err != nil {
return nil, err
}

return s, nil
}

// Close the storage.
func (s *Store) Close() error {
return s.db.Close()
}

// DB returns the underlying database.
// Users must not close the database, because it's owned by the storage.
func (s *Store) DB() *sqlitex.Pool { return s.db }
Expand All @@ -124,26 +146,26 @@ func (s *Store) Migrate() error {
return nil
}

func (s *Store) sqlitePath() string {
return filepath.Join(s.path, dbDir, "db.sqlite")
}

// Device returns the device key pair.
func (s *Store) Device() core.KeyPair {
return s.device
}

func (s *Store) initDB() (err error) {
func newSQLite(path string) (*sqlitex.Pool, error) {
poolSize := int(float64(runtime.NumCPU()) / 2)
if poolSize == 0 {
poolSize = 2
}

// The database is owned by the store, and is closed when the store is closed.
s.db, err = OpenSQLite(s.sqlitePath(), 0, poolSize)
db, err := OpenSQLite(path, 0, poolSize)
if err != nil {
return err
return nil, err
}

return nil
return db, nil
}

func sqlitePath(baseDir string) string {
return filepath.Join(baseDir, dbDir, "db.sqlite")
}
57 changes: 11 additions & 46 deletions backend/daemon/storage2/storage_migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ var migrations = []migration{
}},
}

func desiredVersion() string {
ver := migrations[len(migrations)-1].Version
if ver == "" {
panic("BUG: couldn't find the desired storage schema version")
}

return ver
}

const (
keysDir = "keys"
dbDir = "db"
Expand All @@ -62,50 +71,6 @@ const (
versionFilename = "VERSION"
)

func (s *Store) init() (currentVersion string, err error) {
dirs := [...]string{
filepath.Join(s.path, keysDir),
filepath.Join(s.path, dbDir),
}

currentVersion = migrations[len(migrations)-1].Version
if currentVersion == "" {
panic("BUG: couldn't find current data directory version")
}

for _, d := range dirs {
if err := os.MkdirAll(d, 0700); err != nil {
return "", fmt.Errorf("failed to create dir %s: %w", d, err)
}
}

if s.db != nil {
panic("BUG: db must not be set when calling init()")
}

if err := s.initDB(); err != nil {
return "", err
}

if err := InitSQLiteSchema(s.db); err != nil {
return "", fmt.Errorf("failed to initialize SQLite database: %w", err)
}

if s.device.Wrapped() == nil {
panic("BUG: device key must be set when calling init()")
}

if err := writeDeviceKeyFile(s.path, s.device.Wrapped()); err != nil {
return "", err
}

if err := writeVersionFile(s.path, currentVersion); err != nil {
return "", fmt.Errorf("failed to write version file to init data directory: %w", err)
}

return currentVersion, nil
}

func (s *Store) migrate(currentVersion string) error {
desiredVersion := migrations[len(migrations)-1].Version
if currentVersion > desiredVersion {
Expand Down Expand Up @@ -175,7 +140,7 @@ func (s *Store) migrate(currentVersion string) error {

// Preparing the device key.
{
kp, err := loadDeviceKeyFromFile(s.path)
kp, err := readDeviceKeyFile(s.path)
if err != nil {
return fmt.Errorf("failed to load device key from file: %w", err)
}
Expand Down Expand Up @@ -214,7 +179,7 @@ func writeDeviceKeyFile(dir string, pk crypto.PrivKey) error {
return os.WriteFile(filepath.Join(dir, devicePrivateKeyPath), data, 0600)
}

func loadDeviceKeyFromFile(dir string) (kp core.KeyPair, err error) {
func readDeviceKeyFile(dir string) (kp core.KeyPair, err error) {
data, err := os.ReadFile(filepath.Join(dir, devicePrivateKeyPath))
if err != nil {
return kp, fmt.Errorf("failed to read the file: %w", err)
Expand Down

0 comments on commit fd7fbf5

Please sign in to comment.