66 "os"
77 "path/filepath"
88 "runtime"
9- s "sort"
9+ "sort"
10+ "strconv"
1011 "strings"
1112
1213 "github.com/hazcod/enpass-cli/pkg/clipboard"
@@ -17,27 +18,61 @@ import (
1718)
1819
1920const (
20- defaultLogLevel = logrus .InfoLevel
21- cmdVersion = "version"
22- cmdHelp = "help"
23- cmdInit = "init"
24- cmdList = "list"
25- cmdShow = "show"
26- cmdCopy = "copy"
27- cmdPass = "pass"
21+ // commands
22+ cmdVersion = "version"
23+ cmdHelp = "help"
24+ cmdInit = "init"
25+ cmdList = "list"
26+ cmdShow = "show"
27+ cmdCopy = "copy"
28+ cmdPass = "pass"
29+ // defaults
30+ defaultLogLevel = logrus .InfoLevel
31+ pinMinLength = 8
32+ pinDefaultKdfIterCount = 100000
2833)
2934
3035var (
3136 // overwritten by go build
3237 version = "dev"
33- // enables prompts
34- interactive = true
35- // complete command list
36- commands = map [string ]struct {}{cmdVersion : {}, cmdHelp : {}, cmdInit : {}, cmdList : {}, cmdShow : {}, cmdCopy : {}, cmdPass : {}}
38+ // set of all commands
39+ commands = map [string ]struct {}{cmdVersion : {}, cmdHelp : {}, cmdInit : {}, cmdList : {},
40+ cmdShow : {}, cmdCopy : {}, cmdPass : {}}
3741)
3842
39- func prompt (logger * logrus.Logger , msg string ) string {
40- if interactive {
43+ type Args struct {
44+ command string
45+ // params
46+ filters []string
47+ // flags
48+ vaultPath * string
49+ cardType * string
50+ keyFilePath * string
51+ logLevelStr * string
52+ nonInteractive * bool
53+ pinEnable * bool
54+ sort * bool
55+ trashed * bool
56+ clipboardPrimary * bool
57+ }
58+
59+ func (args * Args ) parse () {
60+ args .vaultPath = flag .String ("vault" , "" , "Path to your Enpass vault." )
61+ args .cardType = flag .String ("type" , "password" , "The type of your card. (password, ...)" )
62+ args .keyFilePath = flag .String ("keyfile" , "" , "Path to your Enpass vault keyfile." )
63+ args .logLevelStr = flag .String ("log" , defaultLogLevel .String (), "The log level from debug (5) to error (1)." )
64+ args .nonInteractive = flag .Bool ("nonInteractive" , false , "Disable prompts and fail instead." )
65+ args .pinEnable = flag .Bool ("pin" , false , "Enable PIN." )
66+ args .sort = flag .Bool ("sort" , false , "Sort the output by title and username of the 'list' and 'show' command." )
67+ args .trashed = flag .Bool ("trashed" , false , "Show trashed items in the 'list' and 'show' command." )
68+ args .clipboardPrimary = flag .Bool ("clipboardPrimary" , false , "Use primary X selection instead of clipboard for the 'copy' command." )
69+ flag .Parse ()
70+ args .command = strings .ToLower (flag .Arg (0 ))
71+ args .filters = flag .Args ()[1 :]
72+ }
73+
74+ func prompt (logger * logrus.Logger , args * Args , msg string ) string {
75+ if ! * args .nonInteractive {
4176 if response , err := ask .HiddenAsk ("Enter " + msg + ": " ); err != nil {
4277 logger .WithError (err ).Fatal ("could not prompt for " + msg )
4378 } else {
@@ -59,25 +94,25 @@ func printHelp() {
5994
6095func sortEntries (cards []enpass.Card ) {
6196 // Sort by username preserving original order
62- s .SliceStable (cards , func (i , j int ) bool {
97+ sort .SliceStable (cards , func (i , j int ) bool {
6398 return strings .ToLower (cards [i ].Subtitle ) < strings .ToLower (cards [j ].Subtitle )
6499 })
65100 // Sort by title, preserving username order
66- s .SliceStable (cards , func (i , j int ) bool {
101+ sort .SliceStable (cards , func (i , j int ) bool {
67102 return strings .ToLower (cards [i ].Title ) < strings .ToLower (cards [j ].Title )
68103 })
69104}
70105
71- func listEntries (logger * logrus.Logger , vault * enpass.Vault , cardType string , sort bool , trashed bool , filters [] string ) {
72- cards , err := vault .GetEntries (cardType , filters )
106+ func listEntries (logger * logrus.Logger , vault * enpass.Vault , args * Args ) {
107+ cards , err := vault .GetEntries (* args . cardType , args . filters )
73108 if err != nil {
74109 logger .WithError (err ).Fatal ("could not retrieve cards" )
75110 }
76- if sort {
111+ if * args . sort {
77112 sortEntries (cards )
78113 }
79114 for _ , card := range cards {
80- if card .IsTrashed () && ! trashed {
115+ if card .IsTrashed () && ! * args . trashed {
81116 continue
82117 }
83118 logger .Printf (
@@ -91,16 +126,16 @@ func listEntries(logger *logrus.Logger, vault *enpass.Vault, cardType string, so
91126 }
92127}
93128
94- func showEntries (logger * logrus.Logger , vault * enpass.Vault , cardType string , sort bool , trashed bool , filters [] string ) {
95- cards , err := vault .GetEntries (cardType , filters )
129+ func showEntries (logger * logrus.Logger , vault * enpass.Vault , args * Args ) {
130+ cards , err := vault .GetEntries (* args . cardType , args . filters )
96131 if err != nil {
97132 logger .WithError (err ).Fatal ("could not retrieve cards" )
98133 }
99- if sort {
134+ if * args . sort {
100135 sortEntries (cards )
101136 }
102137 for _ , card := range cards {
103- if card .IsTrashed () && ! trashed {
138+ if card .IsTrashed () && ! * args . trashed {
104139 continue
105140 }
106141 password , err := card .Decrypt ()
@@ -122,8 +157,8 @@ func showEntries(logger *logrus.Logger, vault *enpass.Vault, cardType string, so
122157 }
123158}
124159
125- func copyEntry (logger * logrus.Logger , vault * enpass.Vault , cardType string , filters [] string ) {
126- card , err := vault .GetUniqueEntry (cardType , filters )
160+ func copyEntry (logger * logrus.Logger , vault * enpass.Vault , args * Args ) {
161+ card , err := vault .GetUniqueEntry (* args . cardType , args . filters )
127162 if err != nil {
128163 logger .WithError (err ).Fatal ("could not retrieve unique card" )
129164 }
@@ -133,13 +168,18 @@ func copyEntry(logger *logrus.Logger, vault *enpass.Vault, cardType string, filt
133168 logger .WithError (err ).Fatal ("could not decrypt card" )
134169 }
135170
171+ if * args .clipboardPrimary {
172+ clipboard .Primary = true
173+ logger .Debug ("primary X selection enabled" )
174+ }
175+
136176 if err := clipboard .WriteAll (password ); err != nil {
137177 logger .WithError (err ).Fatal ("could not copy password to clipboard" )
138178 }
139179}
140180
141- func entryPassword (logger * logrus.Logger , vault * enpass.Vault , cardType string , filters [] string ) {
142- card , err := vault .GetUniqueEntry (cardType , filters )
181+ func entryPassword (logger * logrus.Logger , vault * enpass.Vault , args * Args ) {
182+ card , err := vault .GetUniqueEntry (* args . cardType , args . filters )
143183 if err != nil {
144184 logger .WithError (err ).Fatal ("could not retrieve unique card" )
145185 }
@@ -151,25 +191,35 @@ func entryPassword(logger *logrus.Logger, vault *enpass.Vault, cardType string,
151191 }
152192}
153193
154- func getVaultAccessData (logger * logrus.Logger , vaultPath string , enablePin bool ) (* enpass.VaultAccessData , * pin.SecureStore ) {
194+ func assembleVaultAccessData (logger * logrus.Logger , args * Args ) (* enpass.VaultAccessData , * pin.SecureStore ) {
155195 accessData := & enpass.VaultAccessData {
156196 Password : os .Getenv ("MASTERPW" ),
157197 }
158198
159199 var store * pin.SecureStore
160- if ! enablePin {
200+ if ! * args . pinEnable {
161201 logger .Debug ("PIN disabled" )
162202 } else if ! accessData .IsComplete () {
163203 logger .Debug ("PIN enabled, using store" )
164204
165- storePin := os .Getenv ("PIN" )
166- if storePin == "" {
167- storePin = prompt (logger , "PIN" )
205+ vaultPath , err := filepath .EvalSymlinks (* args .vaultPath )
206+ store , err = pin .NewSecureStore (filepath .Base (vaultPath ), logger .Level )
207+ if err != nil {
208+ logger .WithError (err ).Fatal ("could not create store" )
168209 }
169210
170- vaultPath , err := filepath .EvalSymlinks (vaultPath )
171- store , err = pin .NewSecureStore (filepath .Base (vaultPath ), storePin , logger .Level )
211+ storePin := os .Getenv ("ENP_PIN" )
212+ if storePin == "" {
213+ storePin = prompt (logger , args , "PIN" )
214+ }
215+ if len (storePin ) < pinMinLength {
216+ logger .Fatal ("PIN too short" )
217+ }
218+ pinKdfIterCount , err := strconv .ParseInt (os .Getenv ("ENP_PIN_ITER_COUNT" ), 10 , 64 )
172219 if err != nil {
220+ pinKdfIterCount = pinDefaultKdfIterCount
221+ }
222+ if err := store .GeneratePassphrase (storePin , int (pinKdfIterCount )); err != nil {
173223 logger .WithError (err ).Fatal ("could not initialize store" )
174224 }
175225 logger .Debug ("initialized store" )
@@ -181,41 +231,29 @@ func getVaultAccessData(logger *logrus.Logger, vaultPath string, enablePin bool)
181231 }
182232
183233 if ! accessData .IsComplete () {
184- accessData .Password = prompt (logger , "master password" )
234+ accessData .Password = prompt (logger , args , "master password" )
185235 }
186236
187237 return accessData , store
188238}
189239
190240func main () {
191- vaultPath := flag .String ("vault" , "" , "Path to your Enpass vault." )
192- cardType := flag .String ("type" , "password" , "The type of your card. (password, ...)" )
193- keyFilePath := flag .String ("keyfile" , "" , "Path to your Enpass vault keyfile." )
194- logLevelStr := flag .String ("log" , defaultLogLevel .String (), "The log level from debug (5) to error (1)." )
195- nonInteractive := flag .Bool ("nonInteractive" , false , "Disable prompts and fail instead." )
196- enablePin := flag .Bool ("pin" , false , "Enable PIN." )
197- sort := flag .Bool ("sort" , false , "Sort the output by title and username of the 'list' and 'show' command." )
198- trashed := flag .Bool ("trashed" , false , "Show trashed items in the 'list' and 'show' command." )
199- clipboardPrimary := flag .Bool ("clipboardPrimary" , false , "Use primary X selection instead of clipboard for the 'copy' command." )
241+ args := & Args {}
242+ args .parse ()
200243
201- flag .Parse ()
202-
203- logLevel , err := logrus .ParseLevel (* logLevelStr )
244+ logLevel , err := logrus .ParseLevel (* args .logLevelStr )
204245 if err != nil {
205246 logrus .WithError (err ).Fatal ("invalid log level specified" )
206247 }
207248 logger := logrus .New ()
208249 logger .SetLevel (logLevel )
209250
210- cmd := strings .ToLower (flag .Arg (0 ))
211- filters := flag .Args ()[1 :]
212-
213- if _ , contains := commands [cmd ]; ! contains {
251+ if _ , contains := commands [args .command ]; ! contains {
214252 printHelp ()
215253 logger .Exit (1 )
216254 }
217255
218- switch cmd {
256+ switch args . command {
219257 case cmdHelp :
220258 printHelp ()
221259 return
@@ -227,44 +265,36 @@ func main() {
227265 return
228266 }
229267
230- interactive = ! * nonInteractive
231-
232- if * clipboardPrimary {
233- clipboard .Primary = true
234- logger .Debug ("primary X selection enabled" )
235- }
236-
237- vault , err := enpass .NewVault (* vaultPath , logger .Level )
268+ vault , err := enpass .NewVault (* args .vaultPath , logger .Level )
238269 if err != nil {
239270 logger .WithError (err ).Fatal ("could not create vault" )
240271 }
241272
242- accessData , store := getVaultAccessData (logger , * vaultPath , * enablePin )
243- accessData .KeyfilePath = * keyFilePath
273+ accessData , store := assembleVaultAccessData (logger , args )
274+ accessData .KeyfilePath = * args . keyFilePath
244275
276+ defer func () {
277+ vault .Close ()
278+ }()
245279 if err := vault .Open (accessData ); err != nil {
246280 logger .WithError (err ).Error ("could not open vault" )
247281 logger .Exit (2 )
248282 }
249283 logger .Debug ("opened vault" )
250- defer func () {
251- vault .Close ()
252- logger .Debug ("closed vault" )
253- }()
254284
255- switch cmd {
285+ switch args . command {
256286 case cmdInit :
257- // just init vault and store without doing anything
287+ logger . Debug ( "init done" ) // just init vault and store without doing anything
258288 case cmdList :
259- listEntries (logger , vault , * cardType , * sort , * trashed , filters )
289+ listEntries (logger , vault , args )
260290 case cmdShow :
261- showEntries (logger , vault , * cardType , * sort , * trashed , filters )
291+ showEntries (logger , vault , args )
262292 case cmdCopy :
263- copyEntry (logger , vault , * cardType , filters )
293+ copyEntry (logger , vault , args )
264294 case cmdPass :
265- entryPassword (logger , vault , * cardType , filters )
295+ entryPassword (logger , vault , args )
266296 default :
267- logger .WithField ("command" , cmd ).Fatal ("unknown command" )
297+ logger .WithField ("command" , args . command ).Fatal ("unknown command" )
268298 }
269299
270300 if store != nil {
0 commit comments