-
Notifications
You must be signed in to change notification settings - Fork 1
/
provision_ui.go
196 lines (169 loc) · 6.68 KB
/
provision_ui.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.
package client
import (
"fmt"
"os"
"path"
"golang.org/x/net/context"
"github.com/keybase/client/go/libkb"
keybase1 "github.com/keybase/client/go/protocol"
"github.com/keybase/client/go/qrcode"
rpc "github.com/keybase/go-framed-msgpack-rpc"
)
type ProvisionUI struct {
parent *UI
role libkb.KexRole
}
func NewProvisionUIProtocol(g *libkb.GlobalContext, role libkb.KexRole) rpc.Protocol {
return keybase1.ProvisionUiProtocol(g.UI.GetProvisionUI(role))
}
func (p ProvisionUI) ChooseProvisioningMethod(ctx context.Context, arg keybase1.ChooseProvisioningMethodArg) (keybase1.ProvisionMethod, error) {
p.parent.Output("How would you like to sign this install of Keybase?\n\n")
p.parent.Output("(1) Use an existing device\n")
p.parent.Output("(2) Use a paper key\n")
p.parent.Output("(3) Use my Keybase passphrase\n")
max := 3
if arg.GpgOption {
p.parent.Printf("(4) Use GPG\n")
max = 4
}
var res keybase1.ProvisionMethod
ret, err := PromptSelectionOrCancel(PromptDescriptorChooseProvisioningMethod, p.parent, "Choose a signing option", 1, max)
if err != nil {
if err == ErrInputCanceled {
return res, libkb.CanceledError{M: "user canceled input"}
}
return res, err
}
switch ret {
case 1:
return keybase1.ProvisionMethod_DEVICE, nil
case 2:
return keybase1.ProvisionMethod_PAPER_KEY, nil
case 3:
return keybase1.ProvisionMethod_PASSPHRASE, nil
case 4:
p.parent.Output(`In order to authorize this installation, keybase needs to sign this installation
with your GPG secret key.
You have two options.
(1) Keybase can use GPG commands to sign the installation.
(2) Keybase can export your secret key from GPG and save it to keybase's local encrypted
keyring. This way, it can be used in 'keybase pgp sign' and 'keybase pgp decrypt'
going forward.
`)
gret, err := PromptSelectionOrCancel(PromptDescriptorChooseGPGMethod, p.parent, "Which do you prefer?", 1, 2)
if err != nil {
if err == ErrInputCanceled {
return res, libkb.CanceledError{M: "user canceled input"}
}
return res, err
}
if gret == 1 {
return keybase1.ProvisionMethod_GPG_SIGN, nil
} else if gret == 2 {
return keybase1.ProvisionMethod_GPG_IMPORT, nil
}
}
return res, fmt.Errorf("invalid provision option: %d", ret)
}
func (p ProvisionUI) ChooseDeviceType(ctx context.Context, sessionID int) (keybase1.DeviceType, error) {
p.parent.Output("What type of device would you like to connect this computer with?\n\n")
p.parent.Output("(1) Desktop or laptop\n")
p.parent.Output("(2) Mobile phone\n\n")
var res keybase1.DeviceType
ret, err := PromptSelectionOrCancel(PromptDescriptorChooseDeviceType, p.parent, "Choose a device type", 1, 2)
if err != nil {
if err == ErrInputCanceled {
return res, libkb.CanceledError{M: "user canceled input"}
}
return res, err
}
switch ret {
case 1:
return keybase1.DeviceType_DESKTOP, nil
case 2:
return keybase1.DeviceType_MOBILE, nil
}
return res, fmt.Errorf("invalid device type option: %d", ret)
}
func (p ProvisionUI) DisplayAndPromptSecret(ctx context.Context, arg keybase1.DisplayAndPromptSecretArg) (keybase1.SecretResponse, error) {
var resp keybase1.SecretResponse
if p.role == libkb.KexRoleProvisioner {
// This is the provisioner device (device X)
// For command line app, all secrets are entered on the provisioner only:
p.parent.Output("\nEnter the verification code from your other device here. To get\n")
p.parent.Output("a verification code, run 'keybase login' on your other device.\n\n")
ret, err := PromptWithChecker(PromptDescriptorProvisionPhrase, p.parent, "Verification code", false, libkb.CheckNotEmpty)
if err != nil {
return resp, err
}
resp.Phrase = ret
return resp, nil
}
if p.role == libkb.KexRoleProvisionee {
// this is the provisionee device (device Y)
// For command line app, the provisionee displays secrets only
p.parent.Output("Type this verification code into your other device:\n\n")
p.parent.Output("\t" + arg.Phrase + "\n\n")
p.parent.Output("If you are using the command line client on your other device, run this command:\n\n")
p.parent.Output("\tkeybase device add\n\n")
p.parent.Output("It will then prompt you for the verification code above.\n\n")
if arg.OtherDeviceType == keybase1.DeviceType_MOBILE {
encodings, err := qrcode.Encode([]byte(arg.Phrase))
// ignoring any of these errors...phrase above will suffice.
if err == nil {
p.parent.Output("Or, scan this QR Code with the keybase app on your mobile phone:\n\n")
p.parent.Output(encodings.Terminal)
fname := path.Join(os.TempDir(), "keybase_qr.png")
f, ferr := os.Create(fname)
if ferr == nil {
f.Write(encodings.PNG)
f.Close()
p.parent.Printf("\nThere's also a PNG version in %s that might work better.\n\n", fname)
}
}
}
return resp, nil
}
return resp, libkb.InvalidArgumentError{Msg: fmt.Sprintf("invalid ProvisionUI role: %d", p.role)}
}
func (p ProvisionUI) PromptNewDeviceName(ctx context.Context, arg keybase1.PromptNewDeviceNameArg) (string, error) {
for i := 0; i < 10; i++ {
name, err := PromptWithChecker(PromptDescriptorProvisionDeviceName, p.parent, "Enter a public name for this device", false, libkb.CheckDeviceName)
if err != nil {
return "", err
}
var match bool
for _, existing := range arg.ExistingDevices {
if libkb.NameCmp(name, existing) {
match = true
p.parent.Printf("Device name %q already in use. Please try again.\n", name)
break
}
}
if !match {
return name, nil
}
}
return "", libkb.RetryExhaustedError{}
}
func (p ProvisionUI) DisplaySecretExchanged(ctx context.Context, sessionID int) error {
p.parent.Output("Secret successfully exchanged. On your new device, choose and save a public name for it.\n\n")
return nil
}
func (p ProvisionUI) ProvisioneeSuccess(ctx context.Context, arg keybase1.ProvisioneeSuccessArg) error {
p.parent.Printf(CHECK + " Success! You provisioned your device " + ColorString("bold", arg.DeviceName) + ".\n\n")
p.parent.Printf("You are logged in as " + ColorString("bold", arg.Username) + "\n")
// turn on when kbfs active:
if false {
p.parent.Printf(" - your keybase public directory is available at /keybase/public/%s\n", arg.Username)
p.parent.Printf(" - your keybase encrypted directory is available at /keybase/private/%s\n", arg.Username)
}
p.parent.Printf(" - type `keybase help` for more info.\n")
return nil
}
func (p ProvisionUI) ProvisionerSuccess(ctx context.Context, arg keybase1.ProvisionerSuccessArg) error {
p.parent.Printf(CHECK + " Success! You added a new device named " + ColorString("bold", arg.DeviceName) + " to your account.\n\n")
return nil
}