From 8186e54cd2074575ed9160c944de2e1f3a0bb3eb Mon Sep 17 00:00:00 2001 From: sawka Date: Mon, 15 Aug 2022 18:42:25 -0700 Subject: [PATCH] add client table, ensure client id / public / private key --- cmd/main-server.go | 6 ++ db/migrations/000001_init.up.sql | 6 ++ pkg/sstore/sstore.go | 99 ++++++++++++++++++++++++++++++-- 3 files changed, 106 insertions(+), 5 deletions(-) diff --git a/cmd/main-server.go b/cmd/main-server.go index 9364fe5f2..6c5d359fd 100644 --- a/cmd/main-server.go +++ b/cmd/main-server.go @@ -456,6 +456,12 @@ func main() { fmt.Printf("[error] ensuring default session: %v\n", err) return } + userData, err := sstore.EnsureUserData(context.Background()) + if err != nil { + fmt.Printf("[error] ensuring user data: %v\n", err) + return + } + fmt.Printf("userid = %s\n", userData.UserId) err = remote.LoadRemotes(context.Background()) if err != nil { fmt.Printf("[error] loading remotes: %v\n", err) diff --git a/db/migrations/000001_init.up.sql b/db/migrations/000001_init.up.sql index 5aa14286e..18db0089a 100644 --- a/db/migrations/000001_init.up.sql +++ b/db/migrations/000001_init.up.sql @@ -1,3 +1,9 @@ +CREATE TABLE client ( + userid varchar(36) NOT NULL, + userpublickeybytes blob NOT NULL, + userprivatekeybytes blob NOT NULL +); + CREATE TABLE session ( sessionid varchar(36) PRIMARY KEY, name varchar(50) NOT NULL, diff --git a/pkg/sstore/sstore.go b/pkg/sstore/sstore.go index 479decf8c..18e903834 100644 --- a/pkg/sstore/sstore.go +++ b/pkg/sstore/sstore.go @@ -2,6 +2,10 @@ package sstore import ( "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" "database/sql/driver" "fmt" "log" @@ -10,6 +14,7 @@ import ( "sync" "time" + "github.com/google/uuid" "github.com/jmoiron/sqlx" "github.com/scripthaus-dev/mshell/pkg/base" "github.com/scripthaus-dev/mshell/pkg/packet" @@ -29,11 +34,21 @@ const DefaultScreenWindowName = "w1" const DefaultCwd = "~" -const CmdStatusRunning = "running" -const CmdStatusDetached = "detached" -const CmdStatusError = "error" -const CmdStatusDone = "done" -const CmdStatusHangup = "hangup" +const ( + CmdStatusRunning = "running" + CmdStatusDetached = "detached" + CmdStatusError = "error" + CmdStatusDone = "done" + CmdStatusHangup = "hangup" +) + +const ( + ShareModeLocal = "local" + ShareModePrivate = "private" + ShareModeView = "view" + ShareModeShared = "shared" + ShareModeSharedView = "shared-view" +) var globalDBLock = &sync.Mutex{} var globalDB *sqlx.DB @@ -56,6 +71,14 @@ func GetDB(ctx context.Context) (*sqlx.DB, error) { return globalDB, globalDBErr } +type UserData struct { + UserId string `json:"userid"` + UserPrivateKeyBytes []byte `json:"-"` + UserPublicKeyBytes []byte `json:"-"` + UserPrivateKey *ecdsa.PrivateKey + UserPublicKey *ecdsa.PublicKey +} + type SessionType struct { SessionId string `json:"sessionid"` Name string `json:"name"` @@ -413,3 +436,69 @@ func EnsureDefaultSession(ctx context.Context) (*SessionType, error) { } return GetSessionByName(ctx, DefaultSessionName) } + +func createUserData(tx *TxWrap) error { + userId := uuid.New().String() + curve := elliptic.P384() + pkey, err := ecdsa.GenerateKey(curve, rand.Reader) + if err != nil { + return fmt.Errorf("generating P-834 key: %w", err) + } + pkBytes, err := x509.MarshalECPrivateKey(pkey) + if err != nil { + return fmt.Errorf("marshaling (pkcs8) private key bytes: %w", err) + } + pubBytes, err := x509.MarshalPKIXPublicKey(&pkey.PublicKey) + if err != nil { + return fmt.Errorf("marshaling (pkix) public key bytes: %w", err) + } + query := `INSERT INTO client (userid, userpublickeybytes, userprivatekeybytes) VALUES (?, ?, ?)` + tx.ExecWrap(query, userId, pubBytes, pkBytes) + fmt.Printf("create new userid[%s] with public/private keypair\n", userId) + return nil +} + +func EnsureUserData(ctx context.Context) (*UserData, error) { + var rtn UserData + err := WithTx(ctx, func(tx *TxWrap) error { + query := `SELECT count(*) FROM client` + count := tx.GetInt(query) + if count > 1 { + return fmt.Errorf("invalid client database, multiple (%d) rows in client table", count) + } + if count == 0 { + createErr := createUserData(tx) + if createErr != nil { + return createErr + } + } + found := tx.GetWrap(&rtn, "SELECT * FROM client") + if !found { + return fmt.Errorf("invalid client data") + } + return nil + }) + if err != nil { + return nil, err + } + if rtn.UserId == "" { + return nil, fmt.Errorf("invalid client data (no userid)") + } + if len(rtn.UserPrivateKeyBytes) == 0 || len(rtn.UserPublicKeyBytes) == 0 { + return nil, fmt.Errorf("invalid client data (no public/private keypair)") + } + rtn.UserPrivateKey, err = x509.ParseECPrivateKey(rtn.UserPrivateKeyBytes) + if err != nil { + return nil, fmt.Errorf("invalid client data, cannot parse private key: %w", err) + } + pubKey, err := x509.ParsePKIXPublicKey(rtn.UserPublicKeyBytes) + if err != nil { + return nil, fmt.Errorf("invalid client data, cannot parse public key: %w", err) + } + var ok bool + rtn.UserPublicKey, ok = pubKey.(*ecdsa.PublicKey) + if !ok { + return nil, fmt.Errorf("invalid client data, wrong public key type: %T", pubKey) + } + return &rtn, nil +}