Skip to content

Commit

Permalink
Make memguard fully optional
Browse files Browse the repository at this point in the history
  • Loading branch information
quexten committed Dec 22, 2023
1 parent 3a1e47f commit 189b151
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 127 deletions.
3 changes: 1 addition & 2 deletions agent/bitwarden/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"strings"
"time"

"github.com/awnumar/memguard"
"github.com/quexten/goldwarden/agent/bitwarden/crypto"
"github.com/quexten/goldwarden/agent/bitwarden/twofactor"
"github.com/quexten/goldwarden/agent/config"
Expand Down Expand Up @@ -81,7 +80,7 @@ func LoginWithMasterpassword(ctx context.Context, email string, cfg *config.Conf
return LoginResponseToken{}, crypto.MasterKey{}, "", err
}

masterKey, err = crypto.DeriveMasterKey(*memguard.NewBufferFromBytes([]byte(strings.Clone(password))), email, crypto.KDFConfig{Type: crypto.KDFType(preLogin.KDF), Iterations: uint32(preLogin.KDFIterations), Memory: uint32(preLogin.KDFMemory), Parallelism: uint32(preLogin.KDFParallelism)})
masterKey, err = crypto.DeriveMasterKey([]byte(strings.Clone(password)), email, crypto.KDFConfig{Type: crypto.KDFType(preLogin.KDF), Iterations: uint32(preLogin.KDFIterations), Memory: uint32(preLogin.KDFMemory), Parallelism: uint32(preLogin.KDFParallelism)})
if err != nil {
return LoginResponseToken{}, crypto.MasterKey{}, "", err
}
Expand Down
8 changes: 3 additions & 5 deletions agent/bitwarden/crypto/kdf.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,19 @@ func (masterKey MasterKey) GetBytes() []byte {
return bytes.Clone(buffer.Bytes())
}

func DeriveMasterKey(password memguard.LockedBuffer, email string, kdfConfig KDFConfig) (MasterKey, error) {
func DeriveMasterKey(password []byte, email string, kdfConfig KDFConfig) (MasterKey, error) {
defer debug.FreeOSMemory()

var key []byte
switch kdfConfig.Type {
case PBKDF2:
key = pbkdf2.Key(password.Bytes(), []byte(strings.ToLower(email)), int(kdfConfig.Iterations), 32, sha256.New)
key = pbkdf2.Key(password, []byte(strings.ToLower(email)), int(kdfConfig.Iterations), 32, sha256.New)
case Argon2ID:
var salt [32]byte = sha256.Sum256([]byte(strings.ToLower(email)))
key = argon2.IDKey(password.Bytes(), salt[:], kdfConfig.Iterations, kdfConfig.Memory*1024, uint8(kdfConfig.Parallelism), 32)
key = argon2.IDKey(password, salt[:], kdfConfig.Iterations, kdfConfig.Memory*1024, uint8(kdfConfig.Parallelism), 32)
default:
password.Destroy()
return MasterKey{}, fmt.Errorf("unsupported KDF type %d", kdfConfig.Type)
}
password.Destroy()

return MasterKey{memguard.NewEnclave(key)}, nil
}
Expand Down
2 changes: 1 addition & 1 deletion agent/bitwarden/crypto/keyhierarchy.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"golang.org/x/crypto/hkdf"
)

func InitKeyringFromMasterPassword(keyring *Keyring, accountKey EncString, accountPrivateKey EncString, orgKeys map[string]string, password memguard.LockedBuffer, email string, kdfConfig KDFConfig) error {
func InitKeyringFromMasterPassword(keyring *Keyring, accountKey EncString, accountPrivateKey EncString, orgKeys map[string]string, password []byte, email string, kdfConfig KDFConfig) error {
masterKey, err := DeriveMasterKey(password, email, kdfConfig)
if err != nil {
return err
Expand Down
5 changes: 4 additions & 1 deletion agent/bitwarden/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ func DoFullSync(ctx context.Context, vault *vault.Vault, config *config.Config,
if allowCache {
home, _ := os.UserHomeDir()
sync, err = ReadVault(home + path)
if err != nil {
return err
}
} else {
return err
}
Expand All @@ -44,7 +47,7 @@ func DoFullSync(ctx context.Context, vault *vault.Vault, config *config.Config,
}

var orgKeys map[string]string = make(map[string]string)
log.Info("Initializing %d org keys...", len(sync.Profile.Organizations))
log.Info("Reading %d org keys...", len(sync.Profile.Organizations))
for _, org := range sync.Profile.Organizations {
orgId := org.Id.String()
orgKeys[orgId] = org.Key
Expand Down
6 changes: 5 additions & 1 deletion agent/bitwarden/websocket.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,11 @@ func connectToWebsocket(ctx context.Context, vault *vault.Vault, cfg *config.Con
break
case LogOut:
websocketLog.Info("LogOut received. Wiping vault and exiting...")
memguard.SafeExit(0)
if vault.Keyring.IsMemguard {
memguard.SafeExit(0)
} else {
os.Exit(0)
}
case AuthRequest:
websocketLog.Info("AuthRequest received" + string(cipherid))
authRequest, err := GetAuthRequest(context.WithValue(ctx, AuthToken{}, token.AccessToken), cipherid, cfg)
Expand Down
45 changes: 28 additions & 17 deletions agent/config/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package config

import (
"bytes"
cryptoSubtle "crypto/subtle"
"encoding/base64"
"encoding/hex"
Expand All @@ -10,7 +11,6 @@ import (
"runtime/debug"
"sync"

"github.com/awnumar/memguard"
"github.com/google/uuid"
"github.com/quexten/goldwarden/agent/bitwarden/crypto"
"github.com/quexten/goldwarden/agent/systemauth/pinentry"
Expand Down Expand Up @@ -68,15 +68,18 @@ type LoginToken struct {
}

type Config struct {
key *memguard.LockedBuffer
ConfigFile ConfigFile
mu sync.Mutex
useMemguard bool
key *LockedBuffer
ConfigFile ConfigFile
mu sync.Mutex
}

func DefaultConfig() Config {
func DefaultConfig(useMemguard bool) Config {
deviceUUID, _ := uuid.NewUUID()
keyBuffer := NewBuffer(32, useMemguard)
return Config{
memguard.NewBuffer(32),
useMemguard,
&keyBuffer,
ConfigFile{
IdentityUrl: "https://vault.bitwarden.com/identity",
ApiUrl: "https://vault.bitwarden.com/api",
Expand All @@ -94,7 +97,8 @@ func DefaultConfig() Config {
}

func (c *Config) IsLocked() bool {
return c.key.EqualTo(make([]byte, 32)) && c.HasPin()
key := (*c.key).Bytes()
return bytes.Equal(key, make([]byte, 32)) && c.HasPin()
}

func (c *Config) IsLoggedIn() bool {
Expand All @@ -117,7 +121,8 @@ func (c *Config) Unlock(password string) bool {
return false
}

c.key = memguard.NewBufferFromBytes(key)
keyBuffer := NewBufferFromBytes(key, c.useMemguard)
c.key = &keyBuffer
return true
}

Expand All @@ -140,7 +145,7 @@ func (c *Config) Lock() {
if c.IsLocked() {
return
}
c.key.Wipe()
(*c.key).Wipe()
}

func (c *Config) Purge() {
Expand All @@ -152,7 +157,8 @@ func (c *Config) Purge() {
c.ConfigFile.EncryptedUserSymmetricKey = ""
c.ConfigFile.ConfigKeyHash = ""
c.ConfigFile.EncryptedMasterKey = ""
c.key = memguard.NewBuffer(32)
key := NewBuffer(32, c.useMemguard)
c.key = &key
}

func (c *Config) HasPin() bool {
Expand All @@ -174,7 +180,8 @@ func (c *Config) UpdatePin(password string, write bool) {
plaintextEncryptedMasterPasswordHash, err4 := c.decryptString(c.ConfigFile.EncryptedMasterPasswordHash)
plaintextMasterKey, err5 := c.decryptString(c.ConfigFile.EncryptedMasterKey)

c.key = memguard.NewBufferFromBytes(newKey)
key := NewBufferFromBytes(newKey, c.useMemguard)
c.key = &key

if err1 == nil {
c.ConfigFile.EncryptedToken, err1 = c.encryptString(plaintextToken)
Expand Down Expand Up @@ -315,7 +322,7 @@ func (c *Config) encryptString(data string) (string, error) {
if c.IsLocked() {
return "", errors.New("config is locked")
}
ca, err := subtle.NewChaCha20Poly1305(c.key.Bytes())
ca, err := subtle.NewChaCha20Poly1305((*c.key).Bytes())
if err != nil {
return "", err
}
Expand All @@ -337,7 +344,7 @@ func (c *Config) decryptString(data string) (string, error) {
return "", err
}

ca, err := subtle.NewChaCha20Poly1305(c.key.Bytes())
ca, err := subtle.NewChaCha20Poly1305((*c.key).Bytes())
if err != nil {
return "", err
}
Expand Down Expand Up @@ -379,8 +386,9 @@ func (config *Config) WriteConfig() error {
func ReadConfig(rtCfg RuntimeConfig) (Config, error) {
file, err := os.Open(rtCfg.ConfigDirectory)
if err != nil {
key := NewBuffer(32, rtCfg.UseMemguard)
return Config{
key: memguard.NewBuffer(32),
key: &key,
ConfigFile: ConfigFile{},
}, err
}
Expand All @@ -390,19 +398,22 @@ func ReadConfig(rtCfg RuntimeConfig) (Config, error) {
config := ConfigFile{}
err = decoder.Decode(&config)
if err != nil {
key := NewBuffer(32, rtCfg.UseMemguard)
return Config{
key: memguard.NewBuffer(32),
key: &key,
ConfigFile: ConfigFile{},
}, err
}
if config.ConfigKeyHash == "" {
key := NewBuffer(32, rtCfg.UseMemguard)
return Config{
key: memguard.NewBuffer(32),
key: &key,
ConfigFile: config,
}, nil
}
key := NewBuffer(32, rtCfg.UseMemguard)
return Config{
key: memguard.NewBuffer(32),
key: &key,
ConfigFile: config,
}, nil
}
Expand Down
48 changes: 48 additions & 0 deletions agent/config/lockedbuffer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package config

import "github.com/awnumar/memguard"

type LockedBuffer interface {
Bytes() []byte
Wipe()
}

func NewBuffer(size int, useMemguard bool) LockedBuffer {
if useMemguard {
return MemGuardLockedBuffer{memguard.NewBuffer(size)}
}
return MemoryLockedBuffer{make([]byte, size)}
}

func NewBufferFromBytes(bytes []byte, useMemguard bool) LockedBuffer {
if useMemguard {
return MemGuardLockedBuffer{memguard.NewBufferFromBytes(bytes)}
}
return MemoryLockedBuffer{bytes}
}

type MemGuardLockedBuffer struct {
buffer *memguard.LockedBuffer
}

func (b MemGuardLockedBuffer) Wipe() {
b.buffer.Destroy()
}

func (b MemGuardLockedBuffer) Bytes() []byte {
return b.buffer.Bytes()
}

type MemoryLockedBuffer struct {
buffer []byte
}

func (b MemoryLockedBuffer) Wipe() {
for i := range b.buffer {
b.buffer[i] = 0
}
}

func (b MemoryLockedBuffer) Bytes() []byte {
return b.buffer
}
2 changes: 1 addition & 1 deletion agent/unixsocketagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func StartUnixAgent(path string, runtimeConfig config.RuntimeConfig) error {
cfg, err := config.ReadConfig(runtimeConfig)
if err != nil {
log.Warn("Could not read config: %s", err.Error())
cfg = config.DefaultConfig()
cfg = config.DefaultConfig(runtimeConfig.UseMemguard)
cfg.ConfigFile.RuntimeConfig = runtimeConfig
cfg.WriteConfig()
}
Expand Down
2 changes: 1 addition & 1 deletion agent/virtualagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func StartVirtualAgent(runtimeConfig config.RuntimeConfig) (chan []byte, chan []
var vault = vault.NewVault(&keyring)
cfg, err := config.ReadConfig(runtimeConfig)
if err != nil {
var cfg = config.DefaultConfig()
var cfg = config.DefaultConfig(runtimeConfig.UseMemguard)
cfg.WriteConfig()
}
cfg.ConfigFile.RuntimeConfig = runtimeConfig
Expand Down
44 changes: 22 additions & 22 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,21 @@ require (
github.com/LlamaNite/llamalog v0.2.1
github.com/amenzhinsky/go-polkit v0.0.0-20210519083301-ee6a51849123
github.com/atotto/clipboard v0.1.4
github.com/awnumar/memguard v0.22.3
github.com/bendahl/uinput v1.6.2
github.com/google/uuid v1.3.0
github.com/gorilla/websocket v1.5.0
github.com/awnumar/memguard v0.22.4
github.com/bendahl/uinput v1.7.0
github.com/google/uuid v1.5.0
github.com/gorilla/websocket v1.5.1
github.com/lox/go-touchid v0.0.0-20170712105233-619cc8e578d0
github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a
github.com/mitchellh/go-ps v1.0.0
github.com/pquerna/otp v1.4.0
github.com/shirou/gopsutil/v3 v3.23.6
github.com/spf13/cobra v1.7.0
github.com/tink-crypto/tink-go/v2 v2.0.0
github.com/twpayne/go-pinentry v0.2.0
github.com/vmihailenco/msgpack/v5 v5.3.5
golang.org/x/crypto v0.13.0
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1
golang.org/x/sys v0.12.0
github.com/shirou/gopsutil/v3 v3.23.11
github.com/spf13/cobra v1.8.0
github.com/tink-crypto/tink-go/v2 v2.1.0
github.com/twpayne/go-pinentry v0.3.0
github.com/vmihailenco/msgpack/v5 v5.4.1
golang.org/x/crypto v0.17.0
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848
)

require (
Expand All @@ -34,25 +33,26 @@ require (
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/tklauser/go-sysconf v0.3.11 // indirect
github.com/tklauser/numcpus v0.6.0 // indirect
github.com/tklauser/go-sysconf v0.3.13 // indirect
github.com/tklauser/numcpus v0.7.0 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91 // indirect
golang.org/x/image v0.5.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/exp/shiny v0.0.0-20231219180239-dc181d75b848 // indirect
golang.org/x/image v0.14.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
)

require (
github.com/awnumar/memcall v0.1.2 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/awnumar/memcall v0.2.0 // indirect
github.com/godbus/dbus/v5 v5.1.0
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/keys-pub/go-libfido2 v1.5.3
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rs/zerolog v1.29.1
github.com/rs/zerolog v1.31.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
inet.af/peercred v0.0.0-20210906144145-0893ea02156a
)
Loading

0 comments on commit 189b151

Please sign in to comment.