diff --git a/.github/com.quexten.Goldwarden.yml b/.github/com.quexten.Goldwarden.yml
index 67bdcc4..f15c81a 100644
--- a/.github/com.quexten.Goldwarden.yml
+++ b/.github/com.quexten.Goldwarden.yml
@@ -40,7 +40,7 @@ modules:
- cp -R ./* /app/bin
- chmod +x /app/bin/goldwarden_ui_main.py
- install -D ./com.quexten.Goldwarden.desktop /app/share/applications/com.quexten.Goldwarden.desktop
- - install -D ./goldwarden.svg /app/share/icons/hicolor/scalable/apps/com.quexten.Goldwarden.svg
+ - install -D ./com.quexten.Goldwarden.svg /app/share/icons/hicolor/scalable/apps/com.quexten.Goldwarden.svg
- install -Dm644 ./com.quexten.Goldwarden.metainfo.xml -t /app/share/metainfo/
- blueprint-compiler batch-compile /app/bin/src/gui/.templates/ /app/bin/src/gui/ /app/bin/src/gui/*.blp
sources:
diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml
index 05fdf3c..3c088e1 100644
--- a/.github/workflows/flatpak.yml
+++ b/.github/workflows/flatpak.yml
@@ -19,7 +19,7 @@ jobs:
run: sudo apt-get install -y libfido2-dev
- name: Build
run: go build -o goldwarden -v .
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
with:
name: goldwarden
path: ./goldwarden
@@ -37,7 +37,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Download daemon
- uses: actions/download-artifact@v2
+ uses: actions/download-artifact@v4
with:
name: goldwarden
- uses: flatpak/flatpak-github-actions/flatpak-builder@v6
diff --git a/Readme.md b/Readme.md
index 16e5140..7dab9f2 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1,4 +1,4 @@
-
+
# Goldwarden
diff --git a/cli/agent/bitwarden/http.go b/cli/agent/bitwarden/http.go
index 3fef11d..cdaefc3 100644
--- a/cli/agent/bitwarden/http.go
+++ b/cli/agent/bitwarden/http.go
@@ -98,6 +98,9 @@ func makeAuthenticatedHTTPRequest(ctx context.Context, req *http.Request, recv i
req.Header.Set("Authorization", "Bearer "+token)
}
req.Header.Set("device-type", deviceType())
+ req.Header.Set("User-Agent", "Goldwarden (github.com/quexten/goldwarden)")
+ req.Header.Set("Bitwarden-Client-Name", "goldwarden")
+ req.Header.Set("Bitwarden-Client-Version", "0.0.0")
res, err := httpClient.Do(req)
if err != nil {
diff --git a/cli/agent/bitwarden/models/models.go b/cli/agent/bitwarden/models/models.go
index 5eb58f8..11ff9a2 100644
--- a/cli/agent/bitwarden/models/models.go
+++ b/cli/agent/bitwarden/models/models.go
@@ -76,7 +76,8 @@ type Cipher struct {
Login *LoginCipher `json:"login,omitempty"`
Notes *crypto.EncString `json:"notes,omitempty"`
SecureNote *SecureNoteCipher `json:"secureNote,omitempty"`
-
+ SSHKey *SSHKeyCipher `json:"sshKey,omitempty"`
+
Key *crypto.EncString `json:"key,omitempty"`
}
@@ -88,8 +89,15 @@ const (
CipherCard = 3
CipherIdentity = 4
CipherNote = 2
+ CipherSSHKey = 5
)
+type SSHKeyCipher struct {
+ PrivateKey crypto.EncString `json:"privateKey"`
+ PublicKey crypto.EncString `json:"publicKey"`
+ KeyFingerprint crypto.EncString `json:"keyFingerprint"`
+}
+
type Card struct {
CardholderName crypto.EncString `json:"cardholderName"`
Brand crypto.EncString `json:"brand"`
diff --git a/cli/agent/bitwarden/sync.go b/cli/agent/bitwarden/sync.go
index 44e77ef..6ad5a5f 100644
--- a/cli/agent/bitwarden/sync.go
+++ b/cli/agent/bitwarden/sync.go
@@ -59,6 +59,8 @@ func DoFullSync(ctx context.Context, vault *vault.Vault, config *config.Config,
vault.AddOrUpdateLogin(cipher)
case models.CipherNote:
vault.AddOrUpdateSecureNote(cipher)
+ case models.CipherSSHKey:
+ vault.AddOrUpdateSSHKey(cipher)
}
}
diff --git a/cli/agent/systemauth/pinentry/go-pinentry.go b/cli/agent/systemauth/pinentry/go-pinentry.go
index 648a7e3..67b4d99 100644
--- a/cli/agent/systemauth/pinentry/go-pinentry.go
+++ b/cli/agent/systemauth/pinentry/go-pinentry.go
@@ -9,11 +9,16 @@ import (
"github.com/twpayne/go-pinentry"
)
-func getPassword(title string, description string) (string, error) {
+func getBinaryClientOption() (clientOption pinentry.ClientOption) {
binaryClientOption := pinentry.WithBinaryNameFromGnuPGAgentConf()
if runtime.GOOS == "darwin" {
binaryClientOption = pinentry.WithBinaryName("pinentry-mac")
}
+ return binaryClientOption
+}
+
+func getPassword(title string, description string) (string, error) {
+ binaryClientOption := getBinaryClientOption()
client, err := pinentry.NewClient(
binaryClientOption,
@@ -49,8 +54,10 @@ func getApproval(title string, description string) (bool, error) {
return true, nil
}
+ binaryClientOption := getBinaryClientOption()
+
client, err := pinentry.NewClient(
- pinentry.WithBinaryNameFromGnuPGAgentConf(),
+ binaryClientOption,
pinentry.WithGPGTTY(),
pinentry.WithTitle(title),
pinentry.WithDesc(description),
diff --git a/cli/agent/systemauth/systemauth.go b/cli/agent/systemauth/systemauth.go
index eb0c868..0f2964e 100644
--- a/cli/agent/systemauth/systemauth.go
+++ b/cli/agent/systemauth/systemauth.go
@@ -55,9 +55,12 @@ func (s *SessionStore) CreateSession(pid int, parentpid int, grandparentpid int,
func (s *SessionStore) verifySession(ctx sockets.CallingContext, sessionType SessionType) bool {
for _, session := range s.Store {
- if session.ParentPid == ctx.ParentProcessPid && session.GrandParentPid == ctx.GrandParentProcessPid && session.sessionType == sessionType {
- if session.Expires.After(time.Now()) {
- return true
+ if session.sessionType == sessionType {
+ // only check for ancestor if the session is not a ssh session
+ if sessionType == SSHKey || (session.ParentPid == ctx.ParentProcessPid && session.GrandParentPid == ctx.GrandParentProcessPid) {
+ if session.Expires.After(time.Now()) {
+ return true
+ }
}
}
}
diff --git a/cli/agent/vault/vault.go b/cli/agent/vault/vault.go
index 0db0f38..1a86b2f 100644
--- a/cli/agent/vault/vault.go
+++ b/cli/agent/vault/vault.go
@@ -3,6 +3,8 @@ package vault
import (
"errors"
"strings"
+ "fmt"
+ "regexp"
"sync"
"github.com/quexten/goldwarden/cli/agent/bitwarden/crypto"
@@ -17,6 +19,7 @@ type Vault struct {
Keyring *crypto.Keyring
logins map[string]models.Cipher
secureNotes map[string]models.Cipher
+ sshKeys map[string]models.Cipher
sshKeyNoteIDs []string
envCredentials map[string]string
lastSynced int64
@@ -29,6 +32,7 @@ func NewVault(keyring *crypto.Keyring) *Vault {
Keyring: keyring,
logins: make(map[string]models.Cipher),
secureNotes: make(map[string]models.Cipher),
+ sshKeys: make(map[string]models.Cipher),
sshKeyNoteIDs: make([]string, 0),
envCredentials: make(map[string]string),
lastSynced: 0,
@@ -90,6 +94,12 @@ func (vault *Vault) AddOrUpdateSecureNote(cipher models.Cipher) {
vault.unlockMutex()
}
+func (vault *Vault) AddOrUpdateSSHKey(cipher models.Cipher) {
+ vault.lockMutex()
+ vault.sshKeys[cipher.ID.String()] = cipher
+ vault.unlockMutex()
+}
+
func (vault *Vault) isEnv(cipher models.Cipher) (string, bool) {
if cipher.Type != models.CipherNote {
return "", false
@@ -174,6 +184,26 @@ type SSHKey struct {
PublicKey string
}
+func extractKeyMarker(text, pattern string) (string, string, error) {
+ re := regexp.MustCompile(pattern)
+ match := re.FindStringIndex(text)
+
+ if match != nil {
+ // Extract the matched text
+ extracted := re.FindString(text[match[0]:match[1]])
+ if match[0] == 0 {
+ // begin marker
+ return extracted, text[match[1]:], nil
+ } else if match[1] == len(strings.TrimRight(text, "\n\r ")) {
+ // end marker
+ return extracted, text[:match[0]], nil
+ }
+ return "", text, fmt.Errorf("Token found is neither at the beginning nor end: pattern: %s. match idx: %s", pattern, match)
+ }
+
+ return "", text, fmt.Errorf("No match found in pattern %s", pattern)
+}
+
func (vault *Vault) GetSSHKeys() []SSHKey {
vault.lockMutex()
defer vault.unlockMutex()
@@ -211,11 +241,19 @@ func (vault *Vault) GetSSHKeys() []SSHKey {
}
}
- privateKey = strings.Replace(privateKey, "-----BEGIN OPENSSH PRIVATE KEY-----", "", 1)
- privateKey = strings.Replace(privateKey, "-----END OPENSSH PRIVATE KEY-----", "", 1)
+ beginMarker, privateKey, err := extractKeyMarker(privateKey, `-----\w*BEGIN [a-zA-Z ]+\w*-----`)
+ if err != nil {
+ vaultLog.Error("Failed for note %s: %s", vault.secureNotes[id].Name, err.Error())
+ continue
+ }
+ endMarker, privateKey, err := extractKeyMarker(privateKey, `-----\w*END [a-zA-Z ]+\w*-----`)
+ if err != nil {
+ vaultLog.Error("Failed for note %s: %s", vault.secureNotes[id].Name, err.Error())
+ continue
+ }
pkParts := strings.Join(strings.Split(privateKey, " "), "\n")
- privateKeyString := "-----BEGIN OPENSSH PRIVATE KEY-----" + pkParts + "-----END OPENSSH PRIVATE KEY-----"
+ privateKeyString := beginMarker + pkParts + endMarker
decryptedTitle, err := crypto.DecryptWith(vault.secureNotes[id].Name, key)
if err != nil {
@@ -228,6 +266,20 @@ func (vault *Vault) GetSSHKeys() []SSHKey {
PublicKey: string(publicKey),
})
}
+
+ for id, _ := range vault.sshKeys {
+ key, _ := vault.sshKeys[id].GetKeyForCipher(*vault.Keyring)
+ privKey, _ := crypto.DecryptWith(vault.sshKeys[id].SSHKey.PrivateKey, key)
+ pubKey, _ := crypto.DecryptWith(vault.sshKeys[id].SSHKey.PublicKey, key)
+ name, _ := crypto.DecryptWith(vault.sshKeys[id].Name, key)
+
+ sshKeys = append(sshKeys, SSHKey{
+ Name: string(name),
+ Key: string(privKey),
+ PublicKey: string(pubKey),
+ })
+ }
+
return sshKeys
}
diff --git a/go.mod b/go.mod
index ddbeca0..0371926 100644
--- a/go.mod
+++ b/go.mod
@@ -14,20 +14,20 @@ require (
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
- github.com/icza/gox v0.0.0-20230924165045-adcb03233bb5
+ github.com/icza/gox v0.2.0
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/rymdport/portal v0.2.3
+ github.com/rymdport/portal v0.2.6
github.com/spf13/cobra v1.8.1
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4
github.com/tink-crypto/tink-go/v2 v2.2.0
github.com/twpayne/go-pinentry v0.3.0
github.com/vmihailenco/msgpack/v5 v5.4.1
- golang.org/x/crypto v0.25.0
+ golang.org/x/crypto v0.28.0
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f
- golang.org/x/sys v0.22.0
+ golang.org/x/sys v0.26.0
)
require (
diff --git a/go.sum b/go.sum
index 0b0be16..538ec64 100644
--- a/go.sum
+++ b/go.sum
@@ -36,8 +36,8 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
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/icza/gox v0.2.0 h1:+0N8PCt9/QSx+k0dqe/wdlXJNR/haaPsPwrTJTNDeyk=
+github.com/icza/gox v0.2.0/go.mod h1:rVecw5Q6POJAWBcXgCZdAtwK/hmoNehxCkAP3sMnOIc=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/keybase/backoff v1.0.1-0.20160517061000-726b63b835ec h1:D6qL2WCnAuxucGbmL+mDW8IKRK1pex+R1fw5rKa9nXc=
@@ -84,8 +84,8 @@ github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
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/rymdport/portal v0.2.3 h1:5RoAuMy5wNzEzITwK+9YpMQLU5m7F7IYfmPwN/aVpUk=
-github.com/rymdport/portal v0.2.3/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
+github.com/rymdport/portal v0.2.6 h1:HWmU3gORu7vWcpr7VSwUS2Xx1HtJXVcUuTqEZcMEsIg=
+github.com/rymdport/portal v0.2.6/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -106,8 +106,8 @@ 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.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
-golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
+golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
+golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
@@ -118,10 +118,10 @@ golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7w
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.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
-golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
-golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
+golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
+golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
+golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/gui/com.quexten.Goldwarden.Devel.yml b/gui/com.quexten.Goldwarden.Devel.yml
index 55d5991..39e5618 100644
--- a/gui/com.quexten.Goldwarden.Devel.yml
+++ b/gui/com.quexten.Goldwarden.Devel.yml
@@ -102,7 +102,7 @@ modules:
- cp -R ./gui/* /app/bin
- chmod +x /app/bin/goldwarden_ui_main.py
- install -D ./gui/com.quexten.Goldwarden.desktop /app/share/applications/com.quexten.Goldwarden.desktop
- - install -D ./gui/goldwarden.svg /app/share/icons/hicolor/scalable/apps/com.quexten.Goldwarden.svg
+ - install -D ./gui/com.quexten.Goldwarden.svg /app/share/icons/hicolor/scalable/apps/com.quexten.Goldwarden.svg
- install -Dm644 ./gui/com.quexten.Goldwarden.metainfo.xml -t /app/share/metainfo/
- blueprint-compiler batch-compile /app/bin/src/gui/.templates/ /app/bin/src/gui/ /app/bin/src/gui/*.blp
sources:
diff --git a/gui/goldwarden.svg b/gui/com.quexten.Goldwarden.svg
similarity index 100%
rename from gui/goldwarden.svg
rename to gui/com.quexten.Goldwarden.svg