Encrypt restore token
This commit is contained in:
parent
abd9fd4773
commit
16dc47b570
11 changed files with 137 additions and 49 deletions
|
|
@ -55,14 +55,14 @@ const (
|
|||
|
||||
type ControllerInfo struct {
|
||||
Name string
|
||||
Init func(bool) (Controller, error)
|
||||
Init func() (Controller, error)
|
||||
|
||||
priority int
|
||||
}
|
||||
|
||||
var Controllers []ControllerInfo
|
||||
|
||||
func RegisterController(name string, init func(bool) (Controller, error), priority int) {
|
||||
func RegisterController(name string, init func() (Controller, error), priority int) {
|
||||
Controllers = append(Controllers, ControllerInfo{name, init, priority})
|
||||
sort.SliceStable(Controllers, func(i, j int) bool {
|
||||
return Controllers[i].priority < Controllers[j].priority
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ func init() {
|
|||
RegisterController("null", InitNullController, 1000)
|
||||
}
|
||||
|
||||
func InitNullController(saveRestoreToken bool) (Controller, error) {
|
||||
func InitNullController() (Controller, error) {
|
||||
return &nullController{}, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,12 +22,20 @@
|
|||
package inputcontrol
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/godbus/dbus/v5"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -55,7 +63,7 @@ func init() {
|
|||
RegisterController("RemoteDesktop portal", InitPortalController, 1)
|
||||
}
|
||||
|
||||
func InitPortalController(saveRestoreToken bool) (Controller, error) {
|
||||
func InitPortalController() (Controller, error) {
|
||||
bus, err := dbus.SessionBusPrivate()
|
||||
if err != nil {
|
||||
return nil, &UnsupportedPlatformError{err}
|
||||
|
|
@ -81,25 +89,26 @@ func InitPortalController(saveRestoreToken bool) (Controller, error) {
|
|||
if err != nil {
|
||||
return nil, &UnsupportedPlatformError{err}
|
||||
}
|
||||
supportsRestoreTokens := version.Value().(uint32) >= 2
|
||||
var restoreTokenFilePath string
|
||||
var cacheDirectory string
|
||||
var restoreToken string
|
||||
if supportsRestoreTokens {
|
||||
cacheDirectory, err = os.UserCacheDir()
|
||||
if err != nil {
|
||||
log.Printf("Cannot get user cache directory: %s. Therefore cannot get restore token file path in order to read or save the restore token.\n", err)
|
||||
} else {
|
||||
restoreTokenFilePath = filepath.Join(cacheDirectory, "remote_touchpad_portals_restore_token")
|
||||
restoreTokenBytes, err := os.ReadFile(restoreTokenFilePath)
|
||||
if err != nil {
|
||||
log.Printf("Failed to read restore token file: %s\n", err)
|
||||
} else {
|
||||
restoreToken = string(restoreTokenBytes)
|
||||
}
|
||||
restoreTokenStore, err := func() (*secretStore, error) {
|
||||
if version.Value().(uint32) < 2 {
|
||||
return nil, nil
|
||||
}
|
||||
} else {
|
||||
log.Println("Portals implementation does not support restore tokens")
|
||||
cacheDirectory, err := os.UserCacheDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := os.MkdirAll(cacheDirectory, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secret, err := retrieveSecret(bus)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newSecretStore(secret,
|
||||
filepath.Join(cacheDirectory, "remote-touchpad.portal-restore-token.bin"))
|
||||
}()
|
||||
if err != nil {
|
||||
log.Printf("Skipping restore token: %v", err)
|
||||
}
|
||||
availableDeviceTypesV, err := remoteDesktop.GetProperty(
|
||||
"org.freedesktop.portal.RemoteDesktop.AvailableDeviceTypes")
|
||||
|
|
@ -148,13 +157,15 @@ func InitPortalController(saveRestoreToken bool) (Controller, error) {
|
|||
sessionHandle := dbus.ObjectPath(sessionHandleS)
|
||||
inVardict = make(map[string]dbus.Variant)
|
||||
inVardict["types"] = dbus.MakeVariant(deviceKeyboard | devicePointer)
|
||||
if supportsRestoreTokens {
|
||||
if restoreToken != "" {
|
||||
inVardict["restore_token"] = dbus.MakeVariant(restoreToken)
|
||||
}
|
||||
if saveRestoreToken {
|
||||
inVardict["persist_mode"] = dbus.MakeVariant(untilRevoked)
|
||||
if restoreTokenStore != nil {
|
||||
if restoreToken, err := restoreTokenStore.Load(); err != nil {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
log.Printf("Failed to load restore token: %v", err)
|
||||
}
|
||||
} else if len(restoreToken) > 0 {
|
||||
inVardict["restore_token"] = dbus.MakeVariant(string(restoreToken))
|
||||
}
|
||||
inVardict["persist_mode"] = dbus.MakeVariant(untilRevoked)
|
||||
}
|
||||
result, outVardict, err = getResponse(bus, remoteDesktop,
|
||||
"org.freedesktop.portal.RemoteDesktop.SelectDevices", 0, sessionHandle, inVardict)
|
||||
|
|
@ -174,20 +185,9 @@ func InitPortalController(saveRestoreToken bool) (Controller, error) {
|
|||
if result != 0 {
|
||||
return nil, errors.New("keyboard or pointer access denied")
|
||||
}
|
||||
if supportsRestoreTokens && saveRestoreToken {
|
||||
restoreToken, ok := outVardict["restore_token"].Value().(string)
|
||||
if !ok {
|
||||
log.Println("Failed to get new restore token")
|
||||
} else if restoreTokenFilePath != "" {
|
||||
err := os.MkdirAll(cacheDirectory, 0700)
|
||||
if err != nil {
|
||||
log.Printf("Failed to create cache directory for restore token: %s\n", err)
|
||||
} else {
|
||||
err := os.WriteFile(restoreTokenFilePath, []byte(restoreToken), 0600)
|
||||
if err != nil {
|
||||
log.Printf("Failed to write restore token: %s\n", err)
|
||||
}
|
||||
}
|
||||
if restoreToken, _ := outVardict["restore_token"].Value().(string); restoreTokenStore != nil {
|
||||
if err := restoreTokenStore.Store([]byte(restoreToken)); err != nil {
|
||||
log.Printf("Failed to store restore token: %v", err)
|
||||
}
|
||||
}
|
||||
devicesV, ok := outVardict["devices"]
|
||||
|
|
@ -208,6 +208,84 @@ func InitPortalController(saveRestoreToken bool) (Controller, error) {
|
|||
sessionHandle: sessionHandle}, nil
|
||||
}
|
||||
|
||||
func retrieveSecret(bus *dbus.Conn) ([]byte, error) {
|
||||
portalDesktop := bus.Object("org.freedesktop.portal.Desktop",
|
||||
"/org/freedesktop/portal/desktop")
|
||||
secretReader, secretWriter, err := os.Pipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer secretReader.Close()
|
||||
defer secretWriter.Close()
|
||||
if result, _, err := getResponse(bus, portalDesktop,
|
||||
"org.freedesktop.portal.Secret.RetrieveSecret", 0,
|
||||
dbus.UnixFD(secretWriter.Fd()),
|
||||
map[string]dbus.Variant{},
|
||||
); err != nil {
|
||||
return nil, err
|
||||
} else if result != 0 {
|
||||
return nil, fmt.Errorf("calling 'RetrieveSecret' failed (%v)", result)
|
||||
}
|
||||
if err := secretWriter.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secret, err := io.ReadAll(secretReader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(secret) < 16 {
|
||||
return nil, fmt.Errorf("'RetrieveSecret' returned too few bytes (%d)", len(secret))
|
||||
}
|
||||
return secret, err
|
||||
}
|
||||
|
||||
type secretStore struct {
|
||||
aesgcm cipher.AEAD
|
||||
filename string
|
||||
}
|
||||
|
||||
func newSecretStore(key []byte, filename string) (*secretStore, error) {
|
||||
hkdf := hkdf.New(sha256.New, key, nil, nil)
|
||||
derivedKey := make([]byte, 32)
|
||||
if _, err := io.ReadFull(hkdf, derivedKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
block, err := aes.NewCipher(derivedKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &secretStore{
|
||||
aesgcm: aesgcm,
|
||||
filename: filename,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *secretStore) Load() ([]byte, error) {
|
||||
data, err := os.ReadFile(s.filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(data) < s.aesgcm.NonceSize() {
|
||||
return nil, errors.New("invalid ciphertext")
|
||||
}
|
||||
nonce := data[:s.aesgcm.NonceSize()]
|
||||
ciphertext := data[len(nonce):]
|
||||
return s.aesgcm.Open(nil, nonce, ciphertext, nil)
|
||||
}
|
||||
|
||||
func (s *secretStore) Store(data []byte) error {
|
||||
nonce := make([]byte, s.aesgcm.NonceSize())
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return err
|
||||
}
|
||||
ciphertext := s.aesgcm.Seal(nil, nonce, data, nil)
|
||||
return os.WriteFile(s.filename, slices.Concat(nonce, ciphertext), 0600)
|
||||
}
|
||||
|
||||
func getResponse(bus *dbus.Conn, object dbus.BusObject, method string,
|
||||
flags dbus.Flags, args ...interface{}) (uint32, map[string]dbus.Variant, error) {
|
||||
ch := make(chan *dbus.Signal, 512)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ func init() {
|
|||
RegisterController("uinput", InitUinputController, 10)
|
||||
}
|
||||
|
||||
func InitUinputController(saveRestoreToken bool) (Controller, error) {
|
||||
func InitUinputController() (Controller, error) {
|
||||
keymapName, keymapSet := os.LookupEnv("REMOTE_TOUCHPAD_UINPUT_KEYMAP")
|
||||
if !keymapSet {
|
||||
keymapName = "defkeymap"
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ func init() {
|
|||
RegisterController("Windows", InitWindowsController, 0)
|
||||
}
|
||||
|
||||
func InitWindowsController(saveRestoreToken bool) (Controller, error) {
|
||||
func InitWindowsController() (Controller, error) {
|
||||
p := &windowsController{}
|
||||
if err := sendInputProc.Find(); err != nil {
|
||||
return nil, &UnsupportedPlatformError{err}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ func init() {
|
|||
RegisterController("X11", InitX11Controller, 0)
|
||||
}
|
||||
|
||||
func InitX11Controller(saveRestoreToken bool) (Controller, error) {
|
||||
func InitX11Controller() (Controller, error) {
|
||||
display := C.XOpenDisplay(nil)
|
||||
if display == nil {
|
||||
return nil, &UnsupportedPlatformError{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue