// removeMeta deletes metadata from the named record. func removeMeta(fileName, name string) { passwords := openFile(fileName) defer passwords.Zero() rec, ok := passwords.Store[name] if !ok || rec.Metadata == nil { errorf("Nothing stored under the label %s", name) return } 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]) } for { key, err := readpass.DefaultPasswordPrompt("Remove key: ") if err != nil { 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() saveFile(fileName, passwords) }
// storeMany allows entering multiple name/password pairs to // facilitate adding multiple labels (e.g. for a new password // store). The same notes regarding storeRecord apply here. func storeMany(fileName string, overWrite bool) { var passwords = Passwords{} defer passwords.Zero() if _, err := os.Stat(fileName); err != nil && !os.IsNotExist(err) { errorf("Failed to open password store: %v", err) os.Exit(1) } else if err == nil { passwords = openFile(fileName) } fmt.Println("Use an empty name to indicate that you are done.") for { name, err := readpass.DefaultPasswordPrompt("Name: ") if err != nil { errorf("%v", err) break } else if name == "" { break } rec, ok := passwords.Store[name] if ok && len(rec.Password) != 0 { if !overWrite { errorf("entry exists, not forcing overwrite") os.Exit(1) } else { errorf("*** warning: overwriting password") } } else if !ok { rec = &Record{ Name: name, } } password, err := readpass.PasswordPromptBytes("Password: "******"%v", err) os.Exit(1) } else if len(password) == 0 { errorf("no password entered") continue } rec.Password = password rec.Timestamp = time.Now().Unix() passwords.Store[name] = rec } saveFile(fileName, passwords) }
// storeMeta adds metadata to the named record. func storeMeta(fileName, name string) { passwords := openFile(fileName) defer passwords.Zero() rec, ok := passwords.Store[name] if !ok { rec = &Record{Name: name} password, err := readpass.PasswordPromptBytes("Password: "******"%v", err) os.Exit(1) } else if len(password) == 0 { errorf("no password entered") os.Exit(1) } rec.Password = password passwords.Store[name] = rec defer zero(password) } 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 := readpass.DefaultPasswordPrompt("key = value: ") if err != nil { errorf("%v", err) break } else if line == "" { break } meta := strings.SplitN(line, "=", 2) if len(meta) < 2 { 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() saveFile(fileName, passwords) }
func addAccount(label string, otpType int) { if accounts[label] != "" { errorf("warning: label %s exists with url %s", label, accounts[label]) } // Default prompt is echoing, which we want here. secret, err := readpass.DefaultPasswordPrompt("Secret: ") if err != nil { errorf("Failed to read password.") os.Exit(1) } secret = sanitiseSecret(secret) switch otpType { case HOTP: in, err := readpass.DefaultPasswordPrompt("Initial counter (0): ") if err != nil { errorf("%v", err) os.Exit(1) } if in == "" { in = "0" } d, err := strconv.Atoi(in) if err != nil { errorf("%v", err) os.Exit(1) } in, err = readpass.DefaultPasswordPrompt("Digits (6 or 8): ") if err != nil { errorf("%v", err) os.Exit(1) } else if in == "" { in = "6" } digits, err := strconv.Atoi(in) if err != nil { errorf("%v", err) os.Exit(1) } key, err := base32.StdEncoding.DecodeString(secret) if err != nil { errorf("%v", err) os.Exit(1) } var hotp *twofactor.HOTP hotp = twofactor.NewHOTP(key, uint64(d), digits) fmt.Printf("Confirmation: %s\n", hotp.OTP()) secret = hotp.URL(label) case TOTP: in, err := readpass.DefaultPasswordPrompt("Time step (30s): ") if err != nil { errorf("%v", err) os.Exit(1) } if in == "" { in = "30s" } d, err := time.ParseDuration(in) if err != nil { errorf("%v", err) os.Exit(1) } in, err = readpass.DefaultPasswordPrompt("Digits (6 or 8): ") if err != nil { errorf("%v", err) os.Exit(1) } else if in == "" { in = "6" } digits, err := strconv.Atoi(in) if err != nil { errorf("%v", err) os.Exit(1) } key, err := base32.StdEncoding.DecodeString(secret) if err != nil { errorf("%v", err) os.Exit(1) } var totp *twofactor.TOTP totp = twofactor.NewTOTPSHA1(key, 0, uint64(d.Seconds()), digits) fmt.Printf("Confirmation: %s\n", totp.OTP()) secret = totp.URL(label) case GoogleTOTP: var totp *twofactor.TOTP totp, err = twofactor.NewGoogleTOTP(secret) if err != nil { errorf("%v", err) os.Exit(1) } fmt.Printf("Confirmation: %s\n", totp.OTP()) secret = totp.URL(label) default: errorf("unrecognised OTP type") os.Exit(1) } accounts[label] = secret }