This commit is contained in:
github-actions[bot] 2024-04-28 16:01:24 +00:00 committed by GitHub
commit 2cd7fe75ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
65 changed files with 854 additions and 483 deletions

6
.github/dependabot.yaml vendored Normal file
View File

@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: gomod
directory: /
schedule:
interval: monthly

8
.github/workflows/_typos.toml vendored Normal file
View File

@ -0,0 +1,8 @@
[default]
extend-ignore-identifiers-re = [
# upstream go library issue
".*Encrypter",
]
[files]
extend-exclude = ["go.mod"]

View File

@ -5,7 +5,7 @@ on:
pull_request:
branches: [main]
env:
GO_VERSION: '1.20'
GO_VERSION: '1.22'
jobs:
build_daemon:
runs-on: ubuntu-latest
@ -45,4 +45,4 @@ jobs:
bundle: goldwarden.flatpak
manifest-path: com.quexten.Goldwarden.yml
cache-key: flatpak-builder-${{ github.sha }}
arch: ${{ matrix.arch }}
arch: ${{ matrix.arch }}

View File

@ -7,7 +7,7 @@ on:
branches: [ "main" ]
env:
GO_VERSION: '1.20'
GO_VERSION: '1.22'
jobs:
build_linux_x86_64_debug:
@ -77,7 +77,7 @@ jobs:
path: ./goldwarden_linux_x86
build_macos_x86_64:
runs-on: macos-latest
runs-on: macos-13
steps:
- name: Checkout
uses: actions/checkout@v1
@ -90,13 +90,13 @@ jobs:
uses: Homebrew/actions/setup-homebrew@master
- name: Fido2
run: brew install libfido2
- name: Build
- name: Build
run: go build -o "goldwarden_macos_x86_64" -v .
- uses: actions/upload-artifact@v3
with:
name: goldwarden-macos_x86_64
path: ./goldwarden_macos_x86_64
build_macos_aarch64:
runs-on: macos-14
steps:
@ -111,13 +111,13 @@ jobs:
uses: Homebrew/actions/setup-homebrew@master
- name: Fido2
run: brew install libfido2
- name: Build
- name: Build
run: go build -o "goldwarden_macos_aarch64" -v .
- uses: actions/upload-artifact@v3
with:
name: goldwarden-macos_aarch64
path: ./goldwarden_macos_aarch64
path: ./goldwarden_macos_aarch64
build_windows_x86_64:
runs-on: windows-latest
steps:
@ -132,13 +132,13 @@ jobs:
run: |
scoop bucket add keys.pub https://github.com/keys-pub/scoop-bucket
scoop install libfido2
- name: Build
- name: Build
run: go build -o "goldwarden_windows_x86_64.exe" -v .
- uses: actions/upload-artifact@v3
with:
name: goldwarden-windows_x86_64.exe
path: ./goldwarden_windows_x86_64.exe
build_windows_aarch64:
runs-on: windows-latest
steps:
@ -148,7 +148,7 @@ jobs:
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- name: Build
- name: Build
run: set GOARCH=arm64 && go build -tags nofido2 -o "goldwarden_windows_aarch64.exe" -v .
- uses: actions/upload-artifact@v3
with:

View File

@ -7,7 +7,7 @@ permissions:
packages: write
env:
GO_VERSION: '1.20'
GO_VERSION: '1.22'
jobs:
merge_release:

15
.github/workflows/typos.yaml vendored Normal file
View File

@ -0,0 +1,15 @@
name: Check for Typos
on: [pull_request]
jobs:
run:
name: Spell Check with Typos
runs-on: ubuntu-latest
steps:
- name: Checkout Actions Repository
uses: actions/checkout@v4
- name: Check spelling
uses: crate-ci/typos@master
with:
config: './.github/workflows/_typos.toml'

View File

@ -1,5 +1,5 @@
pkgname=goldwarden
pkgver=0.2.13
pkgver=0.2.14
pkgrel=1
pkgdesc='A feature-packed Bitwarden compatible desktop integration'
arch=('x86_64' 'aarch64')

View File

@ -3,7 +3,8 @@
# Goldwarden
Goldwarden is a Bitwarden compatible desktop client. It focuses on providing useful desktop features that the official tools
do not (yet) have or are not willing to add, and enhanced security measures that other tools do not provide, such as:
do not (yet) have or are not willing to add (for example, because the integrations are not mature enough for a broad userbase),
and enhanced security measures that other tools do not provide, such as:
- Support for SSH Agent (Git signing and SSH login)
- System wide autotype (Linux - Gnome, KDE only for now)

View File

@ -64,17 +64,17 @@ func sync(ctx context.Context, vault *vault.Vault, cfg *config.Config) bool {
return false
}
var protectedUserSymetricKey crypto.SymmetricEncryptionKey
var protectedUserSymmetricKey crypto.SymmetricEncryptionKey
if vault.Keyring.IsMemguard {
protectedUserSymetricKey, err = crypto.MemguardSymmetricEncryptionKeyFromBytes(userSymmetricKey)
protectedUserSymmetricKey, err = crypto.MemguardSymmetricEncryptionKeyFromBytes(userSymmetricKey)
} else {
protectedUserSymetricKey, err = crypto.MemorySymmetricEncryptionKeyFromBytes(userSymmetricKey)
protectedUserSymmetricKey, err = crypto.MemorySymmetricEncryptionKeyFromBytes(userSymmetricKey)
}
if err != nil {
return false
}
err = bitwarden.DoFullSync(context.WithValue(ctx, bitwarden.AuthToken{}, token.AccessToken), vault, cfg, &protectedUserSymetricKey, true)
err = bitwarden.DoFullSync(context.WithValue(ctx, bitwarden.AuthToken{}, token.AccessToken), vault, cfg, &protectedUserSymmetricKey, true)
if err != nil {
return false
}

View File

@ -144,7 +144,14 @@ func handleGetConfigEnvironment(request messages.IPCMessage, cfg *config.Config,
func handleSetClientID(request messages.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (response messages.IPCMessage, err error) {
clientID := messages.ParsePayload(request).(messages.SetClientIDRequest).Value
cfg.SetClientID(clientID)
err = cfg.SetClientID(clientID)
if err != nil {
return messages.IPCMessageFromPayload(messages.ActionResponse{
Success: false,
Message: err.Error(),
})
}
err = cfg.WriteConfig()
if err != nil {
return messages.IPCMessageFromPayload(messages.ActionResponse{
@ -160,7 +167,14 @@ func handleSetClientID(request messages.IPCMessage, cfg *config.Config, vault *v
func handleSetClientSecret(request messages.IPCMessage, cfg *config.Config, vault *vault.Vault, ctx *sockets.CallingContext) (response messages.IPCMessage, err error) {
clientSecret := messages.ParsePayload(request).(messages.SetClientSecretRequest).Value
cfg.SetClientSecret(clientSecret)
err = cfg.SetClientSecret(clientSecret)
if err != nil {
return messages.IPCMessageFromPayload(messages.ActionResponse{
Success: false,
Message: err.Error(),
})
}
err = cfg.WriteConfig()
if err != nil {
return messages.IPCMessageFromPayload(messages.ActionResponse{

View File

@ -34,7 +34,8 @@ func handleLogin(msg messages.IPCMessage, cfg *config.Config, vault *vault.Vault
var masterKey crypto.MasterKey
var masterpasswordHash string
if secret, err := cfg.GetClientSecret(); err == nil && secret != "" {
var secret string // don't shadow err in the next line
if secret, err = cfg.GetClientSecret(); err == nil && secret != "" {
actionsLog.Info("Logging in with client secret")
token, masterKey, masterpasswordHash, err = bitwarden.LoginWithApiKey(ctx, req.Email, cfg, vault)
} else if req.Passwordless {
@ -56,7 +57,7 @@ func handleLogin(msg messages.IPCMessage, cfg *config.Config, vault *vault.Vault
return
}
cfg.SetToken(config.LoginToken{
_ = cfg.SetToken(config.LoginToken{
AccessToken: token.AccessToken,
ExpiresIn: token.ExpiresIn,
TokenType: token.TokenType,
@ -87,7 +88,7 @@ func handleLogin(msg messages.IPCMessage, cfg *config.Config, vault *vault.Vault
if err != nil {
defer func() {
notify.Notify("Goldwarden", "Could not decrypt. Wrong password?", "", 10*time.Second, func() {})
cfg.SetToken(config.LoginToken{})
_ = cfg.SetToken(config.LoginToken{})
vault.Clear()
}()
@ -102,19 +103,19 @@ func handleLogin(msg messages.IPCMessage, cfg *config.Config, vault *vault.Vault
return
}
cfg.SetUserSymmetricKey(vault.Keyring.GetAccountKey().Bytes())
cfg.SetMasterPasswordHash([]byte(masterpasswordHash))
cfg.SetMasterKey([]byte(masterKey.GetBytes()))
var protectedUserSymetricKey crypto.SymmetricEncryptionKey
err = cfg.SetUserSymmetricKey(vault.Keyring.GetAccountKey().Bytes())
err = cfg.SetMasterPasswordHash([]byte(masterpasswordHash))
err = cfg.SetMasterKey([]byte(masterKey.GetBytes()))
var protectedUserSymmetricKey crypto.SymmetricEncryptionKey
if vault.Keyring.IsMemguard {
protectedUserSymetricKey, err = crypto.MemguardSymmetricEncryptionKeyFromBytes(vault.Keyring.GetAccountKey().Bytes())
protectedUserSymmetricKey, err = crypto.MemguardSymmetricEncryptionKeyFromBytes(vault.Keyring.GetAccountKey().Bytes())
} else {
protectedUserSymetricKey, err = crypto.MemorySymmetricEncryptionKeyFromBytes(vault.Keyring.GetAccountKey().Bytes())
protectedUserSymmetricKey, err = crypto.MemorySymmetricEncryptionKeyFromBytes(vault.Keyring.GetAccountKey().Bytes())
}
if err != nil {
defer func() {
notify.Notify("Goldwarden", "Could not decrypt. Wrong password?", "", 10*time.Second, func() {})
cfg.SetToken(config.LoginToken{})
_ = cfg.SetToken(config.LoginToken{})
vault.Clear()
}()
@ -128,7 +129,7 @@ func handleLogin(msg messages.IPCMessage, cfg *config.Config, vault *vault.Vault
}
return
}
err = bitwarden.DoFullSync(context.WithValue(ctx, bitwarden.AuthToken{}, token.AccessToken), vault, cfg, &protectedUserSymetricKey, false)
err = bitwarden.DoFullSync(context.WithValue(ctx, bitwarden.AuthToken{}, token.AccessToken), vault, cfg, &protectedUserSymmetricKey, false)
response, err = messages.IPCMessageFromPayload(messages.ActionResponse{
Success: true,

View File

@ -69,11 +69,7 @@ func handleGetLoginCipher(request messages.IPCMessage, cfg *config.Config, vault
if !login.Login.Totp.IsNull() {
decryptedTotp, err := crypto.DecryptWith(login.Login.Totp, cipherKey)
if err == nil {
if err == nil {
decryptedLogin.TOTPSeed = string(decryptedTotp)
} else {
fmt.Println(err)
}
decryptedLogin.TOTPSeed = string(decryptedTotp)
} else {
fmt.Println(string(decryptedTotp))
}
@ -117,11 +113,7 @@ func handleListLoginsRequest(request messages.IPCMessage, cfg *config.Config, va
continue
}
var decryptedName []byte = []byte{}
var decryptedUsername []byte = []byte{}
var decryptedPassword []byte = []byte{}
var decryptedTotp []byte = []byte{}
var decryptedURL []byte = []byte{}
var decryptedName, decryptedUsername, decryptedPassword, decryptedTotp, decryptedURL []byte
if !login.Name.IsNull() {
decryptedName, err = crypto.DecryptWith(login.Name, key)

View File

@ -20,6 +20,9 @@ func handleCreateSend(msg messages.IPCMessage, cfg *config.Config, vault *vault.
ctx := context.WithValue(context.TODO(), bitwarden.AuthToken{}, token.AccessToken)
url, err := bitwarden.CreateSend(ctx, cfg, vault, parsedMsg.Name, parsedMsg.Text)
if err != nil {
actionsLog.Warn(err.Error())
}
response, err = messages.IPCMessageFromPayload(messages.CreateSendResponse{
URL: url,

View File

@ -17,7 +17,7 @@ func handleAddSSH(msg messages.IPCMessage, cfg *config.Config, vault *vault.Vaul
req := messages.ParsePayload(msg).(messages.CreateSSHKeyRequest)
cipher, publicKey := ssh.NewSSHKeyCipher(req.Name, vault.Keyring)
response, err = messages.IPCMessageFromPayload(messages.ActionResponse{
_, err = messages.IPCMessageFromPayload(messages.ActionResponse{
Success: true,
})
if err != nil {
@ -25,10 +25,13 @@ func handleAddSSH(msg messages.IPCMessage, cfg *config.Config, vault *vault.Vaul
}
token, err := cfg.GetToken()
if err != nil {
actionsLog.Warn(err.Error())
}
ctx := context.WithValue(context.TODO(), bitwarden.AuthToken{}, token.AccessToken)
ciph, err := bitwarden.PostCipher(ctx, cipher, cfg)
postedCipher, err := bitwarden.PostCipher(ctx, cipher, cfg)
if err == nil {
vault.AddOrUpdateSecureNote(ciph)
vault.AddOrUpdateSecureNote(postedCipher)
} else {
actionsLog.Warn("Error posting ssh key cipher: " + err.Error())
}

View File

@ -146,7 +146,10 @@ func handleLockVault(request messages.IPCMessage, cfg *config.Config, vault *vau
func handleWipeVault(request messages.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext *sockets.CallingContext) (response messages.IPCMessage, err error) {
cfg.Purge()
cfg.WriteConfig()
err = cfg.WriteConfig()
if err != nil {
panic(err)
}
vault.Clear()
response, err = messages.IPCMessageFromPayload(messages.ActionResponse{

View File

@ -162,10 +162,10 @@ func LoginWithMasterpassword(ctx context.Context, email string, cfg *config.Conf
return LoginResponseToken{}, crypto.MasterKey{}, "", err
}
} else if err != nil && strings.Contains(err.Error(), "Captcha required.") {
notify.Notify("Goldwarden", fmt.Sprintf("Captcha required"), "", 0, func() {})
notify.Notify("Goldwarden", "Captcha required", "", 0, func() {})
return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("captcha required, please login via the web interface")
} else if err != nil {
notify.Notify("Goldwarden", fmt.Sprintf("Could not login via password: %v", err), "", 0, func() {})
notify.Notify("Goldwarden", fmt.Sprintf("Could not login via password: %s", err.Error()), "", 0, func() {})
return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("could not login via password: %v", err)
}
@ -208,7 +208,15 @@ func LoginWithDevice(ctx context.Context, email string, cfg *config.Config, vaul
}
if authRequestData.RequestApproved {
masterKey, err := crypto.DecryptWithAsymmetric([]byte(authRequestData.Key), publicKey)
if err != nil {
return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("could not decrypt key with asymmetric key: %s", err.Error())
}
masterPasswordHash, err := crypto.DecryptWithAsymmetric([]byte(authRequestData.MasterPasswordHash), publicKey)
if err != nil {
return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("could not decrypt master password hash with asymmetric key: %s", err.Error())
}
values := urlValues(
"grant_type", "password",
"username", email,
@ -233,7 +241,7 @@ func LoginWithDevice(ctx context.Context, email string, cfg *config.Config, vaul
return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("captcha required, please login via the web interface")
} else if err != nil {
return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("could not login via password: %v", err)
return LoginResponseToken{}, crypto.MasterKey{}, "", fmt.Errorf("could not login via password: %s", err.Error())
}
return loginResponseToken, crypto.MasterKeyFromBytes(masterKey), string(masterPasswordHash), nil
}
@ -282,13 +290,17 @@ func RefreshToken(ctx context.Context, cfg *config.Config) bool {
return false
}
cfg.SetToken(config.LoginToken{
err = cfg.SetToken(config.LoginToken{
AccessToken: loginResponseToken.AccessToken,
RefreshToken: "",
Key: loginResponseToken.Key,
TokenType: loginResponseToken.TokenType,
ExpiresIn: loginResponseToken.ExpiresIn,
})
if err != nil {
authLog.Error("Could not set token: %s", err.Error())
return false
}
} else {
authLog.Info("No api credentials set")
}
@ -306,13 +318,17 @@ func RefreshToken(ctx context.Context, cfg *config.Config) bool {
notify.Notify("Goldwarden", fmt.Sprintf("Could not refresh token: %v", err), "", 0, func() {})
return false
}
cfg.SetToken(config.LoginToken{
err = cfg.SetToken(config.LoginToken{
AccessToken: loginResponseToken.AccessToken,
RefreshToken: loginResponseToken.RefreshToken,
Key: loginResponseToken.Key,
TokenType: loginResponseToken.TokenType,
ExpiresIn: loginResponseToken.ExpiresIn,
})
if err != nil {
authLog.Error("Could not set token: %s", err.Error())
return false
}
}
authLog.Info("Token refreshed")

View File

@ -9,6 +9,7 @@ import (
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"errors"
"fmt"
"io"
"math"
@ -112,17 +113,20 @@ func (key MemguardSymmetricEncryptionKey) MacKeyBytes() ([]byte, error) {
return keyBytes, nil
}
func MemoryAssymmetricEncryptionKeyFromBytes(key []byte) (MemoryAsymmetricEncryptionKey, error) {
func MemoryAsymmetricEncryptionKeyFromBytes(key []byte) (MemoryAsymmetricEncryptionKey, error) {
return MemoryAsymmetricEncryptionKey{key}, nil
}
func MemguardAssymmetricEncryptionKeyFromBytes(key []byte) (MemguardAsymmetricEncryptionKey, error) {
func MemguardAsymmetricEncryptionKeyFromBytes(key []byte) (MemguardAsymmetricEncryptionKey, error) {
k := memguard.NewEnclave(key)
return MemguardAsymmetricEncryptionKey{k}, nil
}
func (key MemoryAsymmetricEncryptionKey) PublicBytes() []byte {
privateKey, err := x509.ParsePKCS8PrivateKey(key.encKey)
if err != nil {
panic(err)
}
pub := (privateKey.(*rsa.PrivateKey)).Public()
publicKeyBytes, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
@ -137,6 +141,9 @@ func (key MemguardAsymmetricEncryptionKey) PublicBytes() []byte {
panic(err)
}
privateKey, err := x509.ParsePKCS8PrivateKey(buffer.Bytes())
if err != nil {
panic(err)
}
pub := (privateKey.(*rsa.PrivateKey)).Public()
publicKeyBytes, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
@ -164,7 +171,7 @@ func isMacValid(message, messageMAC, key []byte) bool {
return hmac.Equal(messageMAC, expectedMAC)
}
func encryptAESCBC256(data, key []byte) (iv, ciphertext []byte, _ error) {
func encryptAESCBC256(data, key []byte) (iv, ciphertext []byte, err error) {
data = padPKCS7(data, aes.BlockSize)
block, err := aes.NewCipher(key)
if err != nil {
@ -176,12 +183,28 @@ func encryptAESCBC256(data, key []byte) (iv, ciphertext []byte, _ error) {
if _, err := io.ReadFull(cryptorand.Reader, iv); err != nil {
return nil, nil, err
}
defer func() {
if r := recover(); r != nil {
var ok bool
err, ok = r.(error)
if !ok {
iv = nil
ciphertext = nil
err = errors.New("error encrypting AES CBC 256 data")
}
}
}()
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext, data)
return iv, ciphertext, nil
}
func decryptAESCBC256(iv, ciphertext, key []byte) ([]byte, error) {
func decryptAESCBC256(iv, ciphertext, key []byte) (decryptedData []byte, err error) {
ciphertextCopy := make([]byte, len(ciphertext))
copy(ciphertextCopy, ciphertext)
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
@ -189,28 +212,50 @@ func decryptAESCBC256(iv, ciphertext, key []byte) ([]byte, error) {
if len(iv) != aes.BlockSize {
return nil, fmt.Errorf("iv length does not match AES block size")
}
if len(ciphertext)%aes.BlockSize != 0 {
if len(ciphertextCopy)%aes.BlockSize != 0 {
return nil, fmt.Errorf("ciphertext is not a multiple of AES block size")
}
defer func() {
if r := recover(); r != nil {
var ok bool
err, ok = r.(error)
if !ok {
decryptedData = nil
err = errors.New("error decrypting AES CBC 256 data")
}
}
}()
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(ciphertext, ciphertext) // decrypt in-place
data, err := unpadPKCS7(ciphertext, aes.BlockSize)
mode.CryptBlocks(ciphertextCopy, ciphertextCopy) // decrypt in-place
data, err := unpadPKCS7(ciphertextCopy, aes.BlockSize)
if err != nil {
return nil, err
}
return data, nil
resultBuffer := make([]byte, len(data))
copy(resultBuffer, data)
return resultBuffer, nil
}
func unpadPKCS7(src []byte, size int) ([]byte, error) {
n := src[len(src)-1]
if len(src)%size != 0 {
return nil, fmt.Errorf("expected PKCS7 padding for block size %d, but have %d bytes", size, len(src))
srcCopy := make([]byte, len(src))
copy(srcCopy, src)
n := srcCopy[len(srcCopy)-1]
if len(srcCopy)%size != 0 {
return nil, fmt.Errorf("expected PKCS7 padding for block size %d, but have %d bytes", size, len(srcCopy))
}
if len(src) <= int(n) {
return nil, fmt.Errorf("cannot unpad %d bytes out of a total of %d", n, len(src))
if len(srcCopy) <= int(n) {
return nil, fmt.Errorf("cannot unpad %d bytes out of a total of %d", n, len(srcCopy))
}
src = src[:len(src)-int(n)]
return src, nil
srcCopy = srcCopy[:len(srcCopy)-int(n)]
resultCopy := make([]byte, len(srcCopy))
copy(resultCopy, srcCopy)
return resultCopy, nil
}
func padPKCS7(src []byte, size int) []byte {

View File

@ -2,23 +2,22 @@ package crypto
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/rand"
cryptorand "crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"crypto/x509"
"errors"
"fmt"
"io"
"strconv"
"github.com/awnumar/memguard"
"github.com/quexten/goldwarden/logging"
)
var cryptoLog = logging.GetLogger("Goldwarden", "Crypto")
type EncString struct {
Type EncStringType
IV, CT, MAC []byte
@ -47,7 +46,7 @@ func (s *EncString) UnmarshalText(data []byte) error {
i := bytes.IndexByte(data, '.')
if i < 0 {
return errors.New("invalid cipher string format, missign type. total length: " + strconv.Itoa(len(data)))
return errors.New("invalid cipher string format, missing type. total length: " + strconv.Itoa(len(data)))
}
typStr := string(data[:i])
@ -132,11 +131,6 @@ func DecryptWith(s EncString, key SymmetricEncryptionKey) ([]byte, error) {
return nil, err
}
block, err := aes.NewCipher(encKeyData)
if err != nil {
return nil, err
}
switch s.Type {
case AesCbc256_B64, AesCbc256_HmacSha256_B64:
break
@ -158,17 +152,11 @@ func DecryptWith(s EncString, key SymmetricEncryptionKey) ([]byte, error) {
return nil, fmt.Errorf("decrypt: cipher of unsupported type %q", s.Type)
}
if len(s.IV) != block.BlockSize() {
return nil, fmt.Errorf("decrypt: invalid IV length, expected %d, got %d", block.BlockSize(), len(s.IV))
}
mode := cipher.NewCBCDecrypter(block, s.IV)
dst := make([]byte, len(s.CT))
mode.CryptBlocks(dst, s.CT)
dst, err = unpadPKCS7(dst, aes.BlockSize)
dst, err := decryptAESCBC256(s.IV, s.CT, encKeyData)
if err != nil {
return nil, err
}
return dst, nil
}
@ -189,19 +177,13 @@ func EncryptWith(data []byte, encType EncStringType, key SymmetricEncryptionKey)
return s, fmt.Errorf("encrypt: unsupported cipher type %q", s.Type)
}
s.Type = encType
data = padPKCS7(data, aes.BlockSize)
block, err := aes.NewCipher(encKeyData)
iv, ciphertext, err := encryptAESCBC256(data, encKeyData)
if err != nil {
return s, err
}
s.IV = make([]byte, aes.BlockSize)
if _, err := io.ReadFull(cryptorand.Reader, s.IV); err != nil {
return s, err
}
s.CT = make([]byte, len(data))
mode := cipher.NewCBCEncrypter(block, s.IV)
mode.CryptBlocks(s.CT, data)
s.CT = ciphertext
s.IV = iv
if encType == AesCbc256_HmacSha256_B64 {
if len(macKeyData) == 0 {

View File

@ -49,6 +49,7 @@ func InitKeyringFromMasterKey(keyring *Keyring, accountKey EncString, accountPri
keyring.UnlockWithAccountKey(accountSymmetricKey)
keyringLog.Info("Decrypting account private key")
pkcs8PrivateKey, err := DecryptWith(accountPrivateKey, accountSymmetricKey)
if err != nil {
return err
@ -65,6 +66,7 @@ func InitKeyringFromMasterKey(keyring *Keyring, accountKey EncString, accountPri
func InitKeyringFromUserSymmetricKey(keyring *Keyring, accountSymmetricKey SymmetricEncryptionKey, accountPrivateKey EncString, orgKeys map[string]string) error {
keyring.UnlockWithAccountKey(accountSymmetricKey)
keyringLog.Info("Decrypting account private key")
pkcs8PrivateKey, err := DecryptWith(accountPrivateKey, accountSymmetricKey)
if err != nil {
return err
@ -90,9 +92,15 @@ func stretchKey(masterKey MasterKey, useMemguard bool) (SymmetricEncryptionKey,
var r io.Reader
r = hkdf.Expand(sha256.New, buffer.Data(), []byte("enc"))
r.Read(key)
_, err = r.Read(key)
if err != nil {
return nil, err
}
r = hkdf.Expand(sha256.New, buffer.Data(), []byte("mac"))
r.Read(macKey)
_, err = r.Read(macKey)
if err != nil {
return nil, err
}
if useMemguard {
return MemguardSymmetricEncryptionKey{memguard.NewEnclave(key), memguard.NewEnclave(macKey)}, nil

View File

@ -7,7 +7,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
@ -105,7 +104,7 @@ func makeAuthenticatedHTTPRequest(ctx context.Context, req *http.Request, recv i
return err
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
if err != nil {
return err
}

View File

@ -69,11 +69,17 @@ func CreateAuthResponse(ctx context.Context, authRequest AuthRequestData, keyrin
//}
publicKey, err := base64.StdEncoding.DecodeString(authRequest.PublicKey)
requesterKey, err := crypto.MemoryAssymmetricEncryptionKeyFromBytes(publicKey)
if err != nil {
return AuthRequestResponseData{}, err
}
requesterKey, err := crypto.MemoryAsymmetricEncryptionKeyFromBytes(publicKey)
if err != nil {
return AuthRequestResponseData{}, err
}
encryptedUserSymmetricKey, err := crypto.EncryptWithAsymmetric(userSymmetricKey, requesterKey)
if err != nil {
panic(err)
return AuthRequestResponseData{}, err
}
//encryptedMasterPasswordHash, err := crypto.EncryptWithAsymmetric(masterPasswordHash, requesterKey)
//if err != nil {

View File

@ -71,7 +71,10 @@ func CreateSend(ctx context.Context, cfg *config.Config, vault *vault.Vault, nam
}
sendUseKeyPairBytes := make([]byte, 64)
hkdf.New(sha256.New, sendSourceKey, []byte("bitwarden-send"), []byte("send")).Read(sendUseKeyPairBytes)
_, err = hkdf.New(sha256.New, sendSourceKey, []byte("bitwarden-send"), []byte("send")).Read(sendUseKeyPairBytes)
if err != nil {
return "", err
}
sendUseKeyPair, err := crypto.MemorySymmetricEncryptionKeyFromBytes(sendUseKeyPairBytes)
if err != nil {

View File

@ -16,16 +16,12 @@ import (
var log = logging.GetLogger("Goldwarden", "Bitwarden API")
const path = "/.cache/goldwarden-vault.json"
func Sync(ctx context.Context, config *config.Config) (models.SyncData, error) {
var sync models.SyncData
if err := authenticatedHTTPGet(ctx, config.ConfigFile.ApiUrl+"/sync", &sync); err != nil {
return models.SyncData{}, fmt.Errorf("could not sync: %v", err)
}
home, _ := os.UserHomeDir()
WriteVault(sync, home+path)
return sync, nil
}
@ -34,15 +30,7 @@ func DoFullSync(ctx context.Context, vault *vault.Vault, config *config.Config,
sync, err := Sync(ctx, config)
if err != nil {
log.Error("Could not sync: %v", err)
if allowCache {
home, _ := os.UserHomeDir()
sync, err = ReadVault(home + path)
if err != nil {
return err
}
} else {
return err
}
return err
} else {
log.Info("Sync successful, initializing keyring and vault...")
}
@ -55,7 +43,10 @@ func DoFullSync(ctx context.Context, vault *vault.Vault, config *config.Config,
}
if userSymmetricKey != nil {
log.Info("Initializing keyring from user symmetric key...")
crypto.InitKeyringFromUserSymmetricKey(vault.Keyring, *userSymmetricKey, sync.Profile.PrivateKey, orgKeys)
err = crypto.InitKeyringFromUserSymmetricKey(vault.Keyring, *userSymmetricKey, sync.Profile.PrivateKey, orgKeys)
if err != nil {
return err
}
}
log.Info("Clearing vault...")

View File

@ -76,6 +76,10 @@ func connectToWebsocket(ctx context.Context, vault *vault.Vault, cfg *config.Con
}
token, err := cfg.GetToken()
if err != nil {
return err
}
var websocketURL = "wss://" + url.Host + url.Path + "/hub?access_token=" + token.AccessToken
c, _, err := websocket.DefaultDialer.Dial(websocketURL, nil)
if err != nil {
@ -88,7 +92,10 @@ func connectToWebsocket(ctx context.Context, vault *vault.Vault, cfg *config.Con
done := make(chan struct{})
//handshake required for official bitwarden implementation
c.WriteMessage(1, []byte(`{"protocol":"messagepack","version":1}`))
err = c.WriteMessage(1, []byte(`{"protocol":"messagepack","version":1}`))
if err != nil {
return err
}
go func() {
for {
@ -123,12 +130,14 @@ func connectToWebsocket(ctx context.Context, vault *vault.Vault, cfg *config.Con
websocketLog.Error("Error getting token %s", err)
break
}
DoFullSync(context.WithValue(ctx, AuthToken{}, token.AccessToken), vault, cfg, nil, false)
break
err = DoFullSync(context.WithValue(ctx, AuthToken{}, token.AccessToken), vault, cfg, nil, false)
if err != nil {
log.Error("could not perform full sync: %s", err.Error())
return
}
case SyncCipherDelete:
websocketLog.Warn("Delete requested for cipher " + cipherid)
vault.DeleteCipher(cipherid)
break
case SyncCipherUpdate:
websocketLog.Warn("Update requested for cipher " + cipherid)
token, err := cfg.GetToken()
@ -154,8 +163,6 @@ func connectToWebsocket(ctx context.Context, vault *vault.Vault, cfg *config.Con
vault.AddOrUpdateLogin(cipher)
}
vault.SetLastSynced(time.Now().Unix())
break
case SyncCipherCreate:
websocketLog.Warn("Create requested for cipher " + cipherid)
token, err := cfg.GetToken()
@ -176,11 +183,8 @@ func connectToWebsocket(ctx context.Context, vault *vault.Vault, cfg *config.Con
vault.AddOrUpdateLogin(cipher)
}
vault.SetLastSynced(time.Now().Unix())
break
case SyncSendCreate, SyncSendUpdate, SyncSendDelete:
websocketLog.Warn("SyncSend requested: sends are not supported")
break
case LogOut:
websocketLog.Info("LogOut received. Wiping vault and exiting...")
if vault.Keyring.IsMemguard {
@ -213,17 +217,12 @@ func connectToWebsocket(ctx context.Context, vault *vault.Vault, cfg *config.Con
websocketLog.Error("Error creating auth response %s", err)
}
})
break
case AuthRequestResponse:
websocketLog.Info("AuthRequestResponse received")
break
case SyncFolderDelete, SyncFolderCreate, SyncFolderUpdate:
websocketLog.Warn("SyncFolder requested: folders are not supported")
break
case SyncOrgKeys, SyncSettings:
websocketLog.Warn("SyncOrgKeys requested: orgs / settings are not supported")
break
default:
websocketLog.Warn("Unknown message type received %d", mt1)
}

View File

@ -7,6 +7,7 @@ import (
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"os"
"runtime/debug"
"strings"
@ -19,6 +20,7 @@ import (
"github.com/quexten/goldwarden/agent/pincache"
"github.com/quexten/goldwarden/agent/systemauth/pinentry"
"github.com/quexten/goldwarden/agent/vault"
"github.com/quexten/goldwarden/logging"
"github.com/tink-crypto/tink-go/v2/aead/subtle"
"golang.org/x/crypto/argon2"
"golang.org/x/crypto/sha3"
@ -78,6 +80,8 @@ type Config struct {
mu sync.Mutex
}
var log = logging.GetLogger("Goldwarden", "Config")
func DefaultConfig(useMemguard bool) Config {
deviceUUID, _ := uuid.NewUUID()
keyBuffer := NewBuffer(32, useMemguard)
@ -188,37 +192,65 @@ func (c *Config) UpdatePin(password string, write bool) {
c.ConfigFile.ConfigKeyHash = configKeyHash
plaintextToken, err1 := c.decryptString(c.ConfigFile.EncryptedToken)
plaintextUserSymmetricKey, err3 := c.decryptString(c.ConfigFile.EncryptedUserSymmetricKey)
plaintextEncryptedMasterPasswordHash, err4 := c.decryptString(c.ConfigFile.EncryptedMasterPasswordHash)
plaintextMasterKey, err5 := c.decryptString(c.ConfigFile.EncryptedMasterKey)
plaintextClientID, err6 := c.decryptString(c.ConfigFile.EncryptedClientID)
plaintextClientSecret, err7 := c.decryptString(c.ConfigFile.EncryptedClientSecret)
plaintextUserSymmetricKey, err2 := c.decryptString(c.ConfigFile.EncryptedUserSymmetricKey)
plaintextEncryptedMasterPasswordHash, err3 := c.decryptString(c.ConfigFile.EncryptedMasterPasswordHash)
plaintextMasterKey, err4 := c.decryptString(c.ConfigFile.EncryptedMasterKey)
plaintextClientID, err5 := c.decryptString(c.ConfigFile.EncryptedClientID)
plaintextClientSecret, err6 := c.decryptString(c.ConfigFile.EncryptedClientSecret)
key := NewBufferFromBytes(newKey, c.useMemguard)
c.key = &key
if err1 == nil {
c.ConfigFile.EncryptedToken, err1 = c.encryptString(plaintextToken)
if err1 != nil {
log.Error("could not encrypt token: %s", err1.Error())
return
}
}
if err2 == nil {
c.ConfigFile.EncryptedUserSymmetricKey, err2 = c.encryptString(plaintextUserSymmetricKey)
if err2 != nil {
log.Error("could not encrypt user symmetric key: %s", err2.Error())
return
}
}
if err3 == nil {
c.ConfigFile.EncryptedUserSymmetricKey, err3 = c.encryptString(plaintextUserSymmetricKey)
c.ConfigFile.EncryptedMasterPasswordHash, err3 = c.encryptString(plaintextEncryptedMasterPasswordHash)
if err3 != nil {
log.Error("could not encrypt master password hash: %s", err3.Error())
return
}
}
if err4 == nil {
c.ConfigFile.EncryptedMasterPasswordHash, err4 = c.encryptString(plaintextEncryptedMasterPasswordHash)
c.ConfigFile.EncryptedMasterKey, err4 = c.encryptString(plaintextMasterKey)
if err4 != nil {
log.Error("could not encrypt master key: %s", err4.Error())
return
}
}
if err5 == nil {
c.ConfigFile.EncryptedMasterKey, err5 = c.encryptString(plaintextMasterKey)
c.ConfigFile.EncryptedClientID, err5 = c.encryptString(plaintextClientID)
if err5 != nil {
log.Error("could not encrypt client id: %s", err5.Error())
return
}
}
if err6 == nil {
c.ConfigFile.EncryptedClientID, err6 = c.encryptString(plaintextClientID)
}
if err7 == nil {
c.ConfigFile.EncryptedClientSecret, err7 = c.encryptString(plaintextClientSecret)
c.ConfigFile.EncryptedClientSecret, err6 = c.encryptString(plaintextClientSecret)
if err6 != nil {
log.Error("could not encrypt client secret: %s", err6.Error())
return
}
}
c.mu.Unlock()
if write {
c.WriteConfig()
err := c.WriteConfig()
if err != nil {
log.Error("could not write config: %s", err.Error())
return
}
}
pincache.SetPin(c.useMemguard, []byte(password))
@ -247,6 +279,9 @@ func (c *Config) SetToken(token LoginToken) error {
}
tokenJson, err := json.Marshal(token)
if err != nil {
return fmt.Errorf("could not marshall json: %s", err.Error())
}
encryptedToken, err := c.encryptString(string(tokenJson))
if err != nil {
return err
@ -254,8 +289,7 @@ func (c *Config) SetToken(token LoginToken) error {
// c.mu.Lock()
c.ConfigFile.EncryptedToken = encryptedToken
// c.mu.Unlock()
c.WriteConfig()
return nil
return c.WriteConfig()
}
func (c *Config) GetClientID() (string, error) {
@ -281,8 +315,7 @@ func (c *Config) SetClientID(clientID string) error {
if clientID == "" {
c.ConfigFile.EncryptedClientID = ""
c.WriteConfig()
return nil
return c.WriteConfig()
}
encryptedClientID, err := c.encryptString(clientID)
@ -292,8 +325,7 @@ func (c *Config) SetClientID(clientID string) error {
// c.mu.Lock()
c.ConfigFile.EncryptedClientID = encryptedClientID
// c.mu.Unlock()
c.WriteConfig()
return nil
return c.WriteConfig()
}
func (c *Config) GetClientSecret() (string, error) {
@ -319,8 +351,7 @@ func (c *Config) SetClientSecret(clientSecret string) error {
if clientSecret == "" {
c.ConfigFile.EncryptedClientSecret = ""
c.WriteConfig()
return nil
return c.WriteConfig()
}
encryptedClientSecret, err := c.encryptString(clientSecret)
@ -330,8 +361,7 @@ func (c *Config) SetClientSecret(clientSecret string) error {
// c.mu.Lock()
c.ConfigFile.EncryptedClientSecret = encryptedClientSecret
// c.mu.Unlock()
c.WriteConfig()
return nil
return c.WriteConfig()
}
func (c *Config) GetUserSymmetricKey() ([]byte, error) {
@ -356,8 +386,7 @@ func (c *Config) SetUserSymmetricKey(key []byte) error {
// c.mu.Lock()
c.ConfigFile.EncryptedUserSymmetricKey = encryptedKey
// c.mu.Unlock()
c.WriteConfig()
return nil
return c.WriteConfig()
}
func (c *Config) GetMasterPasswordHash() ([]byte, error) {
@ -372,7 +401,6 @@ func (c *Config) GetMasterPasswordHash() ([]byte, error) {
}
func (c *Config) SetMasterPasswordHash(hash []byte) error {
if c.IsLocked() {
return errors.New("config is locked")
}
@ -386,8 +414,7 @@ func (c *Config) SetMasterPasswordHash(hash []byte) error {
c.ConfigFile.EncryptedMasterPasswordHash = encryptedHash
// c.mu.Unlock()
c.WriteConfig()
return nil
return c.WriteConfig()
}
func (c *Config) GetMasterKey() ([]byte, error) {
@ -412,8 +439,7 @@ func (c *Config) SetMasterKey(key []byte) error {
// c.mu.Lock()
c.ConfigFile.EncryptedMasterKey = encryptedKey
// c.mu.Unlock()
c.WriteConfig()
return nil
return c.WriteConfig()
}
func (c *Config) encryptString(data string) (string, error) {
@ -499,9 +525,11 @@ func ReadConfig(rtCfg RuntimeConfig) (Config, error) {
if _, err := os.Stat(oldPath); err == nil {
if _, err := os.Stat(newPath); err != nil {
if _, err := os.Stat(newPathParent); os.IsNotExist(err) {
os.Mkdir(newPathParent, 0700)
err = os.Mkdir(newPathParent, 0700)
return Config{}, err
}
os.Rename(oldPath, newPath)
err = os.Rename(oldPath, newPath)
return Config{}, err
}
}

View File

@ -6,15 +6,18 @@ import (
"time"
"github.com/godbus/dbus/v5"
"github.com/quexten/goldwarden/logging"
)
var closeListenerMap = make(map[uint32]func())
var notificationID uint32 = 1000000
var log = logging.GetLogger("Goldwarden", "Dbus")
func Notify(title string, body string, actionName string, timeout time.Duration, onclose func()) error {
func Notify(title string, body string, actionName string, timeout time.Duration, onclose func()) {
bus, err := dbus.SessionBus()
if err != nil {
return err
log.Error("could not get a dbus session: %s", err.Error())
return
}
obj := bus.Object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
actions := []string{}
@ -26,16 +29,17 @@ func Notify(title string, body string, actionName string, timeout time.Duration,
call := obj.Call("org.freedesktop.Notifications.Notify", 0, "goldwarden", uint32(notificationID), "", title, body, actions, map[string]dbus.Variant{}, int32(60000))
if call.Err != nil {
return call.Err
log.Error("could not call dbus object: %s", call.Err.Error())
return
}
if len(call.Body) < 1 {
return nil
return
}
id := call.Body[0].(uint32)
closeListenerMap[id] = onclose
if timeout == 0 {
return nil
return
} else {
go func(id uint32) {
time.Sleep(timeout)
@ -45,8 +49,6 @@ func Notify(title string, body string, actionName string, timeout time.Duration,
}
}(id)
}
return nil
}
func ListenForNotifications() error {
@ -62,26 +64,22 @@ func ListenForNotifications() error {
signals := make(chan *dbus.Signal, 10)
bus.Signal(signals)
for {
select {
case message := <-signals:
if message.Name == "org.freedesktop.Notifications.NotificationClosed" {
if len(message.Body) < 1 {
continue
}
id, ok := message.Body[0].(uint32)
if !ok {
continue
}
if id == 0 {
continue
}
if closeListener, ok := closeListenerMap[id]; ok {
delete(closeListenerMap, id)
closeListener()
}
message := <-signals
if message.Name == "org.freedesktop.Notifications.NotificationClosed" {
if len(message.Body) < 1 {
continue
}
id, ok := message.Body[0].(uint32)
if !ok {
continue
}
if id == 0 {
continue
}
if closeListener, ok := closeListenerMap[id]; ok {
delete(closeListenerMap, id)
closeListener()
}
}
}
return nil
}

View File

@ -16,11 +16,11 @@ func SetPin(useMemguard bool, pin []byte) {
func GetPin() ([]byte, error) {
approved := biometrics.CheckBiometrics(biometrics.SSHKey)
if approved {
bufer, err := cachedPin.Open()
buffer, err := cachedPin.Open()
if err != nil {
return nil, err
}
return bufer.Bytes(), nil
return buffer.Bytes(), nil
} else {
return nil, errors.New("biometrics not approved")
}

View File

@ -37,30 +37,26 @@ func MonitorLocks(onlock func()) error {
signals := make(chan *dbus.Signal, 10)
bus.Signal(signals)
for {
select {
case message := <-signals:
if message.Name == "org.gnome.ScreenSaver.ActiveChanged" {
if len(message.Body) == 0 {
continue
}
locked, err := message.Body[0].(bool)
if err || locked {
onlock()
}
message := <-signals
if message.Name == "org.gnome.ScreenSaver.ActiveChanged" {
if len(message.Body) == 0 {
continue
}
if message.Name == "org.freedesktop.ScreenSaver.ActiveChanged" {
if len(message.Body) == 0 {
continue
}
locked, err := message.Body[0].(bool)
if err || locked {
onlock()
}
locked, err := message.Body[0].(bool)
if err || locked {
onlock()
}
}
if message.Name == "org.freedesktop.ScreenSaver.ActiveChanged" {
if len(message.Body) == 0 {
continue
}
locked, err := message.Body[0].(bool)
if err || locked {
onlock()
}
}
}
return nil
}
func MonitorIdle(onidle func()) error {
@ -88,6 +84,4 @@ func MonitorIdle(onidle func()) error {
time.Sleep(1 * time.Second)
}
return nil
}

View File

@ -5,7 +5,7 @@ import (
"os/user"
gops "github.com/mitchellh/go-ps"
"inet.af/peercred"
"github.com/tailscale/peercred"
)
type CallingContext struct {

View File

@ -81,6 +81,10 @@ func Eq(a, b ssh.PublicKey) bool {
}
func (vaultAgent vaultAgent) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
return vaultAgent.SignWithFlags(key, data, agent.SignatureFlagReserved)
}
func (vaultAgent vaultAgent) SignWithFlags(key ssh.PublicKey, data []byte, flags agent.SignatureFlags) (*ssh.Signature, error) {
log.Info("Sign Request for key: %s", ssh.FingerprintSHA256(key))
if vaultAgent.vault.Keyring.IsLocked() {
if !vaultAgent.unlockRequestAction() {
@ -106,6 +110,10 @@ func (vaultAgent vaultAgent) Sign(key ssh.PublicKey, data []byte) (*ssh.Signatur
}
}
if sshKey == nil {
return nil, errors.New("key not found")
}
isGit := false
magicHeader := []byte("SSHSIG\x00\x00\x00\x03git")
if bytes.HasPrefix(data, magicHeader) {
@ -156,7 +164,30 @@ func (vaultAgent vaultAgent) Sign(key ssh.PublicKey, data []byte) (*ssh.Signatur
} else {
notify.Notify("Goldwarden", fmt.Sprintf("SSH Signing Request Approved for %s", sshKey.Name), "", 10*time.Second, func() {})
}
return signer.Sign(rand, data)
algo := ""
switch flags {
case agent.SignatureFlagRsaSha256:
algo = ssh.KeyAlgoRSASHA256
case agent.SignatureFlagRsaSha512:
algo = ssh.KeyAlgoRSASHA512
default:
return signer.Sign(rand, data)
}
log.Info("%s algorithm requested", algo)
algoSigner, err := ssh.NewSignerWithAlgorithms(signer.(ssh.AlgorithmSigner), []string{algo})
if err != nil {
return nil, err
}
return algoSigner.SignWithAlgorithm(rand, data, algo)
}
func (vaultAgent) Extension(extensionType string, contents []byte) ([]byte, error) {
return nil, agent.ErrExtensionUnsupported
}
func (vaultAgent) Signers() ([]ssh.Signer, error) {

View File

@ -99,7 +99,7 @@ func (pe *Pinentry) Get(arg keybase1.SecretEntryArg) (res *keybase1.SecretEntryR
func (pi *pinentryInstance) Close() {
pi.stdin.Close()
pi.cmd.Wait()
_ = pi.cmd.Wait()
}
type pinentryInstance struct {
@ -123,7 +123,6 @@ func (pi *pinentryInstance) Set(cmd, val string, errp *error) {
if string(line) != "OK" {
*errp = fmt.Errorf("Response to " + cmd + " was " + string(line))
}
return
}
func (pi *pinentryInstance) Init() (err error) {

View File

@ -42,8 +42,7 @@ func GetPassword(title string, description string) (string, error) {
return externalPinentry.GetPassword(title, description)
}
// return "", errors.New("Not implemented")
return password, nil
return password, err
}
func GetApproval(title string, description string) (bool, error) {
@ -56,6 +55,5 @@ func GetApproval(title string, description string) (bool, error) {
return externalPinentry.GetApproval(title, description)
}
// return true, errors.New("Not implemented")
return approval, nil
return approval, err
}

View File

@ -31,20 +31,20 @@ const (
var log = logging.GetLogger("Goldwarden", "Agent")
func writeError(c net.Conn, errMsg error) error {
func writeError(c net.Conn, errMsg error) {
payload := messages.ActionResponse{
Success: false,
Message: errMsg.Error(),
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return err
log.Warn("Could not json marshall: %s", err.Error())
return
}
_, err = c.Write(payloadBytes)
if err != nil {
return err
log.Warn("Could not write payload: %s", err.Error())
}
return nil
}
func serveAgentSession(c net.Conn, vault *vault.Vault, cfg *config.Config) {
@ -145,9 +145,9 @@ func serveAgentSession(c net.Conn, vault *vault.Vault, cfg *config.Config) {
},
}
pinnentrySetError := pinentry.SetExternalPinentry(pe)
pinentrySetError := pinentry.SetExternalPinentry(pe)
payload := messages.PinentryRegistrationResponse{
Success: pinnentrySetError == nil,
Success: pinentrySetError == nil,
}
log.Info("Pinentry registration success: %t", payload.Success)
@ -167,9 +167,12 @@ func serveAgentSession(c net.Conn, vault *vault.Vault, cfg *config.Config) {
log.Error("Failed writing to socket " + err.Error())
}
_, err = c.Write([]byte("\n"))
if err != nil {
log.Error("Failed writing to socket " + err.Error())
}
time.Sleep(50 * time.Millisecond) //todo fix properly
if pinnentrySetError != nil {
if pinentrySetError != nil {
return
}
@ -266,8 +269,6 @@ func serveAgentSession(c net.Conn, vault *vault.Vault, cfg *config.Config) {
}
}
}
continue
}
var responseBytes []byte
@ -322,7 +323,10 @@ func StartUnixAgent(path string, runtimeConfig config.RuntimeConfig) error {
log.Warn("Could not read config: %s", err.Error())
cfg = config.DefaultConfig(runtimeConfig.UseMemguard)
cfg.ConfigFile.RuntimeConfig = runtimeConfig
cfg.WriteConfig()
err = cfg.WriteConfig()
if err != nil {
log.Warn("Could not write config: %s", err.Error())
}
}
cfg.ConfigFile.RuntimeConfig = runtimeConfig
if cfg.ConfigFile.RuntimeConfig.DeviceUUID != "" {
@ -348,14 +352,17 @@ func StartUnixAgent(path string, runtimeConfig config.RuntimeConfig) error {
time.Sleep(60 * time.Second)
continue
}
var protectedUserSymetricKey crypto.SymmetricEncryptionKey
var protectedUserSymmetricKey crypto.SymmetricEncryptionKey
if vault.Keyring.IsMemguard {
protectedUserSymetricKey, err = crypto.MemguardSymmetricEncryptionKeyFromBytes(userSymmetricKey)
protectedUserSymmetricKey, err = crypto.MemguardSymmetricEncryptionKeyFromBytes(userSymmetricKey)
} else {
protectedUserSymetricKey, err = crypto.MemorySymmetricEncryptionKeyFromBytes(userSymmetricKey)
protectedUserSymmetricKey, err = crypto.MemorySymmetricEncryptionKeyFromBytes(userSymmetricKey)
}
if err != nil {
log.Error("could not get encryption key from bytes: %s", err.Error())
}
err = bitwarden.DoFullSync(context.WithValue(ctx, bitwarden.AuthToken{}, token.AccessToken), vault, &cfg, &protectedUserSymetricKey, true)
err = bitwarden.DoFullSync(context.WithValue(ctx, bitwarden.AuthToken{}, token.AccessToken), vault, &cfg, &protectedUserSymmetricKey, true)
if err != nil {
log.Error("Could not sync: %s", err.Error())
notify.Notify("Goldwarden", "Could not perform initial sync", "", 0, func() {})
@ -369,7 +376,11 @@ func StartUnixAgent(path string, runtimeConfig config.RuntimeConfig) error {
}
}
processsecurity.DisableDumpable()
err = processsecurity.DisableDumpable()
if err != nil {
log.Warn("Could not disable dumpable: %s", err.Error())
}
go func() {
err = processsecurity.MonitorLocks(func() {
cfg.Lock()
@ -435,14 +446,17 @@ func StartUnixAgent(path string, runtimeConfig config.RuntimeConfig) error {
if err != nil {
log.Error("Could not get user symmetric key: %s", err.Error())
}
var protectedUserSymetricKey crypto.SymmetricEncryptionKey
var protectedUserSymmetricKey crypto.SymmetricEncryptionKey
if vault.Keyring.IsMemguard {
protectedUserSymetricKey, err = crypto.MemguardSymmetricEncryptionKeyFromBytes(userSymmetricKey)
protectedUserSymmetricKey, err = crypto.MemguardSymmetricEncryptionKeyFromBytes(userSymmetricKey)
} else {
protectedUserSymetricKey, err = crypto.MemorySymmetricEncryptionKeyFromBytes(userSymmetricKey)
protectedUserSymmetricKey, err = crypto.MemorySymmetricEncryptionKeyFromBytes(userSymmetricKey)
}
if err != nil {
log.Error("could not get encryption key from bytes: %s", err.Error())
}
err = bitwarden.DoFullSync(context.WithValue(ctx, bitwarden.AuthToken{}, token.AccessToken), vault, &cfg, &protectedUserSymetricKey, true)
err = bitwarden.DoFullSync(context.WithValue(ctx, bitwarden.AuthToken{}, token.AccessToken), vault, &cfg, &protectedUserSymmetricKey, true)
if err != nil {
log.Error("Could not sync: %s", err.Error())
notify.Notify("Goldwarden", "Could not perform initial sync on ssh unlock", "", 0, func() {})
@ -482,7 +496,11 @@ func StartUnixAgent(path string, runtimeConfig config.RuntimeConfig) error {
continue
}
bitwarden.DoFullSync(context.WithValue(ctx, bitwarden.AuthToken{}, token.AccessToken), vault, &cfg, nil, false)
err = bitwarden.DoFullSync(context.WithValue(ctx, bitwarden.AuthToken{}, token.AccessToken), vault, &cfg, nil, false)
if err != nil {
log.Warn("Could not do full sync: %s", err.Error())
continue
}
}
}
}()

View File

@ -149,9 +149,9 @@ func (vault *Vault) isSSHKey(cipher models.Cipher) bool {
cipherID := cipher.ID.String()
if cipher.OrganizationID != nil {
orgID := cipher.OrganizationID.String()
vaultLog.Error("Failed to decrypt field name with on cipher "+cipherID+" in organization "+orgID, err.Error())
vaultLog.Error("Failed to decrypt field name with on cipher %s in organization %s: %s", cipherID, orgID, err.Error())
} else {
vaultLog.Error("Failed to decrypt field name with on cipher "+cipherID, err.Error())
vaultLog.Error("Failed to decrypt field name with on cipher %s: %s", cipherID, err.Error())
}
continue
}

View File

@ -42,50 +42,47 @@ func TypeString(textToType string) {
var sessionHandle dbus.ObjectPath
for {
select {
case message := <-signals:
if state == 0 {
log.Info("Selecting Devices")
result := message.Body[1].(map[string]dbus.Variant)
resultSessionHandle := result["session_handle"]
sessionHandle = dbus.ObjectPath(resultSessionHandle.String()[1 : len(resultSessionHandle.String())-1])
res := obj.Call("org.freedesktop.portal.RemoteDesktop.SelectDevices", 0, sessionHandle, map[string]dbus.Variant{
"types": dbus.MakeVariant(uint32(1)),
})
if res.Err != nil {
log.Error("Error selecting devices: %s", res.Err.Error())
}
state = 1
} else if state == 1 {
log.Info("Starting Session")
res := obj.Call("org.freedesktop.portal.RemoteDesktop.Start", 0, sessionHandle, "", map[string]dbus.Variant{})
if res.Err != nil {
log.Error("Error starting session: %s", res.Err.Error())
}
state = 2
} else if state == 2 {
log.Info("Performing Typing")
state = 3
time.Sleep(1000 * time.Millisecond)
for _, char := range textToType {
if char == '\t' {
obj.Call("org.freedesktop.portal.RemoteDesktop.NotifyKeyboardKeycode", 0, sessionHandle, map[string]dbus.Variant{}, 15, uint32(1))
time.Sleep(autoTypeDelay)
obj.Call("org.freedesktop.portal.RemoteDesktop.NotifyKeyboardKeycode", 0, sessionHandle, map[string]dbus.Variant{}, 15, uint32(0))
time.Sleep(autoTypeDelay)
} else {
obj.Call("org.freedesktop.portal.RemoteDesktop.NotifyKeyboardKeysym", 0, sessionHandle, map[string]dbus.Variant{}, int32(char), uint32(1))
time.Sleep(autoTypeDelay)
obj.Call("org.freedesktop.portal.RemoteDesktop.NotifyKeyboardKeysym", 0, sessionHandle, map[string]dbus.Variant{}, int32(char), uint32(0))
time.Sleep(autoTypeDelay)
}
}
bus.Close()
return
} else {
log.Info("State 3")
return
message := <-signals
switch state {
case 0:
log.Info("Selecting Devices")
result := message.Body[1].(map[string]dbus.Variant)
resultSessionHandle := result["session_handle"]
sessionHandle = dbus.ObjectPath(resultSessionHandle.String()[1 : len(resultSessionHandle.String())-1])
res := obj.Call("org.freedesktop.portal.RemoteDesktop.SelectDevices", 0, sessionHandle, map[string]dbus.Variant{
"types": dbus.MakeVariant(uint32(1)),
})
if res.Err != nil {
log.Error("Error selecting devices: %s", res.Err.Error())
}
state = 1
case 1:
log.Info("Starting Session")
res := obj.Call("org.freedesktop.portal.RemoteDesktop.Start", 0, sessionHandle, "", map[string]dbus.Variant{})
if res.Err != nil {
log.Error("Error starting session: %s", res.Err.Error())
}
state = 2
case 2:
log.Info("Performing Typing")
time.Sleep(1000 * time.Millisecond)
for _, char := range textToType {
if char == '\t' {
obj.Call("org.freedesktop.portal.RemoteDesktop.NotifyKeyboardKeycode", 0, sessionHandle, map[string]dbus.Variant{}, 15, uint32(1))
time.Sleep(autoTypeDelay)
obj.Call("org.freedesktop.portal.RemoteDesktop.NotifyKeyboardKeycode", 0, sessionHandle, map[string]dbus.Variant{}, 15, uint32(0))
time.Sleep(autoTypeDelay)
} else {
obj.Call("org.freedesktop.portal.RemoteDesktop.NotifyKeyboardKeysym", 0, sessionHandle, map[string]dbus.Variant{}, int32(char), uint32(1))
time.Sleep(autoTypeDelay)
obj.Call("org.freedesktop.portal.RemoteDesktop.NotifyKeyboardKeysym", 0, sessionHandle, map[string]dbus.Variant{}, int32(char), uint32(0))
time.Sleep(autoTypeDelay)
}
}
bus.Close()
return
default:
return
}
}
}

View File

@ -0,0 +1,11 @@
{
"name": "com.8bit.bitwarden",
"description": "Bitwarden desktop <-> browser bridge",
"path": "@PATH@",
"type": "stdio",
"allowed_origins": [
"chrome-extension://nngceckbapebfimnlniiiahkandclblb/",
"chrome-extension://jbkfoedolllekgbhcbcoahefnbanhhlh/",
"chrome-extension://ccnckbpmaceehanjmeomladnmlffdjgn/"
]
}

View File

@ -4,6 +4,7 @@ import (
"bytes"
"encoding/binary"
"encoding/json"
"fmt"
"os"
"unsafe"
@ -15,7 +16,7 @@ const bufferSize = 8192 * 8
var nativeEndian binary.ByteOrder
func setupCommunication() {
// determine native endianess
// determine native endianness
var one int16 = 1
b := (*byte)(unsafe.Pointer(&one))
if *b == 0 {
@ -25,44 +26,50 @@ func setupCommunication() {
}
}
func dataToBytes(msg SendMessage) []byte {
func dataToBytes(msg SendMessage) ([]byte, error) {
byteMsg, err := json.Marshal(msg)
if err != nil {
logging.Panicf("Unable to marshal OutgoingMessage struct to slice of bytes: " + err.Error())
return nil, fmt.Errorf("unable to marshal OutgoingMessage struct to slice of bytes: %w", err)
}
return byteMsg
return byteMsg, nil
}
func writeMessageLength(msg []byte) {
func writeMessageLength(msg []byte) error {
err := binary.Write(os.Stdout, nativeEndian, uint32(len(msg)))
if err != nil {
logging.Panicf("Unable to write message length to Stdout: " + err.Error())
return fmt.Errorf("unable to write message length to stdout: %w", err)
}
return nil
}
func readMessageLength(msg []byte) int {
var length uint32
func readMessageLength(msg []byte) (int, error) {
var length int
buf := bytes.NewBuffer(msg)
err := binary.Read(buf, nativeEndian, &length)
if err != nil {
logging.Panicf("Unable to read bytes representing message length:" + err.Error())
return 0, fmt.Errorf("Unable to read bytes representing message length: %w", err)
}
return int(length)
return length, nil
}
func send(msg SendMessage) {
byteMsg := dataToBytes(msg)
func send(msg SendMessage) error {
byteMsg, err := dataToBytes(msg)
if err != nil {
return err
}
logging.Debugf("[SENSITIVE] Sending message: " + string(byteMsg))
writeMessageLength(byteMsg)
err = writeMessageLength(byteMsg)
if err != nil {
return err
}
var msgBuf bytes.Buffer
_, err := msgBuf.Write(byteMsg)
_, err = msgBuf.Write(byteMsg)
if err != nil {
logging.Panicf(err.Error())
return err
}
_, err = msgBuf.WriteTo(os.Stdout)
if err != nil {
logging.Panicf(err.Error())
}
return err
}

View File

@ -23,7 +23,7 @@ func pkcs7Pad(b []byte, blocksize int) ([]byte, error) {
if blocksize <= 0 {
return nil, ErrInvalidBlockSize
}
if b == nil || len(b) == 0 {
if len(b) == 0 {
return nil, ErrInvalidPKCS7Data
}
n := blocksize - (len(b) % blocksize)
@ -37,7 +37,7 @@ func pkcs7Unpad(b []byte, blocksize int) ([]byte, error) {
if blocksize <= 0 {
return nil, ErrInvalidBlockSize
}
if b == nil || len(b) == 0 {
if len(b) == 0 {
return nil, ErrInvalidPKCS7Data
}
if len(b)%blocksize != 0 {
@ -56,10 +56,10 @@ func pkcs7Unpad(b []byte, blocksize int) ([]byte, error) {
return b[:len(b)-n], nil
}
func decryptStringSymmetric(key []byte, ivb64 string, data string) string {
func decryptStringSymmetric(key []byte, ivb64 string, data string) (string, error) {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
return "", err
}
iv, _ := base64.StdEncoding.DecodeString(ivb64)
ciphertext, _ := base64.StdEncoding.DecodeString(data)
@ -67,19 +67,19 @@ func decryptStringSymmetric(key []byte, ivb64 string, data string) string {
bm.CryptBlocks(ciphertext, ciphertext)
ciphertext, _ = pkcs7Unpad(ciphertext, aes.BlockSize)
return string(ciphertext)
return string(ciphertext), nil
}
func encryptStringSymmetric(key []byte, data []byte) EncryptedString {
func encryptStringSymmetric(key []byte, data []byte) (EncryptedString, error) {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
return EncryptedString{}, err
}
data, _ = pkcs7Pad(data, block.BlockSize())
ciphertext := make([]byte, aes.BlockSize+len(data))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
return EncryptedString{}, err
}
bm := cipher.NewCBCEncrypter(block, iv)
bm.CryptBlocks(ciphertext[aes.BlockSize:], data)
@ -88,15 +88,15 @@ func encryptStringSymmetric(key []byte, data []byte) EncryptedString {
IV: base64.StdEncoding.EncodeToString(ciphertext[:aes.BlockSize]),
Data: base64.StdEncoding.EncodeToString(ciphertext[aes.BlockSize:]),
EncType: 0,
}
}, nil
}
func generateTransportKey() []byte {
func generateTransportKey() ([]byte, error) {
key := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, key); err != nil {
panic(err)
return nil, err
}
return key
return key, nil
}
func rsaEncrypt(keyB64 string, message []byte) (string, error) {

View File

@ -0,0 +1,9 @@
#!/usr/bin/env bash
# Check if the "com.quexten.Goldwarden" Flatpak is installed
if flatpak list | grep -q "com.quexten.Goldwarden"; then
flatpak run --command=goldwarden com.quexten.Goldwarden "$@"
else
# If not installed, attempt to run the local version
goldwarden "$@"
fi

View File

@ -1,6 +1,7 @@
package browserbiometrics
import (
_ "embed"
"fmt"
"os"
"path/filepath"
@ -10,6 +11,15 @@ import (
"github.com/quexten/goldwarden/browserbiometrics/logging"
)
//go:embed mozilla-com.8bit.bitwarden.json
var templateMozilla string
//go:embed chrome-com.8bit.bitwarden.json
var templateChrome string
//go:embed goldwarden-proxy.sh
var proxyScript string
var chromiumPaths = []string{
"~/.config/google-chrome/",
"~/.config/google-chrome-beta/",
@ -26,13 +36,17 @@ const appID = "com.quexten.bw-bio-handler"
var transportKey []byte
func Main(rtCfg *config.RuntimeConfig) {
func Main(rtCfg *config.RuntimeConfig) error {
logging.Debugf("Starting browserbiometrics")
transportKey = generateTransportKey()
var err error
transportKey, err = generateTransportKey()
if err != nil {
return err
}
logging.Debugf("Generated transport key")
setupCommunication()
readLoop(rtCfg)
return readLoop(rtCfg)
}
func DetectAndInstallBrowsers() error {
@ -104,7 +118,7 @@ func detectAndInstallBrowsers(startPath string) error {
} else {
tempPath = strings.TrimPrefix(path, home)
}
if strings.Count(tempPath, "/") > 3 {
if strings.Count(tempPath, "/") > 5 {
return nil
}
@ -112,13 +126,25 @@ func detectAndInstallBrowsers(startPath string) error {
fmt.Printf("Found mozilla-like browser: %s\n", path)
fmt.Println("Removing old manifest and proxy script")
os.Chown(path+"/com.8bit.bitwarden.json", 7, 7)
os.Remove(path + "/com.8bit.bitwarden.json")
os.Chown(path+"/goldwarden-proxy.sh", 7, 7)
os.Remove(path + "/goldwarden-proxy.sh")
err = os.Chown(path+"/com.8bit.bitwarden.json", 7, 7)
if err != nil {
return err
}
err = os.Remove(path + "/com.8bit.bitwarden.json")
if err != nil {
return err
}
err = os.Chown(path+"/goldwarden-proxy.sh", 7, 7)
if err != nil {
return err
}
err = os.Remove(path + "/goldwarden-proxy.sh")
if err != nil {
return err
}
fmt.Println("Writing new manifest")
manifest := strings.Replace(templateMozilla, "PATH", path+"/goldwarden-proxy.sh", 1)
manifest := strings.Replace(templateMozilla, "@PATH@", path+"/goldwarden-proxy.sh", 1)
err = os.WriteFile(path+"/com.8bit.bitwarden.json", []byte(manifest), 0444)
if err != nil {
return err
@ -133,13 +159,25 @@ func detectAndInstallBrowsers(startPath string) error {
fmt.Printf("Found chrome-like browser: %s\n", path)
fmt.Println("Removing old manifest and proxy script")
os.Chown(path+"/com.8bit.bitwarden.json", 7, 7)
os.Remove(path + "/com.8bit.bitwarden.json")
os.Chown(path+"/goldwarden-proxy.sh", 7, 7)
os.Remove(path + "/goldwarden-proxy.sh")
err = os.Chown(path+"/com.8bit.bitwarden.json", 7, 7)
if err != nil {
return err
}
err = os.Remove(path + "/com.8bit.bitwarden.json")
if err != nil {
return err
}
err = os.Chown(path+"/goldwarden-proxy.sh", 7, 7)
if err != nil {
return err
}
err = os.Remove(path + "/goldwarden-proxy.sh")
if err != nil {
return err
}
fmt.Println("Writing new manifest")
manifest := strings.Replace(templateChrome, "PATH", path+"/goldwarden-proxy.sh", 1)
manifest := strings.Replace(templateChrome, "@PATH@", path+"/goldwarden-proxy.sh", 1)
err = os.WriteFile(path+"/com.8bit.bitwarden.json", []byte(manifest), 0444)
if err != nil {
return err

View File

@ -1,34 +0,0 @@
package browserbiometrics
const templateMozilla = `{
"name": "com.8bit.bitwarden",
"description": "Bitwarden desktop <-> browser bridge",
"path": "PATH",
"type": "stdio",
"allowed_extensions": [
"{446900e4-71c2-419f-a6a7-df9c091e268b}"
]
}`
const templateChrome = `{
"name": "com.8bit.bitwarden",
"description": "Bitwarden desktop <-> browser bridge",
"path": "PATH",
"type": "stdio",
"allowed_origins": [
"chrome-extension://nngceckbapebfimnlniiiahkandclblb/",
"chrome-extension://jbkfoedolllekgbhcbcoahefnbanhhlh/",
"chrome-extension://ccnckbpmaceehanjmeomladnmlffdjgn/"
]
}`
const proxyScript = `#!/bin/bash
# Check if the "com.quexten.Goldwarden" Flatpak is installed
if flatpak list | grep -q "com.quexten.Goldwarden"; then
flatpak run --command=goldwarden com.quexten.Goldwarden "$@"
else
# If not installed, attempt to run the local version
goldwarden "$@"
fi
`

View File

@ -0,0 +1,9 @@
{
"name": "com.8bit.bitwarden",
"description": "Bitwarden desktop <-> browser bridge",
"path": "@PATH@",
"type": "stdio",
"allowed_extensions": [
"{446900e4-71c2-419f-a6a7-df9c091e268b}"
]
}

View File

@ -3,6 +3,7 @@ package browserbiometrics
import (
"bufio"
"encoding/json"
"fmt"
"io"
"os"
@ -14,7 +15,7 @@ import (
var runtimeConfig *config.RuntimeConfig
func readLoop(rtCfg *config.RuntimeConfig) {
func readLoop(rtCfg *config.RuntimeConfig) error {
runtimeConfig = rtCfg
v := bufio.NewReader(os.Stdin)
s := bufio.NewReaderSize(v, bufferSize)
@ -23,64 +24,86 @@ func readLoop(rtCfg *config.RuntimeConfig) {
lengthNum := int(0)
logging.Debugf("Sending connected message")
send(SendMessage{
err := send(SendMessage{
Command: "connected",
AppID: appID,
})
if err != nil {
return err
}
logging.Debugf("Starting read loop")
for b, err := s.Read(lengthBytes); b > 0 && err == nil; b, err = s.Read(lengthBytes) {
lengthNum = readMessageLength(lengthBytes)
lengthNum, err = readMessageLength(lengthBytes)
if err != nil {
return err
}
content := make([]byte, lengthNum)
_, err := s.Read(content)
if err != nil && err != io.EOF {
logging.Panicf(err.Error())
return err
}
parseMessage(content)
err = parseMessage(content)
if err != nil {
return err
}
}
return nil
}
func parseMessage(msg []byte) {
func parseMessage(msg []byte) error {
logging.Debugf("Received message: " + string(msg))
var genericMessage GenericRecvMessage
err := json.Unmarshal(msg, &genericMessage)
if err != nil {
logging.Panicf("Unable to unmarshal json to struct: " + err.Error())
return fmt.Errorf("unable to unmarshal json to struct: %w", err)
}
if _, ok := (genericMessage.Message.(map[string]interface{})["command"]); ok {
logging.Debugf("Message is unencrypted")
var unmsg UnencryptedRecvMessage
err := json.Unmarshal(msg, &unmsg)
if err != nil {
logging.Panicf("Unable to unmarshal json to struct: " + err.Error())
return fmt.Errorf("unable to unmarshal json to struct: %w", err)
}
handleUnencryptedMessage(unmsg)
err = handleUnencryptedMessage(unmsg)
if err != nil {
return err
}
} else {
logging.Debugf("Message is encrypted")
var encmsg EncryptedRecvMessage
err := json.Unmarshal(msg, &encmsg)
if err != nil {
logging.Panicf("Unable to unmarshal json to struct: " + err.Error())
return fmt.Errorf("unable to unmarshal json to struct: %w", err)
}
decryptedMessage := decryptStringSymmetric(transportKey, encmsg.Message.IV, encmsg.Message.Data)
decryptedMessage, err := decryptStringSymmetric(transportKey, encmsg.Message.IV, encmsg.Message.Data)
if err != nil {
return err
}
var payloadMsg PayloadMessage
err = json.Unmarshal([]byte(decryptedMessage), &payloadMsg)
if err != nil {
logging.Panicf("Unable to unmarshal json to struct: " + err.Error())
return fmt.Errorf("unable to unmarshal json to struct: %w", err)
}
handlePayloadMessage(payloadMsg, genericMessage.AppID)
err = handlePayloadMessage(payloadMsg, genericMessage.AppID)
if err != nil {
return err
}
}
return nil
}
func handleUnencryptedMessage(msg UnencryptedRecvMessage) {
func handleUnencryptedMessage(msg UnencryptedRecvMessage) error {
logging.Debugf("Received unencrypted message: %+v", msg.Message)
logging.Debugf(" with command: %s", msg.Message.Command)
@ -88,17 +111,22 @@ func handleUnencryptedMessage(msg UnencryptedRecvMessage) {
case "setupEncryption":
sharedSecret, err := rsaEncrypt(msg.Message.PublicKey, transportKey)
if err != nil {
logging.Panicf(err.Error())
return err
}
send(SendMessage{
err = send(SendMessage{
Command: "setupEncryption",
AppID: msg.AppID,
SharedSecret: sharedSecret,
})
break
if err != nil {
return err
}
}
return nil
}
func handlePayloadMessage(msg PayloadMessage, appID string) {
func handlePayloadMessage(msg PayloadMessage, appID string) error {
logging.Debugf("Received unencrypted message: %+v", msg)
switch msg.Command {
@ -108,7 +136,7 @@ func handlePayloadMessage(msg PayloadMessage, appID string) {
home, err := os.UserHomeDir()
if err != nil {
panic(err)
return err
}
if runtimeConfig.GoldwardenSocketPath == "" {
@ -127,16 +155,12 @@ func handlePayloadMessage(msg PayloadMessage, appID string) {
result, err := client.NewUnixSocketClient(runtimeConfig).SendToAgent(messages.GetBiometricsKeyRequest{})
if err != nil {
logging.Errorf("Unable to send message to agent: %s", err.Error())
return
return fmt.Errorf("Unable to send message to agent: %w", err)
}
switch result.(type) {
case messages.GetBiometricsKeyResponse:
if err != nil {
logging.Panicf(err.Error())
}
var key = result.(messages.GetBiometricsKeyResponse).Key
switch result := result.(type) {
case messages.GetBiometricsKeyResponse:
var key = result.Key
var payloadMsg ReceiveMessage = ReceiveMessage{
Command: "biometricUnlock",
Response: "unlocked",
@ -145,18 +169,23 @@ func handlePayloadMessage(msg PayloadMessage, appID string) {
}
payloadStr, err := json.Marshal(payloadMsg)
if err != nil {
logging.Panicf(err.Error())
return err
}
logging.Debugf("Payload: %s", payloadStr)
encStr := encryptStringSymmetric(transportKey, payloadStr)
send(SendMessage{
encStr, err := encryptStringSymmetric(transportKey, payloadStr)
if err != nil {
return err
}
err = send(SendMessage{
AppID: appID,
Message: encStr,
})
break
if err != nil {
return err
}
}
break
}
return nil
}

View File

@ -144,7 +144,7 @@ var setVaultURLCmd = &cobra.Command{
var setURLsAutomaticallyCmd = &cobra.Command{
Use: "set-server",
Short: "Set the urls automatically",
Long: `Set the api/identity/vault/notification urls automaticall from a base url.`,
Long: `Set the api/identity/vault/notification urls automatically from a base url.`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
return

9
cmd/goldwarden.service Normal file
View File

@ -0,0 +1,9 @@
[Unit]
Description="Goldwarden daemon"
After=graphical-session.target
[Service]
ExecStart=@BINARY_PATH@ daemonize
[Install]
WantedBy=graphical-session.target

View File

@ -13,7 +13,7 @@ import (
var loginCmd = &cobra.Command{
Use: "login",
Short: "Starts the login process for Bitwarden",
Long: `Starts the login process for Bitwarden.
Long: `Starts the login process for Bitwarden.
You will be prompted to enter your password, and confirm your second factor if you have one.`,
Run: func(cmd *cobra.Command, args []string) {
request := messages.DoLoginRequest{}
@ -49,6 +49,6 @@ var loginCmd = &cobra.Command{
func init() {
vaultCmd.AddCommand(loginCmd)
loginCmd.PersistentFlags().String("email", "", "")
loginCmd.MarkFlagRequired("email")
_ = loginCmd.MarkFlagRequired("email")
loginCmd.PersistentFlags().Bool("passwordless", false, "")
}

View File

@ -4,6 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
"os"
"strings"
"github.com/icza/gox/stringsx"
@ -17,7 +18,7 @@ var baseLoginCmd = &cobra.Command{
Short: "Commands for managing logins.",
Long: `Commands for managing logins.`,
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
_ = cmd.Help()
},
}
@ -26,7 +27,11 @@ var getLoginCmd = &cobra.Command{
Short: "Gets a login in your vault",
Long: `Gets a login in your vault.`,
Run: func(cmd *cobra.Command, args []string) {
loginIfRequired()
err := loginIfRequired()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
uuid, _ := cmd.Flags().GetString("uuid")
name, _ := cmd.Flags().GetString("name")
@ -51,7 +56,7 @@ var getLoginCmd = &cobra.Command{
} else {
fmt.Println(response.Result.Password)
}
break
return
case messages.ActionResponse:
fmt.Println("Error: " + resp.(messages.ActionResponse).Message)
return
@ -64,7 +69,11 @@ var listLoginsCmd = &cobra.Command{
Short: "Lists all logins in your vault",
Long: `Lists all logins in your vault.`,
Run: func(cmd *cobra.Command, args []string) {
loginIfRequired()
err := loginIfRequired()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
logins, err := ListLogins(commandClient)
if err != nil {

View File

@ -54,7 +54,7 @@ var runCmd = &cobra.Command{
command.Stdout = os.Stdout
command.Stderr = os.Stderr
command.Stdin = os.Stdin
command.Run()
_ = command.Run()
},
}

View File

@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"os"
"github.com/quexten/goldwarden/ipc/messages"
"github.com/spf13/cobra"
@ -12,7 +13,7 @@ var sendCmd = &cobra.Command{
Short: "Commands for managing sends",
Long: `Commands for managing sends.`,
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
_ = cmd.Help()
},
}
@ -21,7 +22,12 @@ var sendCreateCmd = &cobra.Command{
Short: "Uploads a Bitwarden send.",
Long: `Uploads a Bitwarden send.`,
Run: func(cmd *cobra.Command, args []string) {
loginIfRequired()
err := loginIfRequired()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
name, _ := cmd.Flags().GetString("name")
text, _ := cmd.Flags().GetString("text")
@ -37,7 +43,7 @@ var sendCreateCmd = &cobra.Command{
switch result.(type) {
case messages.CreateSendResponse:
fmt.Println("Send created: " + result.(messages.CreateSendResponse).URL)
break
return
case messages.ActionResponse:
fmt.Println("Error: " + result.(messages.ActionResponse).Message)
return

View File

@ -23,7 +23,7 @@ var sessionCmd = &cobra.Command{
text = strings.TrimSuffix(text, "\n")
args := strings.Split(text, " ")
rootCmd.SetArgs(args)
rootCmd.Execute()
_ = rootCmd.Execute()
}
},
}
@ -64,6 +64,9 @@ var pinentry = &cobra.Command{
case messages.PinentryApprovalRequest:
err = conn.WriteMessage(messages.PinentryApprovalResponse{Approved: text == "true"})
}
if err != nil {
panic(err)
}
}
},
}

View File

@ -3,6 +3,7 @@
package cmd
import (
_ "embed"
"fmt"
"log"
"os"
@ -81,15 +82,8 @@ var polkitCmd = &cobra.Command{
},
}
const SYSTEMD_SERVICE = `[Unit]
Description="Goldwarden daemon"
After=graphical-session.target
[Service]
ExecStart=BINARY_PATH daemonize
[Install]
WantedBy=graphical-session.target`
//go:embed goldwarden.service
var systemdService string
func setupSystemd() {
if isRoot() {
@ -107,7 +101,10 @@ func setupSystemd() {
panic(err)
}
file.WriteString(strings.ReplaceAll(SYSTEMD_SERVICE, "BINARY_PATH", path))
_, err = file.WriteString(strings.ReplaceAll(systemdService, "@BINARY_PATH@", path))
if err != nil {
panic(err)
}
file.Close()
userDirectory := os.Getenv("HOME")
@ -178,7 +175,7 @@ var setupCmd = &cobra.Command{
Short: "Sets up Goldwarden integrations",
Long: "Sets up Goldwarden integrations",
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
_ = cmd.Help()
},
}

View File

@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"os"
"github.com/atotto/clipboard"
"github.com/quexten/goldwarden/ipc/messages"
@ -13,7 +14,7 @@ var sshCmd = &cobra.Command{
Short: "Commands for managing SSH keys",
Long: `Commands for managing SSH keys.`,
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
_ = cmd.Help()
},
}
@ -24,7 +25,11 @@ var sshAddCmd = &cobra.Command{
Long: `Runs a command with environment variables from your vault.
The variables are stored as a secure note. Consult the documentation for more information.`,
Run: func(cmd *cobra.Command, args []string) {
loginIfRequired()
err := loginIfRequired()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
name, _ := cmd.Flags().GetString("name")
copyToClipboard, _ := cmd.Flags().GetBool("clipboard")
@ -43,9 +48,12 @@ var sshAddCmd = &cobra.Command{
fmt.Println(response.Digest)
if copyToClipboard {
clipboard.WriteAll(string(response.Digest))
err := clipboard.WriteAll(string(response.Digest))
if err != nil {
panic(err)
}
}
break
return
case messages.ActionResponse:
fmt.Println("Error: " + result.(messages.ActionResponse).Message)
return
@ -58,7 +66,11 @@ var listSSHCmd = &cobra.Command{
Short: "Lists all SSH keys in your vault",
Long: `Lists all SSH keys in your vault.`,
Run: func(cmd *cobra.Command, args []string) {
loginIfRequired()
err := loginIfRequired()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
result, err := commandClient.SendToAgent(messages.GetSSHKeysRequest{})
if err != nil {
@ -72,7 +84,7 @@ var listSSHCmd = &cobra.Command{
for _, key := range response.Keys {
fmt.Println(key)
}
break
return
case messages.ActionResponse:
fmt.Println("Error: " + result.(messages.ActionResponse).Message)
return
@ -84,7 +96,7 @@ func init() {
rootCmd.AddCommand(sshCmd)
sshCmd.AddCommand(sshAddCmd)
sshAddCmd.PersistentFlags().String("name", "", "")
sshAddCmd.MarkFlagRequired("name")
_ = sshAddCmd.MarkFlagRequired("name")
sshAddCmd.PersistentFlags().Bool("clipboard", false, "Copy the public key to the clipboard")
sshCmd.AddCommand(listSSHCmd)
}

32
go.mod
View File

@ -1,45 +1,46 @@
module github.com/quexten/goldwarden
go 1.20
go 1.22
toolchain go1.22.1
require (
github.com/LlamaNite/llamalog v0.2.1
github.com/Microsoft/go-winio v0.6.1
github.com/Microsoft/go-winio v0.6.2
github.com/amenzhinsky/go-polkit v0.0.0-20210519083301-ee6a51849123
github.com/atotto/clipboard v0.1.4
github.com/awnumar/memguard v0.22.4
github.com/awnumar/memguard v0.22.5
github.com/gen2brain/beeep v0.0.0-20240112042604-c7bb2cd88fea
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4
github.com/google/uuid v1.5.0
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.1
github.com/icza/gox v0.0.0-20230924165045-adcb03233bb5
github.com/keybase/client/go v0.0.0-20240202160538-668db6be75e4
github.com/keybase/client/go v0.0.0-20240424154521-52f30ea26cb1
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/spf13/cobra v1.8.0
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4
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
golang.org/x/sys v0.16.0
golang.org/x/crypto v0.22.0
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f
golang.org/x/sys v0.19.0
)
require (
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/keybase/backoff v1.0.1-0.20160517061000-726b63b835ec // indirect
github.com/keybase/clockwork v0.1.1-0.20161209210251-976f45f4a979 // indirect
github.com/keybase/go-codec v0.0.0-20180928230036-164397562123 // indirect
github.com/keybase/go-framed-msgpack-rpc v0.0.0-20230103225103-1f052922b096 // indirect
github.com/keybase/go-framed-msgpack-rpc v0.0.0-20231213201819-78fffcb30e42 // indirect
github.com/keybase/go-jsonw v0.0.0-20200325173637-df90f282c233 // indirect
github.com/keybase/go-logging v0.0.0-20231213204715-4b3ff33ba5b6 // indirect
github.com/keybase/msgpackzip v0.0.0-20221220225959-4abf538d2b9c // indirect
github.com/keybase/msgpackzip v0.0.0-20231213201432-ee2f464d1f46 // indirect
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/tools v0.16.1 // indirect
golang.org/x/net v0.24.0 // indirect
)
require (
@ -50,8 +51,7 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rs/zerolog v1.31.0 // indirect
github.com/rs/zerolog v1.32.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
inet.af/peercred v0.0.0-20210906144145-0893ea02156a
)

61
go.sum
View File

@ -1,23 +1,28 @@
github.com/LlamaNite/llamalog v0.2.1 h1:k9XugHmyQqJhCrogca808Jl2rrEKIWMtWyLKX+xX9Mg=
github.com/LlamaNite/llamalog v0.2.1/go.mod h1:zopgmWk8utZPfZCPa/uvQkv99Lan3pRrw/9inbIYZeo=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk=
github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/amenzhinsky/go-polkit v0.0.0-20210519083301-ee6a51849123 h1:VdNhe94PF9yn6KudYnpcBb6bH7l+wsEy9yn6Ulm1/j8=
github.com/amenzhinsky/go-polkit v0.0.0-20210519083301-ee6a51849123/go.mod h1:CdMR3dsiNi5M2BbtFlMo85mRbNt6LiMw04UBzJmoVEU=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/awnumar/memcall v0.2.0 h1:sRaogqExTOOkkNwO9pzJsL8jrOV29UuUW7teRMfbqtI=
github.com/awnumar/memcall v0.2.0/go.mod h1:S911igBPR9CThzd/hYQQmTc9SWNu3ZHIlCGaWsWsoJo=
github.com/awnumar/memguard v0.22.4 h1:1PLgKcgGPeExPHL8dCOWGVjIbQUBgJv9OL0F/yE1PqQ=
github.com/awnumar/memguard v0.22.4/go.mod h1:+APmZGThMBWjnMlKiSM1X7MVpbIVewen2MTkqWkA/zE=
github.com/awnumar/memguard v0.22.5 h1:PH7sbUVERS5DdXh3+mLo8FDcl1eIeVjJVYMnyuYpvuI=
github.com/awnumar/memguard v0.22.5/go.mod h1:+APmZGThMBWjnMlKiSM1X7MVpbIVewen2MTkqWkA/zE=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/gen2brain/beeep v0.0.0-20240112042604-c7bb2cd88fea h1:oWUHxzaBvwkRWiINbBOY39XIF+n9b4RJEPHdQ8waJUo=
github.com/gen2brain/beeep v0.0.0-20240112042604-c7bb2cd88fea/go.mod h1:0W7dI87PvXJ1Sjs0QPvWXKcQmNERY77e8l7GFhZB/s4=
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 h1:qZNfIGkIANxGv/OqtnntR4DfOY2+BgwR60cAcu/i3SE=
@ -26,11 +31,13 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/icza/gox v0.0.0-20230924165045-adcb03233bb5 h1:K7KEFpKgVcjj98jOu2Z3xMBTtTwfYVT90Zmo3ZuWmbE=
github.com/icza/gox v0.0.0-20230924165045-adcb03233bb5/go.mod h1:VbcN86fRkkUMPX2ufM85Um8zFndLZswoIW1eYtpAcVk=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@ -39,18 +46,24 @@ github.com/keybase/backoff v1.0.1-0.20160517061000-726b63b835ec h1:D6qL2WCnAuxuc
github.com/keybase/backoff v1.0.1-0.20160517061000-726b63b835ec/go.mod h1:jeBKj+20GIDry3doFsAMYH9n7Y3l7ajE3xJrKvVB23s=
github.com/keybase/client/go v0.0.0-20240202160538-668db6be75e4 h1:MPUIAszF69Y8gZicAYxzDfrL5G3z/aKyfag8nPSq3js=
github.com/keybase/client/go v0.0.0-20240202160538-668db6be75e4/go.mod h1:V3kb71GXhlWkJjB906M5xyiCqO7ygb4R6peCA+CMHtw=
github.com/keybase/client/go v0.0.0-20240424154521-52f30ea26cb1 h1:toSyN3iQ996iZzFWHYfiXN20wzcavqQUJ171t+n6LqI=
github.com/keybase/client/go v0.0.0-20240424154521-52f30ea26cb1/go.mod h1:NHwdggxLBh4RNQzpCcI0PNArhokrX4Oj4TpnZ8/YIEM=
github.com/keybase/clockwork v0.1.1-0.20161209210251-976f45f4a979 h1:WABVkjKJ3UjbSTgGayemkXfUyZrDwFShivsoIikbM3c=
github.com/keybase/clockwork v0.1.1-0.20161209210251-976f45f4a979/go.mod h1:2j97e0ZjlWYV7dDdV8BjKwMUmBbXu6zZF8FAa9gXRss=
github.com/keybase/go-codec v0.0.0-20180928230036-164397562123 h1:yg56lYPqh9suJepqxOMd/liFgU/x+maRPiB30JNYykM=
github.com/keybase/go-codec v0.0.0-20180928230036-164397562123/go.mod h1:r/eVVWCngg6TsFV/3HuS9sWhDkAzGG8mXhiuYA+Z/20=
github.com/keybase/go-framed-msgpack-rpc v0.0.0-20230103225103-1f052922b096 h1:rMDGkwIszgGP7HodB/YdMVT39mMI5s+LUI6DOrJO0DE=
github.com/keybase/go-framed-msgpack-rpc v0.0.0-20230103225103-1f052922b096/go.mod h1:XO67nMjltHJ8OsBWnFiDU1F67wR+rtJB21NXtb1TKyA=
github.com/keybase/go-framed-msgpack-rpc v0.0.0-20231213201819-78fffcb30e42 h1:r1ZY/j20qhaxcnLXRYf4YJ1hVHxOUqkcXW1AWbtlwRc=
github.com/keybase/go-framed-msgpack-rpc v0.0.0-20231213201819-78fffcb30e42/go.mod h1:oJqYYlOzuyghCL4YfZ0hDLZOyJYcjpRYc2S79aitIBE=
github.com/keybase/go-jsonw v0.0.0-20200325173637-df90f282c233 h1:zLk+cB/0ShMCBcgBOXYgellLZiZahXFicJleKyrlqiM=
github.com/keybase/go-jsonw v0.0.0-20200325173637-df90f282c233/go.mod h1:lofKQwj13L0/7ji5VYaY0257JDlQE2BRRf+rI2Vk1rU=
github.com/keybase/go-logging v0.0.0-20231213204715-4b3ff33ba5b6 h1:H4IvZdHXpeK963LgCMbTcEviEal4891UGf2iOqOGL94=
github.com/keybase/go-logging v0.0.0-20231213204715-4b3ff33ba5b6/go.mod h1:0yOEB+QF1Ega1Cr7oMKb3yUAc3C9/eg6fBHB5HLP7AA=
github.com/keybase/msgpackzip v0.0.0-20221220225959-4abf538d2b9c h1:PRG2AXSelSy7MiDI+PwJR2QSqI1N3OybRUutsMiHtpo=
github.com/keybase/msgpackzip v0.0.0-20221220225959-4abf538d2b9c/go.mod h1:DkylHDco/FLr1+GM6wg0GF4E3CCKov54MSYojKYAbS0=
github.com/keybase/msgpackzip v0.0.0-20231213201432-ee2f464d1f46 h1:HZwgh9xppsgOtC/KFuPOUKqtR70tmqoZKDdzFzMgofk=
github.com/keybase/msgpackzip v0.0.0-20231213201432-ee2f464d1f46/go.mod h1:yBUdIgz7FWt9P/poJyPJ75v9WPDITnKGfwive77P8O4=
github.com/keys-pub/go-libfido2 v1.5.4-0.20230628153049-536daffdd394 h1:zf+3yRJH5NIVOhLceS4P6AVQWEgQPhMCpxgMSB46HdI=
github.com/keys-pub/go-libfido2 v1.5.4-0.20230628153049-536daffdd394/go.mod h1:92J9LtSBl0UyUWljElJpTbMMNhC6VeY8dshsu40qjjo=
github.com/lox/go-touchid v0.0.0-20170712105233-619cc8e578d0 h1:m81erW+1MD5vl3lKQ/+TYPHJ6Y9/C1COqxXPE51FkDk=
@ -72,10 +85,14 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/reiver/go-oi v1.0.0 h1:nvECWD7LF+vOs8leNGV/ww+F2iZKf3EYjYZ527turzM=
github.com/reiver/go-oi v1.0.0/go.mod h1:RrDBct90BAhoDTxB1fenZwfykqeGvhI6LsNfStJoEkI=
github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e h1:quuzZLi72kkJjl+f5AQ93FMcadG19WkS7MO6TXFOSas=
github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e/go.mod h1:+5vNVvEWwEIx86DB9Ke/+a5wBI464eDRo3eF0LcfpWg=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
@ -84,8 +101,11 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk=
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o=
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 h1:Gz0rz40FvFVLTBk/K8UNAenb36EbDSnh+q7Z9ldcC8w=
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4/go.mod h1:phI29ccmHQBc+wvroosENp1IF9195449VDnFDhJ4rJU=
github.com/tink-crypto/tink-go/v2 v2.1.0 h1:QXFBguwMwTIaU17EgZpEJWsUSc60b1BAGTzBIoMdmok=
github.com/tink-crypto/tink-go/v2 v2.1.0/go.mod h1:y1TnYFt1i2eZVfx4OGc+C+EMp4CoKWAw2VSEuoicHHI=
github.com/twpayne/go-pinentry v0.3.0 h1:Rr+fEOZXmeItOb4thjeVaBWJKB9Xa/eojolycyF/26c=
@ -94,30 +114,31 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 h1:+iq7lrkxmFNBM7xx+Rae2W6uyPfhPeDWD+n+JgppptE=
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210301091718-77cc2087c03b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
inet.af/peercred v0.0.0-20210906144145-0893ea02156a h1:qdkS8Q5/i10xU2ArJMKYhVa1DORzBfYS/qA2UK2jheg=
inet.af/peercred v0.0.0-20210906144145-0893ea02156a/go.mod h1:FjawnflS/udxX+SvpsMgZfdqx2aykOlkISeAsADi5IU=

View File

@ -36,6 +36,7 @@
<developer_name>Bernd Schoolmann</developer_name>
<update_contact>mail@quexten.com</update_contact>
<releases>
<release version="0.2.14" date="2024-04-28"/>
<release version="0.2.13" date="2024-02-23"/>
<release version="0.2.12" date="2024-02-17"/>
<release version="0.2.9" date="2024-01-04"/>

View File

@ -109,7 +109,7 @@ class MainWindow(Gtk.ApplicationWindow):
self.results_list.hide()
keycont = Gtk.EventControllerKey()
def handle_keypress(cotroller, keyval, keycode, state, user_data):
def handle_keypress(controller, keyval, keycode, state, user_data):
ctrl_pressed = state & Gdk.ModifierType.CONTROL_MASK > 0
alt_pressed = state & Gdk.ModifierType.ALT_MASK > 0
@ -155,7 +155,7 @@ class MainWindow(Gtk.ApplicationWindow):
if ctrl_pressed and alt_pressed:
Gtk.show_uri(None, self.results_list.get_selected_row().uri, Gdk.CURRENT_TIME)
elif keyval == 116:
totp_code = totp.totp(self.resuts_list.get_selected_row().totp)
totp_code = totp.totp(self.results_list.get_selected_row().totp)
if ctrl_pressed and not alt_pressed:
set_clipboard(totp_code)
if ctrl_pressed and alt_pressed:

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python3
import sys
import gi
gi.require_version('Gtk', '4.0')
@ -279,10 +279,10 @@ def show_login():
def handle_res():
if res == "ok":
dialog.close()
elif res == "badpass":
bad_pass_diag = Gtk.MessageDialog(transient_for=dialog, modal=True, message_type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK, text="Bad password")
bad_pass_diag.connect("response", lambda dialog, response: bad_pass_diag.close())
bad_pass_diag.present()
# elif res == "badpass":
# bad_pass_diag = Gtk.MessageDialog(transient_for=dialog, modal=True, message_type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK, text="Bad password")
# bad_pass_diag.connect("response", lambda dialog, response: bad_pass_diag.close())
# bad_pass_diag.present()
GLib.idle_add(handle_res)
login_thread = Thread(target=login)

View File

@ -47,7 +47,7 @@ class MainWindow(Gtk.ApplicationWindow):
self.ssh_socket_path_group = Adw.PreferencesGroup()
self.ssh_socket_path_group.set_title("SSH Socket Path")
self.ssh_socket_path_group.set_description("Add this to your your enviorment variables")
self.ssh_socket_path_group.set_description("Add this to your environment variables")
self.preferences_page.add(self.ssh_socket_path_group)
self.ssh_socket_path_row = Adw.ActionRow()

View File

@ -0,0 +1,20 @@
import os
import subprocess
is_flatpak = os.path.exists("/.flatpak-info")
def register_autostart(autostart: bool):
if is_flatpak:
print("Running in flatpak, registering with background portal for autostart.")
try:
subprocess.Popen(["python3", "/app/bin/src/linux/flatpak/autostart.py"], start_new_session=True)
except:
pass
def set_status(status: str):
if is_flatpak:
try:
subprocess.Popen(["python3", "/app/bin/src/linux/flatpak/status.py", status], start_new_session=True)
except:
pass

View File

@ -1,17 +1,16 @@
import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
import gc
from gi.repository import Gtk, Adw, GLib, Gio
from gi.repository import GLib, Gio
from random import randint
import time
import os
import sys
from threading import Timer
def receive_autostart(self, *args):
print("autostart enabled..!?")
print(args)
os._exit(0)
sys.exit(0)
def request_autostart():
bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)

View File

@ -0,0 +1,37 @@
"""
Script to set the status of the background process.
Run separately so that gtk dependencies don't stick around in memory.
"""
import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import GLib, Gio
import sys
def set_status(message):
bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
proxy = Gio.DBusProxy.new_sync(
bus,
Gio.DBusProxyFlags.NONE,
None,
'org.freedesktop.portal.Desktop',
'/org/freedesktop/portal/desktop',
'org.freedesktop.portal.Background',
None,
)
options = {
'message': GLib.Variant('s', message),
}
try:
request = proxy.SetStatus('(a{sv})', options)
sys.exit(0)
except Exception as e:
print(e)
if len(sys.argv) > 1:
set_status(sys.argv[1])
loop = GLib.MainLoop()
loop.run()

View File

@ -1,9 +1,10 @@
#!/usr/bin/python
#!/usr/bin/env python3
import time
import subprocess
from tendo import singleton
from .monitors import dbus_autofill_monitor
from .monitors import dbus_monitor
from .monitors import locked_monitor
import sys
from src.services import goldwarden
from src.services import pinentry
@ -12,6 +13,7 @@ import os
import secrets
import time
import os
import src.linux.flatpak.api as flatpak_api
root_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir, os.pardir))
is_flatpak = os.path.exists("/.flatpak-info")
@ -37,6 +39,7 @@ def main():
# start daemons
dbus_autofill_monitor.run_daemon(token) # todo: remove after migration
dbus_monitor.run_daemon(token)
locked_monitor.run_daemon(token)
pinentry.daemonize()
if not "--hidden" in sys.argv:
@ -44,13 +47,7 @@ def main():
p.stdin.write(f"{token}\n".encode())
p.stdin.flush()
if is_flatpak:
# to autostart the appes
try:
print("Enabling autostart...")
subprocess.Popen(["python3", "-m", "src.linux.background"], cwd=root_path, start_new_session=True)
except Exception as e:
pass
flatpak_api.register_autostart(True)
while True:
time.sleep(60)

View File

@ -0,0 +1,30 @@
from gi.repository import Gtk
import dbus
import dbus.service
from dbus.mainloop.glib import DBusGMainLoop
from threading import Thread
import subprocess
import os
from src.services import goldwarden
import time
import src.linux.flatpak.api as flatpak_api
daemon_token = None
def daemon():
time.sleep(5)
goldwarden.create_authenticated_connection(daemon_token)
while True:
status = goldwarden.get_vault_status()
if status["locked"]:
flatpak_api.set_status("Locked")
else:
flatpak_api.set_status("Unlocked")
time.sleep(1)
def run_daemon(token):
print("running locked status daemon")
global daemon_token
daemon_token = token
thread = Thread(target=daemon)
thread.start()

View File

@ -37,6 +37,7 @@ if BINARY_PATH is None:
authenticated_connection = None
def create_authenticated_connection(token):
print("create authenticated connection")
global authenticated_connection
authenticated_connection = subprocess.Popen([f"{BINARY_PATH}", "session"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if not token == None:

View File

@ -45,8 +45,10 @@ func main() {
}
if len(os.Args) > 1 && (strings.Contains(os.Args[1], "com.8bit.bitwarden.json") || strings.Contains(os.Args[1], "chrome-extension://")) {
browserbiometrics.Main(&runtimeConfig)
return
err = browserbiometrics.Main(&runtimeConfig)
if err != nil {
panic(err)
}
}
cmd.Execute(runtimeConfig)