1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package libkb
5
6import "fmt"
7
8type PassphraseType string
9
10const (
11	PassphraseTypeKeybase PassphraseType = "Keybase"
12	PassphraseTypePGP     PassphraseType = "PGP"
13)
14
15type UnlockerFunc func(pw string, storeSecret bool) (ret GenericKey, err error)
16
17type KeyUnlocker struct {
18	tries          int
19	reason         string
20	keyDesc        string
21	which          PassphraseType
22	useSecretStore bool
23	ui             SecretUI
24	unlocker       UnlockerFunc
25}
26
27func NewKeyUnlocker(tries int, reason string, keyDesc string, which PassphraseType, useSecretStore bool, ui SecretUI, unlocker UnlockerFunc) KeyUnlocker {
28	return KeyUnlocker{
29		tries:          tries,
30		reason:         reason,
31		keyDesc:        keyDesc,
32		which:          which,
33		useSecretStore: useSecretStore,
34		ui:             ui,
35		unlocker:       unlocker,
36	}
37}
38
39func (arg KeyUnlocker) Run(m MetaContext) (ret GenericKey, err error) {
40	var emsg string
41
42	if arg.ui == nil {
43		err = NoUIError{"secret"}
44		return nil, err
45	}
46
47	prompt := "Please enter your " + string(arg.which) + " passphrase to unlock the secret key for:\n" +
48		arg.keyDesc + "\n"
49	if len(arg.reason) > 0 {
50		prompt = prompt + "\nReason: " + arg.reason
51	}
52
53	title := "Your " + string(arg.which) + " passphrase"
54
55	for i := 0; arg.tries <= 0 || i < arg.tries; i++ {
56		res, err := GetSecret(m, arg.ui, title, prompt, emsg, arg.useSecretStore)
57		if err != nil {
58			// probably canceled
59			return nil, err
60		}
61		ret, err = arg.unlocker(res.Passphrase, res.StoreSecret)
62		if err == nil {
63			// success
64			return ret, nil
65		}
66		if _, ok := err.(PassphraseError); ok {
67			// keep trying
68			emsg = "Failed to unlock key; bad passphrase"
69		} else {
70			// unretryable error
71			return nil, err
72		}
73	}
74
75	return nil, fmt.Errorf("Too many failures; giving up")
76}
77