Skip to content

Commit

Permalink
Add support to save/read password to/from keyring.
Browse files Browse the repository at this point in the history
  • Loading branch information
marcopaganini committed Mar 7, 2024
1 parent 6af0316 commit cc2eee8
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 17 deletions.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,27 @@ Without any special options, **termotp** shows a formatted table of your TOTP pr

Produces a plain listing of the vault.

**--set-keyring**

Read the password from the keyboard and write it to the keyring. This option causes all other options to be silently ignored.

Under OS X, you'll need the `/usr/bin/security` binary to interface with the OS X keychain. This binary should be available by default.

In Linux and BSD implementations, this depends on the [Secret Service](https://specifications.freedesktop.org/secret-service/latest/) dbus interface provided by [Gnome Keyring](https://wiki.gnome.org/Projects/GnomeKeyring). These implementations are installed and started by default on most modern distributions.

Please note that this assumes that the `login` collection exists in the keyring (the default on most distros). If it doesn't, use [Seahorse](https://wiki.gnome.org/Apps/Seahorse) to create it:

* Open `seahorse`
* Go to `File` > `New` > `Password Keyring`
* Click `Continue`
* When asked for a name, use `login`.

**--use-keyring**

Read the password from the `login` keyring (which should be open by default after login) instead of the keyboard. This allows passwordless operation while maintaining your vault encrypted.

Make sure to write your password to the keyring with `--set-keyring` before using this option.

**---version**

Just print the current program version (or git commit number) and exit.
Expand All @@ -88,6 +109,11 @@ Add support for other OTP programs, like AndOTP, 2FA, etc. I'll proceed to do th
* [2fa](https://github.com/rsc/2fa): Another bare bones OTP generator that uses its own database (manual import).
* [Syncthing](http://syncthing.net): Allows you to sync files directly between multiple devices (including your phone.)

## Thanks

* https://github.com/zalando/ for their Keyring manipulation library.
* https://github.com/sam-artuso for his ideas on using fzf (as an external program) and keyring.

## Author

Marco Paganini <paganini AT paganini dot net>
9 changes: 2 additions & 7 deletions aegis.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func filterAegisVault(plainJSON []byte, rematch *regexp.Regexp) ([]otpEntry, err

// aegisDecrypt opens an encrypted Aegis JSON export file and
// returns the plain json contents.
func aegisDecrypt(fname string) ([]byte, error) {
func aegisDecrypt(fname string, password []byte) ([]byte, error) {
buf, err := os.ReadFile(fname)
if err != nil {
return nil, err
Expand All @@ -135,14 +135,9 @@ func aegisDecrypt(fname string) ([]byte, error) {
return nil, err
}

var masterkey []byte
password, err := readPassword()
if err != nil {
return nil, err
}

// Extract all master key slots from header.
// Exit when a valid masterkey has been found.
var masterkey []byte
for _, slot := range encJSON.Header.Slots {
var (
nonce []byte
Expand Down
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,23 @@ require (
github.com/ktr0731/go-fuzzyfinder v0.7.0
github.com/romana/rlog v0.0.0-20220412051723-c08f605858a9
github.com/xlzd/gotp v0.1.0
github.com/zalando/go-keyring v0.2.3
golang.org/x/crypto v0.5.0
golang.org/x/term v0.4.0
)

require (
github.com/alessio/shellescape v1.4.1 // indirect
github.com/danieljoos/wincred v1.2.0 // indirect
github.com/gdamore/encoding v1.0.0 // indirect
github.com/gdamore/tcell/v2 v2.5.3 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/ktr0731/go-ansisgr v0.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/nsf/termbox-go v1.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.2 // indirect
golang.org/x/sys v0.4.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.6.0 // indirect
)
15 changes: 12 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=
github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE=
github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec=
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/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell/v2 v2.5.3 h1:b9XQrT6QGbgI7JvZOJXFNczOQeIYbo8BfeSMzt2sAV0=
github.com/gdamore/tcell/v2 v2.5.3/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo=
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/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
Expand Down Expand Up @@ -35,12 +41,15 @@ github.com/romana/rlog v0.0.0-20220412051723-c08f605858a9 h1:8tVb/1pwM1HrrK4HuBJ
github.com/romana/rlog v0.0.0-20220412051723-c08f605858a9/go.mod h1:kPzumBKm/AKQWtDbtf8w0s/R+LwoYT1rTjsOYGcS82k=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM=
github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/xlzd/gotp v0.1.0 h1:37blvlKCh38s+fkem+fFh7sMnceltoIEBYTVXyoa5Po=
github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms=
github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
Expand All @@ -59,8 +68,8 @@ golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
Expand Down
66 changes: 60 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/romana/rlog"
"github.com/zalando/go-keyring"
)

// BuildVersion holds the current git head version number.
Expand All @@ -31,13 +32,21 @@ type otpEntry struct {
token string
}

// Keyring constants. User is not your user.
const (
keyRingService = "termotp"
keyRingUser = "anon"
)

// cmdLineFlags contains the command-line flags.
type cmdLineFlags struct {
input string
fuzzy bool
fzf bool
plain bool
version bool
input string
fuzzy bool
fzf bool
plain bool
setkeyring bool
usekeyring bool
version bool
}

// die logs a message with rlog.Critical and exits with a return code.
Expand Down Expand Up @@ -142,9 +151,16 @@ func parseFlags() (cmdLineFlags, error) {
flag.BoolVar(&flags.fzf, "fzf", false, "Use fzf (needs external binary in path).")
flag.BoolVar(&flags.plain, "plain", false, "Use plain output (disables fuzzy finder and tabular output.)")
flag.BoolVar(&flags.version, "version", false, "Show program version and exit.")
flag.BoolVar(&flags.setkeyring, "set-keyring", false, "Set the keyring password and exit.")
flag.BoolVar(&flags.usekeyring, "use-keyring", false, "Use keyring stored password.")

flag.Parse()

// --setkeyring requires nothing else.
if flags.setkeyring {
return flags, nil
}

if flags.version {
fmt.Printf("Build Version: %s\n", BuildVersion)
os.Exit(0)
Expand Down Expand Up @@ -213,6 +229,19 @@ func fzf(table string) (string, error) {
return f[len(f)-1], nil
}

// setkeyring asks for a password and write it to the keyring.
func setkeyring() error {
password, err := readPassword()
if err != nil {
return err
}

if err = keyring.Set(keyRingService, keyRingUser, string(password)); err != nil {
return err
}
return nil
}

func main() {
// Usage prints the default usage for this program.
flag.Usage = func() {
Expand All @@ -227,6 +256,15 @@ func main() {
die(err)
}

if flags.setkeyring {
fmt.Println("Please enter the password to be stored in the keyring.")
if err := setkeyring(); err != nil {
die(err)
}
fmt.Println("Password set. Use --use-keyring to read the password from the keyring.")
os.Exit(0)
}

// Get input file from the input files glob.
input, err := inputFile(flags.input)
if err != nil {
Expand All @@ -244,7 +282,23 @@ func main() {
die(err)
}

db, err := aegisDecrypt(input)
// Read password (from keyboard or keyring) and decrypt aegis vault.
var (
password []byte
secret string
)

if flags.usekeyring {
secret, err = keyring.Get(keyRingService, keyRingUser)
password = []byte(secret)
} else {
password, err = readPassword()
}
if err != nil {
die(err)
}

db, err := aegisDecrypt(input, password)
if err != nil {
die(err)
}
Expand Down

0 comments on commit cc2eee8

Please sign in to comment.