Пример #1
0
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
}
Пример #2
0
func showSecret(ps *store.SecretStore, cfg *config) error {
	label := cfg.Args[0]
	if !ps.Has(label) {
		return errors.New("no token found under label")
	}

	rec := ps.Store[label]
	var otpType int
	otp, label, err := twofactor.FromURL(string(rec.Secret))
	if err != nil {
		return err
	}
	if _, ok := rec.Metadata["type"]; !ok {
		otpType = parseTwofactorType(otp.Type())
	} else {
		otpType = parseOTPKind(string(rec.Metadata["type"]))
	}

	switch otpType {
	case TOTP:
		printTOTP(label, otp)
	case GoogleTOTP:
		printGTOTP(label, otp)
	case HOTP:
		printHOTP(label, otp)
		cfg.Updated = true
		hotp := otp.(*twofactor.HOTP)
		rec.Secret = []byte(hotp.URL(label))
		rec.Timestamp = time.Now().Unix()
		ps.Store[label] = rec
	default:
		return errors.New("unknown OTP type")
	}
	return nil
}
Пример #3
0
func addSecret(ps *store.SecretStore, cfg *config, m secret.ScryptMode) error {
	if cfg.WithMeta {
		return addMeta(ps, cfg, m)
	}
	label := cfg.Args[0]

	var rec *store.SecretRecord
	if ps.Has(label) {
		if !cfg.Overwrite {
			return errors.New("entry exists, not forcing overwrite")
		}
		util.Errorf("*** WARNING: overwriting password")
		rec = ps.Store[label]
	} else {
		rec = &store.SecretRecord{Label: label}
	}

	password, err := readpass.PasswordPromptBytes("New password: "******"no password entered")
	}
	rec.Secret = password
	rec.Timestamp = time.Now().Unix()
	ps.Store[label] = rec
	return nil
}
Пример #4
0
func showQR(ps *store.SecretStore, cfg *config) error {
	label := cfg.Args[0]
	if !ps.Has(label) {
		return errors.New("no token found under label")
	}
	filename := cfg.Args[1]

	rec := ps.Store[label]

	otp, label, err := twofactor.FromURL(string(rec.Secret))
	if err != nil {
		return err
	}

	var qr []byte

	switch otp.Type() {
	case twofactor.OATH_HOTP:
		hotp := otp.(*twofactor.HOTP)
		qr, err = hotp.QR(label)
	case twofactor.OATH_TOTP:
		totp := otp.(*twofactor.TOTP)
		qr, err = totp.QR(label)
	default:
		err = errors.New("QR codes can only be generated for OATH OTPs")
	}

	if err != nil {
		return err
	}

	return ioutil.WriteFile(filename, qr, 0600)
}
Пример #5
0
func showEntry(ps *store.SecretStore, cfg *config) error {
	title := cfg.Args[0]

	if !ps.Has(title) {
		return errors.New("entry not found")
	}

	fmt.Printf("%s\n", ps.Store[title].Secret)
	return nil
}
Пример #6
0
func editEntry(ps *store.SecretStore, cfg *config) error {
	title := cfg.Args[0]
	if !ps.Has(title) {
		return errors.New("entry not found")
	}

	tmp, err := ioutil.TempFile("", "cu_journal")
	if err != nil {
		return err
	}
	fileName := tmp.Name()
	tmp.Close()
	defer os.Remove(fileName)
	err = ioutil.WriteFile(fileName, ps.Store[title].Secret, 0600)

	defer func() {
		err := os.Remove(fileName)
		if err != nil {
			fmt.Println("*** WARNING ***")
			fmt.Println("FAILED TO REMOVE TEMPORARY FILE", fileName)
			fmt.Println("You should remove this yourself.")
			fmt.Printf("\nThe reason: %v\n", err)
		}
	}()

	editor := cfg.Editor
	if editor == "" {
		editor = defaultEditor
	}

	args := strings.Split(editor, " ")
	args = append(args, fileName)
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err = cmd.Run()
	if err != nil {
		return err
	}

	fileData, err := ioutil.ReadFile(fileName)
	if err != nil {
		return err
	}
	util.Zero(ps.Store[title].Secret)
	ps.Store[title].Secret = fileData

	return nil
}
Пример #7
0
func remove(ps *store.SecretStore, cfg *config, m secret.ScryptMode) error {
	if cfg.WithMeta {
		return removeMeta(ps, cfg)
	}

	label := cfg.Args[0]
	if !ps.Has(label) {
		return errors.New("entry not found")
	}

	fmt.Println("Removed ", label)
	delete(ps.Store, label)
	return nil
}
Пример #8
0
func showSecret(ps *store.SecretStore, cfg *config, m secret.ScryptMode) error {
	label := cfg.Args[0]
	if !ps.Has(label) {
		return errors.New("entry not found")
	}

	r := ps.Store[label]
	if !cfg.Clip {
		fmt.Printf("Secret: %s\n", r.Secret)
	} else {
		fmt.Printf("%s", r.Secret)
		return nil
	}
	if cfg.WithMeta {
		fmt.Printf("Timestamp: %d (%s)\n", r.Timestamp,
			time.Unix(r.Timestamp, 0).Format(timeFormat))
		for k, v := range r.Metadata {
			fmt.Printf("\t%s: %s\n", k, v)
		}
	}
	return nil
}
Пример #9
0
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
}
Пример #10
0
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
}
Пример #11
0
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
}
Пример #12
0
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
}