Beispiel #1
0
// 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
}
Beispiel #2
0
// 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)
}
Beispiel #3
0
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
}
Beispiel #4
0
// 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
}
Beispiel #5
0
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
		}
	}
}
Beispiel #6
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
}
Beispiel #7
0
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
}
Beispiel #8
0
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
}
Beispiel #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
}
Beispiel #10
0
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
}
Beispiel #11
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
}
Beispiel #12
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
}
Beispiel #13
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
}