waveterm/pkg/sstore/sstore.go

641 lines
18 KiB
Go
Raw Normal View History

2022-06-13 20:11:56 +02:00
package sstore
import (
2022-07-01 21:17:19 +02:00
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"database/sql/driver"
"fmt"
2022-07-01 23:07:13 +02:00
"log"
2022-08-17 00:08:28 +02:00
"os"
"os/user"
2022-07-01 02:02:19 +02:00
"path"
2022-06-21 06:57:23 +02:00
"sync"
2022-06-13 20:11:56 +02:00
"time"
"github.com/google/uuid"
"github.com/jmoiron/sqlx"
2022-07-01 21:17:19 +02:00
"github.com/scripthaus-dev/mshell/pkg/base"
"github.com/scripthaus-dev/mshell/pkg/packet"
2022-07-01 02:02:19 +02:00
"github.com/scripthaus-dev/sh2-server/pkg/scbase"
_ "github.com/mattn/go-sqlite3"
2022-06-13 20:11:56 +02:00
)
const LineTypeCmd = "cmd"
const LineTypeText = "text"
const DBFileName = "sh2.db"
2022-07-01 02:02:19 +02:00
2022-07-01 21:17:19 +02:00
const DefaultSessionName = "default"
const DefaultWindowName = "default"
const LocalRemoteName = "local"
const DefaultScreenWindowName = "w1"
2022-07-01 21:17:19 +02:00
2022-07-01 23:07:13 +02:00
const DefaultCwd = "~"
const (
CmdStatusRunning = "running"
CmdStatusDetached = "detached"
CmdStatusError = "error"
CmdStatusDone = "done"
CmdStatusHangup = "hangup"
)
const (
ShareModeLocal = "local"
ShareModePrivate = "private"
ShareModeView = "view"
ShareModeShared = "shared"
)
const (
ConnectModeStartup = "startup"
ConnectModeAuto = "auto"
ConnectModeManual = "manual"
)
const (
RemoteTypeSsh = "ssh"
)
2022-07-01 21:17:19 +02:00
var globalDBLock = &sync.Mutex{}
var globalDB *sqlx.DB
var globalDBErr error
func GetSessionDBName() string {
2022-07-01 02:02:19 +02:00
scHome := scbase.GetScHomeDir()
return path.Join(scHome, DBFileName)
}
2022-06-13 20:11:56 +02:00
func GetDB(ctx context.Context) (*sqlx.DB, error) {
if IsTxWrapContext(ctx) {
return nil, fmt.Errorf("cannot call GetDB from within a running transaction")
}
2022-07-01 21:17:19 +02:00
globalDBLock.Lock()
defer globalDBLock.Unlock()
if globalDB == nil && globalDBErr == nil {
dbName := GetSessionDBName()
globalDB, globalDBErr = sqlx.Open("sqlite3", fmt.Sprintf("file:%s?cache=shared&mode=rwc&_journal_mode=WAL&_busy_timeout=5000", dbName))
if globalDBErr != nil {
globalDBErr = fmt.Errorf("opening db[%s]: %w", dbName, globalDBErr)
}
}
2022-07-01 21:17:19 +02:00
return globalDB, globalDBErr
}
type UserData struct {
UserId string `json:"userid"`
UserPrivateKeyBytes []byte `json:"-"`
UserPublicKeyBytes []byte `json:"-"`
UserPrivateKey *ecdsa.PrivateKey
UserPublicKey *ecdsa.PublicKey
}
2022-06-21 06:57:23 +02:00
type SessionType struct {
SessionId string `json:"sessionid"`
Name string `json:"name"`
SessionIdx int64 `json:"sessionidx"`
ActiveScreenId string `json:"activescreenid"`
OwnerUserId string `json:"owneruserid"`
ShareMode string `json:"sharemode"`
AccessKey string `json:"-"`
NotifyNum int64 `json:"notifynum"`
Screens []*ScreenType `json:"screens"`
Remotes []*RemoteInstance `json:"remotes"`
2022-07-15 03:39:40 +02:00
// only for updates
Remove bool `json:"remove,omitempty"`
Full bool `json:"full,omitempty"`
2022-07-12 23:27:16 +02:00
}
type WindowOptsType struct {
}
func (opts *WindowOptsType) Scan(val interface{}) error {
return quickScanJson(opts, val)
}
func (opts WindowOptsType) Value() (driver.Value, error) {
2022-07-12 23:27:16 +02:00
return quickValueJson(opts)
2022-07-05 07:18:01 +02:00
}
type WindowShareOptsType struct {
}
func (opts *WindowShareOptsType) Scan(val interface{}) error {
return quickScanJson(opts, val)
}
func (opts WindowShareOptsType) Value() (driver.Value, error) {
return quickValueJson(opts)
}
2022-07-05 07:18:01 +02:00
type WindowType struct {
SessionId string `json:"sessionid"`
WindowId string `json:"windowid"`
CurRemote string `json:"curremote"`
WinOpts WindowOptsType `json:"winopts"`
OwnerUserId string `json:"owneruserid"`
ShareMode string `json:"sharemode"`
ShareOpts WindowShareOptsType `json:"shareopts"`
Lines []*LineType `json:"lines"`
Cmds []*CmdType `json:"cmds"`
Remotes []*RemoteInstance `json:"remotes"`
2022-07-15 03:39:40 +02:00
// only for updates
Remove bool `json:"remove,omitempty"`
}
type ScreenOptsType struct {
TabColor string `json:"tabcolor"`
}
func (opts *ScreenOptsType) Scan(val interface{}) error {
return quickScanJson(opts, val)
}
func (opts ScreenOptsType) Value() (driver.Value, error) {
return quickValueJson(opts)
2022-07-12 23:27:16 +02:00
}
type ScreenType struct {
SessionId string `json:"sessionid"`
ScreenId string `json:"screenid"`
ScreenIdx int64 `json:"screenidx"`
Name string `json:"name"`
ActiveWindowId string `json:"activewindowid"`
ScreenOpts ScreenOptsType `json:"screenopts"`
OwnerUserId string `json:"owneruserid"`
ShareMode string `json:"sharemode"`
Windows []*ScreenWindowType `json:"windows"`
2022-07-15 03:39:40 +02:00
// only for updates
Remove bool `json:"remove,omitempty"`
Full bool `json:"full,omitempty"`
2022-07-12 23:27:16 +02:00
}
const (
LayoutFull = "full"
)
2022-07-12 23:27:16 +02:00
type LayoutType struct {
Type string `json:"type"`
Parent string `json:"parent,omitempty"`
ZIndex int64 `json:"zindex,omitempty"`
Float bool `json:"float,omitempty"`
Top string `json:"top,omitempty"`
Bottom string `json:"bottom,omitempty"`
Left string `json:"left,omitempty"`
Right string `json:"right,omitempty"`
Width string `json:"width,omitempty"`
Height string `json:"height,omitempty"`
2022-07-12 23:27:16 +02:00
}
func (l *LayoutType) Scan(val interface{}) error {
return quickScanJson(l, val)
}
func (l LayoutType) Value() (driver.Value, error) {
2022-07-12 23:27:16 +02:00
return quickValueJson(l)
}
type ScreenWindowType struct {
SessionId string `json:"sessionid"`
ScreenId string `json:"screenid"`
WindowId string `json:"windowid"`
Name string `json:"name"`
2022-07-12 23:27:16 +02:00
Layout LayoutType `json:"layout"`
2022-07-15 03:39:40 +02:00
// only for updates
Remove bool `json:"remove,omitempty"`
2022-07-12 22:50:44 +02:00
}
type HistoryItemType struct {
2022-08-11 21:07:41 +02:00
HistoryId string `json:"historyid"`
Ts int64 `json:"ts"`
UserId string `json:"userid"`
SessionId string `json:"sessionid"`
ScreenId string `json:"screenid"`
WindowId string `json:"windowid"`
LineId string `json:"lineid"`
2022-08-12 08:45:15 +02:00
HadError bool `json:"haderror"`
2022-08-11 21:07:41 +02:00
CmdId string `json:"cmdid"`
CmdStr string `json:"cmdstr"`
// only for updates
Remove bool `json:"remove"`
}
type RemoteState struct {
2022-08-23 01:26:44 +02:00
Cwd string `json:"cwd"`
Env0 []byte `json:"env0"` // "env -0" format
}
func (s *RemoteState) Scan(val interface{}) error {
2022-07-12 23:27:16 +02:00
return quickScanJson(s, val)
}
func (s RemoteState) Value() (driver.Value, error) {
2022-07-12 23:27:16 +02:00
return quickValueJson(s)
}
2022-07-07 09:10:37 +02:00
type TermOpts struct {
Rows int64 `json:"rows"`
Cols int64 `json:"cols"`
FlexRows bool `json:"flexrows,omitempty"`
MaxPtySize int64 `json:"maxptysize,omitempty"`
2022-07-07 09:10:37 +02:00
}
func (opts *TermOpts) Scan(val interface{}) error {
2022-07-12 23:27:16 +02:00
return quickScanJson(opts, val)
2022-07-07 09:10:37 +02:00
}
func (opts TermOpts) Value() (driver.Value, error) {
2022-07-12 23:27:16 +02:00
return quickValueJson(opts)
2022-07-07 09:10:37 +02:00
}
type RemoteInstance struct {
RIId string `json:"riid"`
Name string `json:"name"`
SessionId string `json:"sessionid"`
WindowId string `json:"windowid"`
2022-08-11 03:33:32 +02:00
RemoteId string `json:"remoteid"`
SessionScope bool `json:"sessionscope"`
State RemoteState `json:"state"`
2022-08-11 03:33:32 +02:00
// only for updates
Remove bool `json:"remove,omitempty"`
2022-06-21 06:57:23 +02:00
}
2022-06-13 20:11:56 +02:00
type LineType struct {
2022-06-17 00:51:41 +02:00
SessionId string `json:"sessionid"`
WindowId string `json:"windowid"`
LineId string `json:"lineid"`
2022-06-13 20:11:56 +02:00
Ts int64 `json:"ts"`
UserId string `json:"userid"`
LineType string `json:"linetype"`
Text string `json:"text,omitempty"`
CmdId string `json:"cmdid,omitempty"`
Remove bool `json:"remove,omitempty"`
}
type SSHOpts struct {
SSHHost string `json:"sshhost"`
SSHOptsStr string `json:"sshopts"`
SSHIdentity string `json:"sshidentity"`
SSHUser string `json:"sshuser"`
}
type RemoteOptsType struct {
Color string `json:"color"`
}
func (opts *RemoteOptsType) Scan(val interface{}) error {
return quickScanJson(opts, val)
}
func (opts RemoteOptsType) Value() (driver.Value, error) {
return quickValueJson(opts)
}
type RemoteType struct {
2022-08-17 00:08:28 +02:00
RemoteId string `json:"remoteid"`
PhysicalId string `json:"physicalid"`
RemoteType string `json:"remotetype"`
RemoteAlias string `json:"remotealias"`
RemoteCanonicalName string `json:"remotecanonicalname"`
RemoteSudo bool `json:"remotesudo"`
RemoteUser string `json:"remoteuser"`
RemoteHost string `json:"remotehost"`
ConnectMode string `json:"connectmode"`
2022-08-17 00:08:28 +02:00
InitPk *packet.InitPacketType `json:"inipk"`
SSHOpts *SSHOpts `json:"sshopts"`
RemoteOpts *RemoteOptsType `json:"remoteopts"`
2022-08-17 00:08:28 +02:00
LastConnectTs int64 `json:"lastconnectts"`
}
func (r *RemoteType) GetName() string {
if r.RemoteAlias != "" {
return r.RemoteAlias
}
2022-08-17 00:08:28 +02:00
if r.RemoteUser == "" {
return r.RemoteHost
}
2022-08-17 00:08:28 +02:00
return fmt.Sprintf("%s@%s", r.RemoteUser, r.RemoteHost)
}
type CmdType struct {
2022-07-07 09:10:37 +02:00
SessionId string `json:"sessionid"`
CmdId string `json:"cmdid"`
RemoteId string `json:"remoteid"`
CmdStr string `json:"cmdstr"`
RemoteState RemoteState `json:"remotestate"`
TermOpts TermOpts `json:"termopts"`
Status string `json:"status"`
StartPk *packet.CmdStartPacketType `json:"startpk"`
DonePk *packet.CmdDonePacketType `json:"donepk"`
2022-07-12 22:50:44 +02:00
UsedRows int64 `json:"usedrows"`
2022-07-07 09:10:37 +02:00
RunOut []packet.PacketType `json:"runout"`
Remove bool `json:"remove"`
2022-07-07 09:10:37 +02:00
}
func (r *RemoteType) ToMap() map[string]interface{} {
rtn := make(map[string]interface{})
rtn["remoteid"] = r.RemoteId
2022-08-17 00:08:28 +02:00
rtn["physicalid"] = r.PhysicalId
rtn["remotetype"] = r.RemoteType
2022-08-17 00:08:28 +02:00
rtn["remotealias"] = r.RemoteAlias
rtn["remotecanonicalname"] = r.RemoteCanonicalName
rtn["remotesudo"] = r.RemoteSudo
rtn["remoteuser"] = r.RemoteUser
rtn["remotehost"] = r.RemoteHost
rtn["connectmode"] = r.ConnectMode
rtn["initpk"] = quickJson(r.InitPk)
rtn["sshopts"] = quickJson(r.SSHOpts)
rtn["remoteopts"] = quickJson(r.RemoteOpts)
rtn["lastconnectts"] = r.LastConnectTs
return rtn
}
func RemoteFromMap(m map[string]interface{}) *RemoteType {
if len(m) == 0 {
return nil
2022-07-07 09:10:37 +02:00
}
var r RemoteType
quickSetStr(&r.RemoteId, m, "remoteid")
2022-08-17 00:08:28 +02:00
quickSetStr(&r.PhysicalId, m, "physicalid")
quickSetStr(&r.RemoteType, m, "remotetype")
2022-08-17 00:08:28 +02:00
quickSetStr(&r.RemoteAlias, m, "remotealias")
quickSetStr(&r.RemoteCanonicalName, m, "remotecanonicalname")
quickSetBool(&r.RemoteSudo, m, "remotesudo")
quickSetStr(&r.RemoteUser, m, "remoteuser")
quickSetStr(&r.RemoteHost, m, "remotehost")
quickSetStr(&r.ConnectMode, m, "connectmode")
quickSetJson(&r.InitPk, m, "initpk")
quickSetJson(&r.SSHOpts, m, "sshopts")
quickSetJson(&r.RemoteOpts, m, "remoteopts")
quickSetInt64(&r.LastConnectTs, m, "lastconnectts")
return &r
2022-07-07 09:10:37 +02:00
}
func (cmd *CmdType) ToMap() map[string]interface{} {
rtn := make(map[string]interface{})
rtn["sessionid"] = cmd.SessionId
rtn["cmdid"] = cmd.CmdId
rtn["remoteid"] = cmd.RemoteId
rtn["cmdstr"] = cmd.CmdStr
rtn["remotestate"] = quickJson(cmd.RemoteState)
rtn["termopts"] = quickJson(cmd.TermOpts)
rtn["status"] = cmd.Status
rtn["startpk"] = quickJson(cmd.StartPk)
rtn["donepk"] = quickJson(cmd.DonePk)
rtn["runout"] = quickJson(cmd.RunOut)
2022-07-12 22:50:44 +02:00
rtn["usedrows"] = cmd.UsedRows
2022-07-07 09:10:37 +02:00
return rtn
}
func CmdFromMap(m map[string]interface{}) *CmdType {
if len(m) == 0 {
return nil
}
2022-07-07 09:10:37 +02:00
var cmd CmdType
quickSetStr(&cmd.SessionId, m, "sessionid")
quickSetStr(&cmd.CmdId, m, "cmdid")
quickSetStr(&cmd.RemoteId, m, "remoteid")
quickSetStr(&cmd.CmdStr, m, "cmdstr")
quickSetJson(&cmd.RemoteState, m, "remotestate")
quickSetJson(&cmd.TermOpts, m, "termopts")
quickSetStr(&cmd.Status, m, "status")
quickSetJson(&cmd.StartPk, m, "startpk")
quickSetJson(&cmd.DonePk, m, "donepk")
quickSetJson(&cmd.RunOut, m, "runout")
2022-07-12 22:50:44 +02:00
quickSetInt64(&cmd.UsedRows, m, "usedrows")
2022-07-07 09:10:37 +02:00
return &cmd
2022-06-13 20:11:56 +02:00
}
func makeNewLineCmd(sessionId string, windowId string, userId string, cmdId string) *LineType {
2022-06-13 20:11:56 +02:00
rtn := &LineType{}
2022-06-17 00:51:41 +02:00
rtn.SessionId = sessionId
rtn.WindowId = windowId
rtn.LineId = uuid.New().String()
2022-06-13 20:11:56 +02:00
rtn.Ts = time.Now().UnixMilli()
2022-07-05 19:51:47 +02:00
rtn.UserId = userId
2022-06-13 20:11:56 +02:00
rtn.LineType = LineTypeCmd
rtn.CmdId = cmdId
2022-06-13 20:11:56 +02:00
return rtn
}
2022-07-05 19:51:47 +02:00
func makeNewLineText(sessionId string, windowId string, userId string, text string) *LineType {
2022-06-13 20:11:56 +02:00
rtn := &LineType{}
2022-06-17 00:51:41 +02:00
rtn.SessionId = sessionId
rtn.WindowId = windowId
rtn.LineId = uuid.New().String()
2022-06-13 20:11:56 +02:00
rtn.Ts = time.Now().UnixMilli()
2022-07-05 19:51:47 +02:00
rtn.UserId = userId
2022-06-13 20:11:56 +02:00
rtn.LineType = LineTypeText
rtn.Text = text
return rtn
}
2022-06-21 06:57:23 +02:00
2022-07-05 19:51:47 +02:00
func AddCommentLine(ctx context.Context, sessionId string, windowId string, userId string, commentText string) (*LineType, error) {
rtnLine := makeNewLineText(sessionId, windowId, userId, commentText)
err := InsertLine(ctx, rtnLine, nil)
2022-07-05 19:51:47 +02:00
if err != nil {
return nil, err
}
return rtnLine, nil
}
func AddCmdLine(ctx context.Context, sessionId string, windowId string, userId string, cmd *CmdType) (*LineType, error) {
rtnLine := makeNewLineCmd(sessionId, windowId, userId, cmd.CmdId)
err := InsertLine(ctx, rtnLine, cmd)
2022-07-05 19:51:47 +02:00
if err != nil {
return nil, err
}
return rtnLine, nil
}
2022-07-01 21:17:19 +02:00
func EnsureLocalRemote(ctx context.Context) error {
physicalId, err := base.GetRemoteId()
2022-07-01 21:17:19 +02:00
if err != nil {
return fmt.Errorf("getting local physical remoteid: %w", err)
2022-07-01 21:17:19 +02:00
}
remote, err := GetRemoteByPhysicalId(ctx, physicalId)
2022-07-01 21:17:19 +02:00
if err != nil {
return fmt.Errorf("getting remote[%s] from db: %w", physicalId, err)
2022-07-01 21:17:19 +02:00
}
if remote != nil {
return nil
}
2022-08-17 00:08:28 +02:00
hostName, err := os.Hostname()
if err != nil {
return fmt.Errorf("getting hostname: %w", err)
}
user, err := user.Current()
if err != nil {
return fmt.Errorf("getting user: %w", err)
}
2022-07-01 21:17:19 +02:00
// create the local remote
localRemote := &RemoteType{
RemoteId: uuid.New().String(),
PhysicalId: physicalId,
RemoteType: RemoteTypeSsh,
2022-08-17 00:08:28 +02:00
RemoteAlias: LocalRemoteName,
RemoteCanonicalName: fmt.Sprintf("%s@%s", user.Username, hostName),
RemoteSudo: false,
RemoteUser: user.Username,
RemoteHost: hostName,
ConnectMode: ConnectModeStartup,
2022-07-01 21:17:19 +02:00
}
err = InsertRemote(ctx, localRemote)
if err != nil {
return err
}
2022-08-17 00:08:28 +02:00
log.Printf("[db] added remote '%s', id=%s\n", localRemote.GetName(), localRemote.RemoteId)
2022-07-01 23:07:13 +02:00
return nil
}
func AddTest01Remote(ctx context.Context) error {
remote, err := GetRemoteByAlias(ctx, "test01")
if err != nil {
return fmt.Errorf("getting remote[test01] from db: %w", err)
}
if remote != nil {
return nil
}
testRemote := &RemoteType{
RemoteId: uuid.New().String(),
RemoteType: RemoteTypeSsh,
RemoteAlias: "test01",
RemoteCanonicalName: "ubuntu@test01.ec2",
RemoteSudo: false,
RemoteUser: "ubuntu",
RemoteHost: "test01.ec2",
SSHOpts: &SSHOpts{
SSHHost: "test01.ec2",
SSHUser: "ubuntu",
SSHIdentity: "/Users/mike/aws/mfmt.pem",
},
ConnectMode: ConnectModeStartup,
}
err = InsertRemote(ctx, testRemote)
if err != nil {
return err
}
log.Printf("[db] added remote '%s', id=%s\n", testRemote.GetName(), testRemote.RemoteId)
return nil
}
func AddTest02Remote(ctx context.Context) error {
remote, err := GetRemoteByAlias(ctx, "test2")
if err != nil {
return fmt.Errorf("getting remote[test01] from db: %w", err)
}
if remote != nil {
return nil
}
testRemote := &RemoteType{
RemoteId: uuid.New().String(),
RemoteType: RemoteTypeSsh,
RemoteAlias: "test2",
RemoteCanonicalName: "test2@test01.ec2",
RemoteSudo: false,
RemoteUser: "test2",
RemoteHost: "test01.ec2",
SSHOpts: &SSHOpts{
SSHHost: "test01.ec2",
SSHUser: "test2",
},
ConnectMode: ConnectModeStartup,
}
err = InsertRemote(ctx, testRemote)
if err != nil {
return err
}
log.Printf("[db] added remote '%s', id=%s\n", testRemote.GetName(), testRemote.RemoteId)
return nil
}
2022-07-01 23:45:33 +02:00
func EnsureDefaultSession(ctx context.Context) (*SessionType, error) {
2022-07-01 23:07:13 +02:00
session, err := GetSessionByName(ctx, DefaultSessionName)
if err != nil {
2022-07-01 23:45:33 +02:00
return nil, err
2022-07-01 23:07:13 +02:00
}
if session != nil {
2022-07-01 23:45:33 +02:00
return session, nil
2022-07-01 23:07:13 +02:00
}
_, err = InsertSessionWithName(ctx, DefaultSessionName, true)
2022-07-01 23:07:13 +02:00
if err != nil {
2022-07-01 23:45:33 +02:00
return nil, err
2022-07-01 23:07:13 +02:00
}
2022-07-01 23:45:33 +02:00
return GetSessionByName(ctx, DefaultSessionName)
2022-07-01 21:17:19 +02:00
}
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
}