// login simulates a login prompt; it will ask for a user name and // password, and attempt to validate the password against the user's // authenticator. It returns a boolean indicating whether the program // should ask for another user name and password pair. func login() bool { login, err := util.ReadLine("User name: ") dieIfError(err) if login == "" { return false } password, err := util.ReadLine("Password: "******"authentication failed (no such user)") return true } shouldUpdate, err := auth.Validate(u.Auth, password) if err != nil { log.Printf("authentication failed (%v)", err) return true } if shouldUpdate { writeStore() } log.Println("authentication successful") return true }
// Registration asks the user for their username and what type of // authentication they will be using. It then determines whether the // authentication type is supported, and calls the appropriate // registration function to handle setting up the authenticator. func register() { rfns := map[string]func() *auth.Authenticator{ auth.TypePassword: regPassword, auth.TypeTOTP: regTOTP, } var u = &User{} var err error u.Name, err = util.ReadLine("Login name: ") dieIfError(err) fmt.Println("Supported authentication types:") for i := range supported { fmt.Printf("\t%s\n", supported[i]) } for { authType, err := util.ReadLine("Authentication type: ") dieIfError(err) rfn, ok := rfns[authType] if !ok { fmt.Println(authType, "isn't a supported authentication type.") continue } u.Auth = rfn() break } store[u.Name] = u writeStore() log.Println("registered", u.Name) }
func importUntrusted(ks *store.KeyStore, cfg *config) error { fmt.Println("*****************************************") fmt.Println("*** WARNING: IMPORTING UNTRUSTED KEYS ***") fmt.Println("*****************************************") keyData, err := util.ReadFile(cfg.Args[0]) if err != nil { return err } vkey, err := store.ParseVerifiedKey(keyData) if err != nil { return err } if vkey.IsSelfSigned() { fmt.Println("Key is self-signed.") } else { fmt.Println("Unrecognised signature.") } h := sha256.New() h.Write(vkey.Public) fmt.Printf("Fingerprint: %x\n", h.Sum(nil)) for { line, err := util.ReadLine("\nAre you sure you want to import this key? (yes or no) ") if err != nil { return nil } if line == "yes" { fmt.Println("As you wish.") break } else if line == "no" { return errors.New("canceled by user") } else { fmt.Println("Please enter either 'yes' or 'no'.") } } if cfg.Label == "self" { cfg.Label, err = util.ReadLine("Label: ") if err != nil { return err } } if !ks.AddKey(cfg.Label, vkey.Public, nil) { return errors.New("failed to add new key") } return nil }
// Registration with a YubiKey requires the user enter their secret key // and initial OTP. func regYubiKey() *auth.Authenticator { k, err := util.ReadLine("Hex-encoded key: ") dieIfError(err) kb, err := hex.DecodeString(k) dieIfError(err) otp, err := util.ReadLine("OTP: ") dieIfError(err) a, err := auth.NewYubiKey(kb, otp) dieIfError(err) return a }
func readCommands(storeName string, input chan string, proceed chan bool) { // Catch the case where the input channel is closed. defer func() { if err := recover(); err != nil { log.Printf("readCommands: %v", err) } }() prompt := fmt.Sprintf("%s command> ", storeName) for { line, err := util.ReadLine(prompt) if err != nil { if err == io.EOF { close(input) return } fmt.Fprintf(os.Stderr, "[!] %v", err) } if line == "" { continue } input <- line _, ok := <-proceed if !ok { fmt.Println("done") return } } }
func removeMeta(ps *store.SecretStore, cfg *config) error { label := cfg.Args[0] if !ps.Has(label) { return errors.New("entry not found") } rec := ps.Store[label] for { var keys = make([]string, 0, len(rec.Metadata)) for k := range rec.Metadata { keys = append(keys, k) } sort.Strings(keys) fmt.Println("Keys:") for i := range keys { fmt.Printf("\t%s\n", keys[i]) } key, err := util.ReadLine("Remove key: ") if err != nil { util.Errorf("Failed to read key: %v", err) continue } else if key == "" { break } delete(rec.Metadata, key) fmt.Println("Deleted key", key) } rec.Timestamp = time.Now().Unix() ps.Store[label] = rec return nil }
func importVerified(ks *store.KeyStore, cfg *config) error { keyData, err := util.ReadFile(cfg.Args[0]) if err != nil { return err } if cfg.Label == "self" { cfg.Label, err = util.ReadLine("Label: ") if err != nil { return err } } if !ks.ImportVerifiedKey(cfg.Label, keyData) { return errors.New("verified import failed") } vkey, err := store.ParseVerifiedKey(keyData) if err != nil { return err } label, ok := ks.FindPublic(vkey.Signer) if !ok { return errors.New("invalid signer on key") } fmt.Printf("Imported public key signed by '%s'.\n", label) return nil }
func storeSingleSecret(label string) error { r, ok := session.Store.Store[label] if ok { answer, err := util.ReadLine(label + " exists. Overwrite secret (y/n)? ") if err != nil { return err } answer = strings.ToLower(answer) if answer != "y" && answer != "yes" { fmt.Println("Not overwriting.") return nil } } else { r = new(store.SecretRecord) } password, err := readpass.PasswordPromptBytes("New password: "******"no password entered") } util.Zero(r.Secret) r.Secret = password r.Timestamp = time.Now().Unix() session.Store.Timestamp = r.Timestamp session.Store.Store[label] = r session.Dirty = true return nil }
func writeNew(ps *store.SecretStore, cfg *config) error { title := strings.TrimSpace(cfg.Args[0]) if ps.Has(title) { fmt.Printf("There is already an entry with the title '%s'.\n", title) yesOrNo, err := util.ReadLine("Do you want to edit the entry (y/n)? ") if err != nil { return err } if yesOrNo = strings.ToLower(yesOrNo); yesOrNo == "y" || yesOrNo == "yes" { return editEntry(ps, cfg) } fmt.Println("Please enter a new title (or an empty string to abort).") newTitle, err := util.ReadLine("Title: ") if err != nil { return err } else if newTitle == "" { return errors.New("user aborted entry") } title = newTitle } entry, err := newEntry(title, cfg.Editor) if err != nil { fmt.Println("[!] Failed to write a new entry:") fmt.Printf("\t%v\n", err) return err } ps.Store[title] = &store.SecretRecord{ Label: title, Timestamp: time.Now().Unix(), Secret: entry, } return nil }
func writeMeta(args []string) error { if len(args) == 0 { return errors.New("no label specified") } else if len(args) > 1 { return errors.New("only one label may be specified") } label := args[0] r, ok := session.Store.Store[label] if !ok { return errors.New("no such record") } if r.Metadata == nil { r.Metadata = map[string][]byte{} } fmt.Println("Enter metadata; use an empty line to indicate that you are done.") for { line, err := util.ReadLine("key = value: ") if err != nil { return err } else if line == "" { break } meta := strings.SplitN(line, "=", 2) if len(meta) < 2 { util.Errorf("Metadata should be in the form 'key=value'") continue } key := strings.TrimSpace(meta[0]) val := strings.TrimSpace(meta[1]) if prev, ok := r.Metadata[key]; ok { fmt.Printf("Note: replacing previous value of '%s'\n", string(prev)) } r.Metadata[key] = []byte(val) } r.Timestamp = time.Now().Unix() session.Store.Timestamp = r.Timestamp session.Store.Store[label] = r session.Dirty = true return nil }
func multi(ps *store.SecretStore, cfg *config, m secret.ScryptMode) error { fmt.Println("Use an empty name to indicate that you are done.") for { name, err := util.ReadLine("Name: ") if err != nil { return err } else if name == "" { break } var rec *store.SecretRecord if ps.Has(name) { if !cfg.Overwrite { util.Errorf("Entry exists, not forcing overwrite.") continue } else { util.Errorf("*** WARNING: overwriting password") } rec = ps.Store[name] } else { rec = &store.SecretRecord{ Label: name, } } password, err := util.PassPrompt("Password: "******"No password entered.") continue } rec.Secret = password rec.Timestamp = time.Now().Unix() ps.Store[name] = rec } return nil }
func addMeta(ps *store.SecretStore, cfg *config, m secret.ScryptMode) error { label := cfg.Args[0] if !ps.Has(label) { tempConfig := *cfg tempConfig.WithMeta = false err := addSecret(ps, &tempConfig, m) if err != nil { return err } } rec := ps.Store[label] if rec.Metadata == nil { rec.Metadata = map[string][]byte{} } fmt.Println("Enter metadata; use an empty line to indicate that you are done.") for { line, err := util.ReadLine("key = value: ") if err != nil { return err } else if line == "" { break } meta := strings.SplitN(line, "=", 2) if len(meta) < 2 { util.Errorf("Metadata should be in the form 'key=value'") continue } key := strings.TrimSpace(meta[0]) val := strings.TrimSpace(meta[1]) rec.Metadata[key] = []byte(val) } rec.Timestamp = time.Now().Unix() ps.Store[label] = rec return nil }
func addSecret(ps *store.SecretStore, cfg *config) error { label := cfg.Args[0] if ps.Has(label) { if cfg.Overwrite { util.Errorf("WARNING: a token already exists under this label!") } else { return errors.New("token already exists under label") } } // Default prompt is echoing, which we want here. secret, err := util.PassPrompt("Secret: ") if err != nil { return err } var rec *store.SecretRecord secret = sanitiseSecret(secret) switch cfg.OTPType { case HOTP: in, err := util.ReadLine("Initial counter (0): ") if err != nil { return err } if in == "" { in = "0" } d, err := strconv.Atoi(in) if err != nil { return err } in, err = util.ReadLine("Digits (6 or 8): ") if err != nil { return err } else if in == "" { in = "6" } digits, err := strconv.Atoi(in) if err != nil { return err } key, err := base32.StdEncoding.DecodeString(string(secret)) if err != nil { fmt.Printf("%s", secret) return err } var hotp *twofactor.HOTP hotp = twofactor.NewHOTP(key, uint64(d), digits) confirmation := hotp.OTP() fmt.Printf("Confirmation: %s\n", confirmation) rec = &store.SecretRecord{ Label: label, Secret: []byte(hotp.URL(label)), Timestamp: time.Now().Unix(), Metadata: map[string][]byte{ "key": secret, "type": []byte("HOTP"), "confirmation": []byte(confirmation), }, } case TOTP: in, err := util.ReadLine("Time step (30s): ") if err != nil { return err } if in == "" { in = "30s" } d, err := time.ParseDuration(in) if err != nil { return err } in, err = util.ReadLine("Digits (6 or 8): ") if err != nil { return err } else if in == "" { in = "6" } digits, err := strconv.Atoi(in) if err != nil { return err } key, err := base32.StdEncoding.DecodeString(string(secret)) if err != nil { return err } var totp *twofactor.TOTP totp = twofactor.NewTOTPSHA1(key, 0, uint64(d.Seconds()), digits) confirmation := totp.OTP() fmt.Printf("Confirmation: %s\n", confirmation) rec = &store.SecretRecord{ Label: label, Secret: []byte(totp.URL(label)), Timestamp: time.Now().Unix(), Metadata: map[string][]byte{ "key": secret, "type": []byte("TOTP-SHA1"), "step": []byte(d.String()), "confirmation": []byte(confirmation), }, } case GoogleTOTP: var totp *twofactor.TOTP totp, err = twofactor.NewGoogleTOTP(string(secret)) if err != nil { return err } confirmation := totp.OTP() fmt.Printf("Confirmation: %s\n", confirmation) rec = &store.SecretRecord{ Label: label, Secret: []byte(totp.URL(label)), Timestamp: time.Now().Unix(), Metadata: map[string][]byte{ "key": secret, "type": []byte("TOTP-GOOGLE"), "step": []byte("30s"), "confirmation": []byte(confirmation), }, } default: return errors.New("unrecognised OTP type") } ps.Store[label] = rec return nil }