mirror of
https://github.com/quexten/goldwarden.git
synced 2024-09-11 19:17:33 +03:00
This commit is contained in:
commit
2cd7fe75ec
6
.github/dependabot.yaml
vendored
Normal file
6
.github/dependabot.yaml
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: gomod
|
||||
directory: /
|
||||
schedule:
|
||||
interval: monthly
|
8
.github/workflows/_typos.toml
vendored
Normal file
8
.github/workflows/_typos.toml
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
[default]
|
||||
extend-ignore-identifiers-re = [
|
||||
# upstream go library issue
|
||||
".*Encrypter",
|
||||
]
|
||||
|
||||
[files]
|
||||
extend-exclude = ["go.mod"]
|
4
.github/workflows/flatpak.yml
vendored
4
.github/workflows/flatpak.yml
vendored
@ -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 }}
|
||||
|
20
.github/workflows/go.yml
vendored
20
.github/workflows/go.yml
vendored
@ -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:
|
||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -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
15
.github/workflows/typos.yaml
vendored
Normal 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'
|
2
PKGBUILD
2
PKGBUILD
@ -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')
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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{
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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{
|
||||
|
@ -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")
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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...")
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
"os/user"
|
||||
|
||||
gops "github.com/mitchellh/go-ps"
|
||||
"inet.af/peercred"
|
||||
"github.com/tailscale/peercred"
|
||||
)
|
||||
|
||||
type CallingContext struct {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
11
browserbiometrics/chrome-com.8bit.bitwarden.json
Normal file
11
browserbiometrics/chrome-com.8bit.bitwarden.json
Normal 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/"
|
||||
]
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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) {
|
||||
|
9
browserbiometrics/goldwarden-proxy.sh
Normal file
9
browserbiometrics/goldwarden-proxy.sh
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
`
|
9
browserbiometrics/mozilla-com.8bit.bitwarden.json
Normal file
9
browserbiometrics/mozilla-com.8bit.bitwarden.json
Normal 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}"
|
||||
]
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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
9
cmd/goldwarden.service
Normal file
@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description="Goldwarden daemon"
|
||||
After=graphical-session.target
|
||||
|
||||
[Service]
|
||||
ExecStart=@BINARY_PATH@ daemonize
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical-session.target
|
@ -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, "")
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -54,7 +54,7 @@ var runCmd = &cobra.Command{
|
||||
command.Stdout = os.Stdout
|
||||
command.Stderr = os.Stderr
|
||||
command.Stdin = os.Stdin
|
||||
command.Run()
|
||||
_ = command.Run()
|
||||
},
|
||||
}
|
||||
|
||||
|
12
cmd/send.go
12
cmd/send.go
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -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()
|
||||
},
|
||||
}
|
||||
|
||||
|
26
cmd/ssh.go
26
cmd/ssh.go
@ -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
32
go.mod
@ -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
61
go.sum
@ -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=
|
||||
|
@ -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"/>
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
20
gui/src/linux/flatpak/api.py
Normal file
20
gui/src/linux/flatpak/api.py
Normal 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
|
@ -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)
|
37
gui/src/linux/flatpak/status.py
Normal file
37
gui/src/linux/flatpak/status.py
Normal 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()
|
@ -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)
|
||||
|
30
gui/src/linux/monitors/locked_monitor.py
Normal file
30
gui/src/linux/monitors/locked_monitor.py
Normal 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()
|
@ -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:
|
||||
|
6
main.go
6
main.go
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user