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 (
7	"fmt"
8	"sync"
9)
10
11type PassphraseStreamCache struct {
12	sync.Mutex
13	tsec             *LockedTriplesec
14	passphraseStream *PassphraseStream
15}
16
17// LockedTriplesec is a wrapper around a Triplesec interface,
18// which allows multiple goroutines to handle the same underlying
19// Triplesec at the same time. The mechanism is simply a mutex
20// wrapping all accesses.
21type LockedTriplesec struct {
22	sync.Mutex
23	t Triplesec
24}
25
26func (t *LockedTriplesec) DeriveKey(l int) ([]byte, []byte, error) {
27	t.Lock()
28	defer t.Unlock()
29	return t.t.DeriveKey(l)
30}
31
32func (t *LockedTriplesec) Decrypt(b []byte) ([]byte, error) {
33	t.Lock()
34	defer t.Unlock()
35	return t.t.Decrypt(b)
36}
37
38func (t *LockedTriplesec) Encrypt(b []byte) ([]byte, error) {
39	t.Lock()
40	defer t.Unlock()
41	return t.t.Encrypt(b)
42}
43
44func (t *LockedTriplesec) Scrub() {
45	t.Lock()
46	defer t.Unlock()
47	t.t.Scrub()
48}
49
50func NewLockedTriplesec(t Triplesec) *LockedTriplesec {
51	if t == nil {
52		return nil
53	}
54	return &LockedTriplesec{t: t}
55}
56
57var _ Triplesec = (*LockedTriplesec)(nil)
58
59func NewPassphraseStreamCache(tsec Triplesec, ps *PassphraseStream) *PassphraseStreamCache {
60	return &PassphraseStreamCache{
61		tsec:             NewLockedTriplesec(tsec),
62		passphraseStream: ps,
63	}
64}
65
66func (s *PassphraseStreamCache) TriplesecAndGeneration() (Triplesec, PassphraseGeneration) {
67	var zed PassphraseGeneration
68	if s == nil {
69		return nil, zed
70	}
71	s.Lock()
72	defer s.Unlock()
73
74	// Beware the classic Go `nil` interface bug...
75	if s.tsec == nil {
76		return nil, zed
77	}
78	if s.passphraseStream == nil {
79		return nil, zed
80	}
81	ppgen := s.passphraseStream.Generation()
82	if ppgen.IsNil() {
83		return nil, zed
84	}
85
86	return s.tsec, ppgen
87}
88
89func (s *PassphraseStreamCache) PassphraseStreamAndTriplesec() (pps *PassphraseStream, tsec Triplesec) {
90
91	if s == nil {
92		return nil, nil
93	}
94
95	s.Lock()
96	defer s.Unlock()
97
98	// Beware the classic Go `nil` interface bug...
99	if s.tsec != nil {
100		tsec = s.tsec
101	}
102
103	if s.passphraseStream != nil {
104		pps = s.passphraseStream.Clone()
105	}
106
107	return pps, tsec
108}
109
110// PassphraseStream returns a copy of the currently cached passphrase stream,
111// or nil if none exists.
112func (s *PassphraseStreamCache) PassphraseStream() *PassphraseStream {
113	if s == nil {
114		return nil
115	}
116	s.Lock()
117	defer s.Unlock()
118	return s.passphraseStream.Clone()
119}
120
121func (s *PassphraseStreamCache) MutatePassphraseStream(f func(*PassphraseStream)) bool {
122	if s == nil {
123		return false
124	}
125	s.Lock()
126	defer s.Unlock()
127	if s.passphraseStream == nil {
128		return false
129	}
130	f(s.passphraseStream)
131	return true
132}
133
134func (s *PassphraseStreamCache) Valid() bool {
135	if s == nil {
136		return false
137	}
138	s.Lock()
139	defer s.Unlock()
140	return s.passphraseStream != nil && s.tsec != nil
141}
142
143func (s *PassphraseStreamCache) ValidPassphraseStream() bool {
144	if s == nil {
145		return false
146	}
147	s.Lock()
148	defer s.Unlock()
149	return s.passphraseStream != nil
150}
151
152func (s *PassphraseStreamCache) ValidTsec() bool {
153	if s == nil {
154		return false
155	}
156	s.Lock()
157	defer s.Unlock()
158	return s.tsec != nil
159}
160
161func (s *PassphraseStreamCache) Clear() {
162	if s == nil {
163		return
164	}
165	s.Lock()
166	defer s.Unlock()
167	if s.tsec != nil {
168		s.tsec.Scrub()
169		s.tsec = nil
170	}
171	s.passphraseStream = nil
172}
173
174func (s *PassphraseStreamCache) Dump() {
175	fmt.Printf("PassphraseStreamCache:\n")
176	if s == nil {
177		fmt.Printf("nil\n")
178		return
179	}
180	s.Lock()
181	defer s.Unlock()
182	fmt.Printf("PassphraseStreamCache:\n")
183	fmt.Printf("Valid: %v\n", s.Valid())
184	fmt.Printf("\n")
185}
186