1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package engine
5
6import (
7	"crypto/rand"
8	"fmt"
9	"sync"
10	"testing"
11
12	"github.com/keybase/client/go/kex2"
13	"github.com/keybase/client/go/libkb"
14	keybase1 "github.com/keybase/client/go/protocol/keybase1"
15	"github.com/stretchr/testify/require"
16)
17
18func TestKex2Provision(t *testing.T) {
19	subTestKex2Provision(t, false)
20}
21
22func TestKex2ProvisionPUK(t *testing.T) {
23	subTestKex2Provision(t, true)
24}
25
26func subTestKex2Provision(t *testing.T, upgradePerUserKey bool) {
27	// device X (provisioner) context:
28	tcX := SetupEngineTest(t, "kex2provision")
29	defer tcX.Cleanup()
30
31	tcX.Tp.DisableUpgradePerUserKey = !upgradePerUserKey
32
33	// provisioner needs to be logged in
34	userX := CreateAndSignupFakeUser(tcX, "login")
35
36	// device Y (provisionee) context:
37	tcY := SetupEngineTest(t, "kex2provision")
38	defer tcY.Cleanup()
39
40	tcY.Tp.DisableUpgradePerUserKey = !upgradePerUserKey
41
42	var secretX kex2.Secret
43	if _, err := rand.Read(secretX[:]); err != nil {
44		t.Fatal(err)
45	}
46
47	var secretY kex2.Secret
48	if _, err := rand.Read(secretY[:]); err != nil {
49		t.Fatal(err)
50	}
51
52	var wg sync.WaitGroup
53
54	// start provisionee
55	wg.Add(1)
56	go func() {
57		defer wg.Done()
58		err := (func() error {
59			uis := libkb.UIs{
60				ProvisionUI: &testProvisionUI{secretCh: make(chan kex2.Secret, 1)},
61			}
62			m := NewMetaContextForTest(tcY).WithUIs(uis).WithNewProvisionalLoginContext()
63			deviceID, err := libkb.NewDeviceID()
64			if err != nil {
65				return err
66			}
67			suffix, err := libkb.RandBytes(5)
68			if err != nil {
69				return err
70			}
71			dname := fmt.Sprintf("device_%x", suffix)
72			device := &libkb.Device{
73				ID:          deviceID,
74				Description: &dname,
75				Type:        keybase1.DeviceTypeV2_DESKTOP,
76			}
77			provisionee := NewKex2Provisionee(tcY.G, device, secretY, userX.UID(), fakeSalt())
78			return RunEngine2(m, provisionee)
79		})()
80		require.NoError(t, err, "no kex2 provisionee error")
81	}()
82
83	// start provisioner
84	wg.Add(1)
85	go func() {
86		defer wg.Done()
87		uis := libkb.UIs{
88			SecretUI:    userX.NewSecretUI(),
89			ProvisionUI: &testProvisionUI{},
90		}
91		provisioner := NewKex2Provisioner(tcX.G, secretX, nil)
92		m := NewMetaContextForTest(tcX).WithUIs(uis)
93		go provisioner.AddSecret(secretY)
94		if err := RunEngine2(m, provisioner); err != nil {
95			t.Errorf("provisioner error: %s", err)
96			return
97		}
98	}()
99
100	wg.Wait()
101}
102
103// Get a new device, test context and all, by provisioning it from an existing test context.
104// This was copied from subTestKex2Provision
105// Note that it uses Errorf in goroutines, so if it fails
106// the test will not fail until later.
107// Returns (tcY, CleanupFunction)
108func provisionNewDeviceKex(tcX *libkb.TestContext, userX *FakeUser) (*libkb.TestContext, func()) {
109	// tcX is the device X (provisioner) context:
110	// tcX should already have been logged in.
111
112	t := tcX.T
113
114	// device Y (provisionee) context:
115	tcY := SetupEngineTest(t, "kex2provision")
116	cleanup := func() { tcY.Cleanup() }
117
118	var secretX kex2.Secret
119	if _, err := rand.Read(secretX[:]); err != nil {
120		t.Fatal(err)
121	}
122
123	var secretY kex2.Secret
124	if _, err := rand.Read(secretY[:]); err != nil {
125		t.Fatal(err)
126	}
127
128	var wg sync.WaitGroup
129
130	// start provisionee
131	wg.Add(1)
132	go func() {
133		defer wg.Done()
134		err := (func() error {
135			uis := libkb.UIs{
136				ProvisionUI: &testProvisionUI{secretCh: make(chan kex2.Secret, 1)},
137			}
138			m := NewMetaContextForTest(tcY).WithUIs(uis).WithNewProvisionalLoginContext()
139			deviceID, err := libkb.NewDeviceID()
140			if err != nil {
141				return err
142			}
143			suffix, err := libkb.RandBytes(5)
144			if err != nil {
145				return err
146			}
147			dname := fmt.Sprintf("device_%x", suffix)
148			device := &libkb.Device{
149				ID:          deviceID,
150				Description: &dname,
151				Type:        keybase1.DeviceTypeV2_DESKTOP,
152			}
153			provisionee := NewKex2Provisionee(tcY.G, device, secretY, userX.UID(), fakeSalt())
154			return RunEngine2(m, provisionee)
155		})()
156		require.NoError(t, err, "kex2 provisionee")
157	}()
158
159	// start provisioner
160	wg.Add(1)
161	go func() {
162		defer wg.Done()
163		uis := libkb.UIs{
164			SecretUI:    userX.NewSecretUI(),
165			ProvisionUI: &testProvisionUI{},
166		}
167		provisioner := NewKex2Provisioner(tcX.G, secretX, nil)
168		go provisioner.AddSecret(secretY)
169		m := NewMetaContextForTest(*tcX).WithUIs(uis)
170		if err := RunEngine2(m, provisioner); err != nil {
171			t.Errorf("provisioner error: %s", err)
172			return
173		}
174	}()
175
176	wg.Wait()
177
178	return &tcY, cleanup
179}
180